<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

extern crate devicemapper;
extern crate libstratis;
extern crate rand;
extern crate tempdir;
extern crate time;
extern crate uuid;

use std::fs::OpenOptions;
use std::io::Write;
use std::path::{Path, PathBuf};

use self::devicemapper::DM;
use self::devicemapper::consts::SECTOR_SIZE;
use self::devicemapper::LinearDev;
use self::devicemapper::Segment;
use self::devicemapper::{DataBlocks, Sectors};
use self::devicemapper::ThinDev;
use self::devicemapper::ThinPoolDev;

use self::tempdir::TempDir;
use self::uuid::Uuid;

use libstratis::engine::{Engine, EngineError, ErrorEnum};
use libstratis::engine::strat_engine::blockdevmgr::{initialize, resolve_devices};
use libstratis::engine::strat_engine::device::{blkdev_size, wipe_sectors, write_sectors};
use libstratis::engine::strat_engine::engine::DevOwnership;
use libstratis::engine::strat_engine::filesystem::{create_fs, mount_fs, unmount_fs};
use libstratis::engine::strat_engine::metadata::{StaticHeader, BDA_STATIC_HDR_SECTORS,
                                                 MIN_MDA_SECTORS};
use libstratis::engine::strat_engine::serde_structs::Recordable;
use libstratis::engine::strat_engine::setup::{find_all, get_metadata};
use libstratis::engine::strat_engine::StratEngine;

/// Dirty sectors where specified, with 1s.
fn dirty_sectors(path: &Path, offset: Sectors, length: Sectors) {
    write_sectors(path, offset, length, &[1u8; SECTOR_SIZE]).unwrap();
}

/// Verify that it is impossible to initialize a set of disks of which
/// even one is dirty, i.e, has some data written within BDA_STATIC_HDR_SECTORS
/// of start of disk. Choose the dirty disk randomly. This means that even
/// if our code is broken with respect to this property, this test might
/// sometimes succeed.
/// FIXME: Consider enriching device specs so that this test will fail
/// consistently.
/// Verify that force flag allows all dirty disks to be initialized.
pub fn test_force_flag_dirty(paths: &[&Path]) -> () {

    let index = rand::random::<u8>() as usize % paths.len();
    dirty_sectors(paths[index],
                  Sectors(index as u64 % *BDA_STATIC_HDR_SECTORS),
                  Sectors(1));

    let unique_devices = resolve_devices(&paths).unwrap();

    let uuid = Uuid::new_v4();
    assert!(initialize(&uuid, unique_devices.clone(), MIN_MDA_SECTORS, false).is_err());
    assert!(paths
                .iter()
                .enumerate()
                .all(|(i, path)| {
        StaticHeader::determine_ownership(&mut OpenOptions::new().read(true).open(path).unwrap())
            .unwrap() ==
        if i == index {
            DevOwnership::Theirs
        } else {
            DevOwnership::Unowned
        }
    }));

    assert!(initialize(&uuid, unique_devices.clone(), MIN_MDA_SECTORS, true).is_ok());
    assert!(paths
                .iter()
                .all(|path| {
                         StaticHeader::determine_ownership(&mut OpenOptions::new()
                                                                    .read(true)
                                                                    .open(path)
                                                                    .unwrap())
                                 .unwrap() == DevOwnership::Ours(uuid)
                     }));
}

/// Verify that it is impossible to steal blockdevs from another Stratis
/// pool.
/// 1. Initialize devices with pool uuid.
/// 2. Initializing again with different uuid must fail.
/// 3. Initializing again with same pool uuid must succeed, because all the
/// devices already belong so there's nothing to do.
/// 4. Initializing again with different uuid and force = true also fails.
pub fn test_force_flag_stratis(paths: &[&Path]) -> () {
    let unique_devices = resolve_devices(&paths).unwrap();

    let uuid = Uuid::new_v4();
    let uuid2 = Uuid::new_v4();

    initialize(&uuid, unique_devices.clone(), MIN_MDA_SECTORS, false).unwrap();
    assert!(initialize(&uuid2, unique_devices.clone(), MIN_MDA_SECTORS, false).is_err());

    assert!(initialize(&uuid, unique_devices.clone(), MIN_MDA_SECTORS, false).is_ok());

    // FIXME: this should succeed, but currently it fails, to be extra safe.
    // See: https://github.com/stratis-storage/stratisd/pull/292
    assert!(initialize(&uuid2, unique_devices.clone(), MIN_MDA_SECTORS, true).is_err());
}

/// Verify that the sum of the lengths of the available range of the
/// blockdevs in the linear device equals the size of the linear device.
pub fn test_linear_device(paths: &[&Path]) -> () {
    let unique_devices = resolve_devices(&paths).unwrap();
    let initialized = initialize(&Uuid::new_v4(),
                                 unique_devices.clone(),
                                 MIN_MDA_SECTORS,
                                 false)
            .unwrap();
    let total_blockdev_size = initialized
        .iter()
        .fold(Sectors(0), |s, i| s + i.avail_range().length);

    let segments = initialized
        .iter()
        .map(|block_dev| block_dev.avail_range())
        .collect::<Vec<Segment>>();

    let device_name = "stratis_testing_linear";
    let dm = DM::new().unwrap();
    let lineardev = LinearDev::new(&device_name, &dm, segments).unwrap();

    let mut linear_dev_path = PathBuf::from("/dev/mapper");
    linear_dev_path.push(device_name);

    let lineardev_size = blkdev_size(&OpenOptions::new()
                                          .read(true)
                                          .open(linear_dev_path)
                                          .unwrap())
            .unwrap();
    assert!(total_blockdev_size.bytes() == lineardev_size);
    lineardev.teardown(&dm).unwrap();
}

/// Verify no errors when writing files to a mounted filesystem on a thin
/// device.
pub fn test_thinpool_device(paths: &[&Path]) -> () {
    let initialized = initialize(&Uuid::new_v4(),
                                 resolve_devices(&paths).unwrap(),
                                 MIN_MDA_SECTORS,
                                 false)
            .unwrap();

    let (metadata_blockdev, data_blockdev) = (initialized.first().unwrap(),
                                              initialized.last().unwrap());

    let dm = DM::new().unwrap();
    let metadata_dev = LinearDev::new("stratis_testing_thinpool_metadata",
                                      &dm,
                                      vec![metadata_blockdev.avail_range()])
            .unwrap();

    // Clear the meta data device.  If the first block is not all zeros - the
    // stale data will cause the device to appear as an existing meta rather
    // than a new one.  Clear the entire device to be safe.  Stratis implements
    // the same approach when constructing a thin pool.
    wipe_sectors(&metadata_dev.devnode().unwrap(),
                 Sectors(0),
                 metadata_dev.size().unwrap())
            .unwrap();

    let data_dev = LinearDev::new("stratis_testing_thinpool_datadev",
                                  &dm,
                                  vec![data_blockdev.avail_range()])
            .unwrap();
    let thinpool_dev = ThinPoolDev::new("stratis_testing_thinpool",
                                        &dm,
                                        data_dev.size().unwrap(),
                                        Sectors(1024),
                                        DataBlocks(256000),
                                        metadata_dev,
                                        data_dev)
            .unwrap();
    let thin_dev = ThinDev::new("stratis_testing_thindev",
                                &dm,
                                &thinpool_dev,
                                7,
                                Sectors(300000))
            .unwrap();

    create_fs(&thin_dev.devnode().unwrap()).unwrap();

    let tmp_dir = TempDir::new("stratis_testing").unwrap();
    mount_fs(&thin_dev.devnode().unwrap(), tmp_dir.path()).unwrap();
    for i in 0..100 {
        let file_path = tmp_dir.path().join(format!("stratis_test{}.txt", i));
        writeln!(&OpenOptions::new()
                      .create(true)
                      .write(true)
                      .open(file_path)
                      .unwrap(),
                 "data")
                .unwrap();
    }
    // The -d (detach-loop) is passed for both loopback and real devs,
    // it helps with loopback devs and does no harm for real devs.
    unmount_fs(tmp_dir.path(), &["-d"]).unwrap();
    thin_dev.teardown(&dm).unwrap();
    thinpool_dev.teardown(&dm).unwrap();
}

/// Test that creating a pool claims all blockdevs and that destroying a pool
/// releases all blockdevs.
pub fn test_pool_blockdevs(paths: &[&Path]) -> () {
    let mut engine = StratEngine::initialize().unwrap();
    let (uuid, blockdevs) = engine
        .create_pool("test_pool", paths, None, true)
        .unwrap();
    assert!(blockdevs
                .iter()
                .all(|path| {
                         StaticHeader::determine_ownership(&mut OpenOptions::new()
                                                                    .read(true)
                                                                    .open(path)
                                                                    .unwrap())
                                 .unwrap() == DevOwnership::Ours(uuid)
                     }));
    engine.destroy_pool(&uuid).unwrap();
    assert!(blockdevs
                .iter()
                .all(|path| {
                         StaticHeader::determine_ownership(&mut OpenOptions::new()
                                                                    .read(true)
                                                                    .open(path)
                                                                    .unwrap())
                                 .unwrap() == DevOwnership::Unowned
                     }));
}

/// Verify that tearing down an engine doesn't fail if no filesystems on it.
pub fn test_teardown(paths: &[&Path]) -> () {
    let mut engine = StratEngine::initialize().unwrap();
    engine
        .create_pool("test_pool", paths, None, true)
        .unwrap();
    assert!(engine.teardown().is_ok())
}

/// Verify that find_all function locates and assigns pools appropriately.
/// 1. Split available paths into 2 discrete sets.
/// 2. Initialize the block devices in the first set with a pool uuid.
/// 3. Run find_all() and verify that it has found the initialized devices
/// and no others.
/// 4. Initialize the block devices in the second set with a different pool
/// uuid.
/// 5. Run find_all() again and verify that both sets of devices are found.
/// 6. Verify that get_metadata() return an error. initialize() only
/// initializes block devices, it does not write metadata.
pub fn test_setup(paths: &[&Path]) -> () {
    assert!(paths.len() > 2);

    let (paths1, paths2) = paths.split_at(2);

    let unique_devices = resolve_devices(paths1).unwrap();
    let uuid1 = Uuid::new_v4();
    initialize(&uuid1, unique_devices, MIN_MDA_SECTORS, false).unwrap();

    let pools = find_all().unwrap();
    assert!(pools.len() == 1);
    assert!(pools.contains_key(&uuid1));
    let devices = pools.get(&uuid1).expect("pools.contains_key() was true");
    assert!(devices.len() == paths1.len());

    let unique_devices = resolve_devices(paths2).unwrap();
    let uuid2 = Uuid::new_v4();
    initialize(&uuid2, unique_devices, MIN_MDA_SECTORS, false).unwrap();

    let pools = find_all().unwrap();
    assert!(pools.len() == 2);

    assert!(pools.contains_key(&uuid1));
    let devices1 = pools.get(&uuid1).expect("pools.contains_key() was true");
    assert!(devices1.len() == paths1.len());

    assert!(pools.contains_key(&uuid2));
    let devices2 = pools.get(&uuid2).expect("pools.contains_key() was true");
    assert!(devices2.len() == paths2.len());

    assert!(pools
                .iter()
                .map(|(uuid, devs)| get_metadata(uuid, devs))
                .all(|x| x.unwrap().is_none()));
}

/// Verify that a pool with no devices does not have the minimum amount of
/// space required.
pub fn test_empty_pool(paths: &[&Path]) -> () {
    assert!(paths.len() == 0);
    let mut engine = StratEngine::initialize().unwrap();
    assert!(match engine
                      .create_pool("test_pool", paths, None, true)
                      .unwrap_err() {
                EngineError::Engine(ErrorEnum::Invalid, _) => true,
                _ => false,
            });
}

/// Verify that metadata can be read from pools.
/// 1. Split paths into two separate sets.
/// 2. Create pools from the two sets.
/// 3. Use find_all() to get the devices in the pool.
/// 4. Use get_metadata to find metadata for each pool and verify correctness.
/// 5. Teardown the engine and repeat.
pub fn test_basic_metadata(paths: &[&Path]) {
    assert!(paths.len() > 2);

    let (paths1, paths2) = paths.split_at(2);

    let mut engine = StratEngine::initialize().unwrap();

    let name1 = "name1";
    let (uuid1, _) = engine.create_pool(&name1, paths1, None, false).unwrap();
    let metadata1 = engine
        .get_strat_pool(&uuid1)
        .unwrap()
        .record()
        .unwrap();

    let name2 = "name2";
    let (uuid2, _) = engine.create_pool(&name2, paths2, None, false).unwrap();
    let metadata2 = engine
        .get_strat_pool(&uuid2)
        .unwrap()
        .record()
        .unwrap();

    let pools = find_all().unwrap();
    assert!(pools.len() == 2);
    assert!(get_metadata(&uuid1, pools.get(&uuid1).unwrap())
                .unwrap()
                .unwrap() == metadata1);
    assert!(get_metadata(&uuid2, pools.get(&uuid2).unwrap())
                .unwrap()
                .unwrap() == metadata2);

    engine.teardown().unwrap();
    let pools = find_all().unwrap();
    assert!(pools.len() == 2);
    assert!(get_metadata(&uuid1, pools.get(&uuid1).unwrap())
                .unwrap()
                .unwrap() == metadata1);
    assert!(get_metadata(&uuid2, pools.get(&uuid2).unwrap())
                .unwrap()
                .unwrap() == metadata2);
}
<commit_msg>Extend test with a very basic assertion about get_blockdevmgr<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

extern crate devicemapper;
extern crate libstratis;
extern crate rand;
extern crate tempdir;
extern crate time;
extern crate uuid;

use std::fs::OpenOptions;
use std::io::Write;
use std::path::{Path, PathBuf};

use self::devicemapper::DM;
use self::devicemapper::consts::SECTOR_SIZE;
use self::devicemapper::LinearDev;
use self::devicemapper::Segment;
use self::devicemapper::{DataBlocks, Sectors};
use self::devicemapper::ThinDev;
use self::devicemapper::ThinPoolDev;

use self::tempdir::TempDir;
use self::uuid::Uuid;

use libstratis::engine::{Engine, EngineError, ErrorEnum};
use libstratis::engine::strat_engine::blockdevmgr::{initialize, resolve_devices};
use libstratis::engine::strat_engine::device::{blkdev_size, wipe_sectors, write_sectors};
use libstratis::engine::strat_engine::engine::DevOwnership;
use libstratis::engine::strat_engine::filesystem::{create_fs, mount_fs, unmount_fs};
use libstratis::engine::strat_engine::metadata::{StaticHeader, BDA_STATIC_HDR_SECTORS,
                                                 MIN_MDA_SECTORS};
use libstratis::engine::strat_engine::serde_structs::Recordable;
use libstratis::engine::strat_engine::setup::{find_all, get_blockdevmgr, get_metadata};
use libstratis::engine::strat_engine::StratEngine;

/// Dirty sectors where specified, with 1s.
fn dirty_sectors(path: &Path, offset: Sectors, length: Sectors) {
    write_sectors(path, offset, length, &[1u8; SECTOR_SIZE]).unwrap();
}

/// Verify that it is impossible to initialize a set of disks of which
/// even one is dirty, i.e, has some data written within BDA_STATIC_HDR_SECTORS
/// of start of disk. Choose the dirty disk randomly. This means that even
/// if our code is broken with respect to this property, this test might
/// sometimes succeed.
/// FIXME: Consider enriching device specs so that this test will fail
/// consistently.
/// Verify that force flag allows all dirty disks to be initialized.
pub fn test_force_flag_dirty(paths: &[&Path]) -> () {

    let index = rand::random::<u8>() as usize % paths.len();
    dirty_sectors(paths[index],
                  Sectors(index as u64 % *BDA_STATIC_HDR_SECTORS),
                  Sectors(1));

    let unique_devices = resolve_devices(&paths).unwrap();

    let uuid = Uuid::new_v4();
    assert!(initialize(&uuid, unique_devices.clone(), MIN_MDA_SECTORS, false).is_err());
    assert!(paths
                .iter()
                .enumerate()
                .all(|(i, path)| {
        StaticHeader::determine_ownership(&mut OpenOptions::new().read(true).open(path).unwrap())
            .unwrap() ==
        if i == index {
            DevOwnership::Theirs
        } else {
            DevOwnership::Unowned
        }
    }));

    assert!(initialize(&uuid, unique_devices.clone(), MIN_MDA_SECTORS, true).is_ok());
    assert!(paths
                .iter()
                .all(|path| {
                         StaticHeader::determine_ownership(&mut OpenOptions::new()
                                                                    .read(true)
                                                                    .open(path)
                                                                    .unwrap())
                                 .unwrap() == DevOwnership::Ours(uuid)
                     }));
}

/// Verify that it is impossible to steal blockdevs from another Stratis
/// pool.
/// 1. Initialize devices with pool uuid.
/// 2. Initializing again with different uuid must fail.
/// 3. Initializing again with same pool uuid must succeed, because all the
/// devices already belong so there's nothing to do.
/// 4. Initializing again with different uuid and force = true also fails.
pub fn test_force_flag_stratis(paths: &[&Path]) -> () {
    let unique_devices = resolve_devices(&paths).unwrap();

    let uuid = Uuid::new_v4();
    let uuid2 = Uuid::new_v4();

    initialize(&uuid, unique_devices.clone(), MIN_MDA_SECTORS, false).unwrap();
    assert!(initialize(&uuid2, unique_devices.clone(), MIN_MDA_SECTORS, false).is_err());

    assert!(initialize(&uuid, unique_devices.clone(), MIN_MDA_SECTORS, false).is_ok());

    // FIXME: this should succeed, but currently it fails, to be extra safe.
    // See: https://github.com/stratis-storage/stratisd/pull/292
    assert!(initialize(&uuid2, unique_devices.clone(), MIN_MDA_SECTORS, true).is_err());
}

/// Verify that the sum of the lengths of the available range of the
/// blockdevs in the linear device equals the size of the linear device.
pub fn test_linear_device(paths: &[&Path]) -> () {
    let unique_devices = resolve_devices(&paths).unwrap();
    let initialized = initialize(&Uuid::new_v4(),
                                 unique_devices.clone(),
                                 MIN_MDA_SECTORS,
                                 false)
            .unwrap();
    let total_blockdev_size = initialized
        .iter()
        .fold(Sectors(0), |s, i| s + i.avail_range().length);

    let segments = initialized
        .iter()
        .map(|block_dev| block_dev.avail_range())
        .collect::<Vec<Segment>>();

    let device_name = "stratis_testing_linear";
    let dm = DM::new().unwrap();
    let lineardev = LinearDev::new(&device_name, &dm, segments).unwrap();

    let mut linear_dev_path = PathBuf::from("/dev/mapper");
    linear_dev_path.push(device_name);

    let lineardev_size = blkdev_size(&OpenOptions::new()
                                          .read(true)
                                          .open(linear_dev_path)
                                          .unwrap())
            .unwrap();
    assert!(total_blockdev_size.bytes() == lineardev_size);
    lineardev.teardown(&dm).unwrap();
}

/// Verify no errors when writing files to a mounted filesystem on a thin
/// device.
pub fn test_thinpool_device(paths: &[&Path]) -> () {
    let initialized = initialize(&Uuid::new_v4(),
                                 resolve_devices(&paths).unwrap(),
                                 MIN_MDA_SECTORS,
                                 false)
            .unwrap();

    let (metadata_blockdev, data_blockdev) = (initialized.first().unwrap(),
                                              initialized.last().unwrap());

    let dm = DM::new().unwrap();
    let metadata_dev = LinearDev::new("stratis_testing_thinpool_metadata",
                                      &dm,
                                      vec![metadata_blockdev.avail_range()])
            .unwrap();

    // Clear the meta data device.  If the first block is not all zeros - the
    // stale data will cause the device to appear as an existing meta rather
    // than a new one.  Clear the entire device to be safe.  Stratis implements
    // the same approach when constructing a thin pool.
    wipe_sectors(&metadata_dev.devnode().unwrap(),
                 Sectors(0),
                 metadata_dev.size().unwrap())
            .unwrap();

    let data_dev = LinearDev::new("stratis_testing_thinpool_datadev",
                                  &dm,
                                  vec![data_blockdev.avail_range()])
            .unwrap();
    let thinpool_dev = ThinPoolDev::new("stratis_testing_thinpool",
                                        &dm,
                                        data_dev.size().unwrap(),
                                        Sectors(1024),
                                        DataBlocks(256000),
                                        metadata_dev,
                                        data_dev)
            .unwrap();
    let thin_dev = ThinDev::new("stratis_testing_thindev",
                                &dm,
                                &thinpool_dev,
                                7,
                                Sectors(300000))
            .unwrap();

    create_fs(&thin_dev.devnode().unwrap()).unwrap();

    let tmp_dir = TempDir::new("stratis_testing").unwrap();
    mount_fs(&thin_dev.devnode().unwrap(), tmp_dir.path()).unwrap();
    for i in 0..100 {
        let file_path = tmp_dir.path().join(format!("stratis_test{}.txt", i));
        writeln!(&OpenOptions::new()
                      .create(true)
                      .write(true)
                      .open(file_path)
                      .unwrap(),
                 "data")
                .unwrap();
    }
    // The -d (detach-loop) is passed for both loopback and real devs,
    // it helps with loopback devs and does no harm for real devs.
    unmount_fs(tmp_dir.path(), &["-d"]).unwrap();
    thin_dev.teardown(&dm).unwrap();
    thinpool_dev.teardown(&dm).unwrap();
}

/// Test that creating a pool claims all blockdevs and that destroying a pool
/// releases all blockdevs.
pub fn test_pool_blockdevs(paths: &[&Path]) -> () {
    let mut engine = StratEngine::initialize().unwrap();
    let (uuid, blockdevs) = engine
        .create_pool("test_pool", paths, None, true)
        .unwrap();
    assert!(blockdevs
                .iter()
                .all(|path| {
                         StaticHeader::determine_ownership(&mut OpenOptions::new()
                                                                    .read(true)
                                                                    .open(path)
                                                                    .unwrap())
                                 .unwrap() == DevOwnership::Ours(uuid)
                     }));
    engine.destroy_pool(&uuid).unwrap();
    assert!(blockdevs
                .iter()
                .all(|path| {
                         StaticHeader::determine_ownership(&mut OpenOptions::new()
                                                                    .read(true)
                                                                    .open(path)
                                                                    .unwrap())
                                 .unwrap() == DevOwnership::Unowned
                     }));
}

/// Verify that tearing down an engine doesn't fail if no filesystems on it.
pub fn test_teardown(paths: &[&Path]) -> () {
    let mut engine = StratEngine::initialize().unwrap();
    engine
        .create_pool("test_pool", paths, None, true)
        .unwrap();
    assert!(engine.teardown().is_ok())
}

/// Verify that find_all function locates and assigns pools appropriately.
/// 1. Split available paths into 2 discrete sets.
/// 2. Initialize the block devices in the first set with a pool uuid.
/// 3. Run find_all() and verify that it has found the initialized devices
/// and no others.
/// 4. Initialize the block devices in the second set with a different pool
/// uuid.
/// 5. Run find_all() again and verify that both sets of devices are found.
/// 6. Verify that get_metadata() return an error. initialize() only
/// initializes block devices, it does not write metadata.
pub fn test_setup(paths: &[&Path]) -> () {
    assert!(paths.len() > 2);

    let (paths1, paths2) = paths.split_at(2);

    let unique_devices = resolve_devices(paths1).unwrap();
    let uuid1 = Uuid::new_v4();
    initialize(&uuid1, unique_devices, MIN_MDA_SECTORS, false).unwrap();

    let pools = find_all().unwrap();
    assert!(pools.len() == 1);
    assert!(pools.contains_key(&uuid1));
    let devices = pools.get(&uuid1).expect("pools.contains_key() was true");
    assert!(devices.len() == paths1.len());

    let unique_devices = resolve_devices(paths2).unwrap();
    let uuid2 = Uuid::new_v4();
    initialize(&uuid2, unique_devices, MIN_MDA_SECTORS, false).unwrap();

    let pools = find_all().unwrap();
    assert!(pools.len() == 2);

    assert!(pools.contains_key(&uuid1));
    let devices1 = pools.get(&uuid1).expect("pools.contains_key() was true");
    assert!(devices1.len() == paths1.len());

    assert!(pools.contains_key(&uuid2));
    let devices2 = pools.get(&uuid2).expect("pools.contains_key() was true");
    assert!(devices2.len() == paths2.len());

    assert!(pools
                .iter()
                .map(|(uuid, devs)| get_metadata(uuid, devs))
                .all(|x| x.unwrap().is_none()));
}

/// Verify that a pool with no devices does not have the minimum amount of
/// space required.
pub fn test_empty_pool(paths: &[&Path]) -> () {
    assert!(paths.len() == 0);
    let mut engine = StratEngine::initialize().unwrap();
    assert!(match engine
                      .create_pool("test_pool", paths, None, true)
                      .unwrap_err() {
                EngineError::Engine(ErrorEnum::Invalid, _) => true,
                _ => false,
            });
}

/// Verify that metadata can be read from pools.
/// 1. Split paths into two separate sets.
/// 2. Create pools from the two sets.
/// 3. Use find_all() to get the devices in the pool.
/// 4. Use get_metadata to find metadata for each pool and verify correctness.
/// 5. Teardown the engine and repeat.
pub fn test_basic_metadata(paths: &[&Path]) {
    assert!(paths.len() > 2);

    let (paths1, paths2) = paths.split_at(2);

    let mut engine = StratEngine::initialize().unwrap();

    let name1 = "name1";
    let (uuid1, _) = engine.create_pool(&name1, paths1, None, false).unwrap();
    let metadata1 = engine
        .get_strat_pool(&uuid1)
        .unwrap()
        .record()
        .unwrap();

    let name2 = "name2";
    let (uuid2, _) = engine.create_pool(&name2, paths2, None, false).unwrap();
    let metadata2 = engine
        .get_strat_pool(&uuid2)
        .unwrap()
        .record()
        .unwrap();

    let pools = find_all().unwrap();
    assert!(pools.len() == 2);
    let devnodes1 = pools.get(&uuid1).unwrap();
    let devnodes2 = pools.get(&uuid2).unwrap();
    let pool_save1 = get_metadata(&uuid1, devnodes1).unwrap().unwrap();
    let pool_save2 = get_metadata(&uuid2, devnodes2).unwrap().unwrap();
    assert!(pool_save1 == metadata1);
    assert!(pool_save2 == metadata2);
    assert!(get_blockdevmgr(&pool_save1, devnodes1).is_ok());
    assert!(get_blockdevmgr(&pool_save2, devnodes2).is_ok());

    engine.teardown().unwrap();
    let pools = find_all().unwrap();
    assert!(pools.len() == 2);
    let devnodes1 = pools.get(&uuid1).unwrap();
    let devnodes2 = pools.get(&uuid2).unwrap();
    let pool_save1 = get_metadata(&uuid1, devnodes1).unwrap().unwrap();
    let pool_save2 = get_metadata(&uuid2, devnodes2).unwrap().unwrap();
    assert!(pool_save1 == metadata1);
    assert!(pool_save2 == metadata2);
    assert!(get_blockdevmgr(&pool_save1, devnodes1).is_ok());
    assert!(get_blockdevmgr(&pool_save2, devnodes2).is_ok());
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    access::ModuleAccess,
    file_format::{
        AbilitySet, CompiledModule, FieldDefinition, FunctionDefinition, SignatureToken,
        StructDefinition, StructFieldInformation, StructTypeParameter, TypeParameterIndex,
        Visibility,
    },
};
use move_core_types::{
    account_address::AccountAddress,
    identifier::Identifier,
    language_storage::{ModuleId, StructTag, TypeTag},
};
use std::collections::BTreeMap;

/// Defines normalized representations of Move types, fields, kinds, structs, functions, and
/// modules. These representations are useful in situations that require require comparing
/// functions, resources, and types across modules. This arises in linking, compatibility checks
/// (e.g., "is it safe to deploy this new module without updating its dependents and/or restarting
/// genesis?"), defining schemas for resources stored on-chain, and (possibly in the future)
/// allowing module updates transactions.

/// A normalized version of `SignatureToken`, a type expression appearing in struct or function
/// declarations. Unlike `SignatureToken`s, `normalized::Type`s from different modules can safely be
/// compared.
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub enum Type {
    Bool,
    U8,
    U64,
    U128,
    Address,
    Signer,
    Struct {
        address: AccountAddress,
        module: Identifier,
        name: Identifier,
        type_arguments: Vec<Type>,
    },
    Vector(Box<Type>),
    TypeParameter(TypeParameterIndex),
    Reference(Box<Type>),
    MutableReference(Box<Type>),
}

/// Normalized version of a `FieldDefinition`. The `name` is included even though it is
/// metadata that it is ignored by the VM. The reason: names are important to clients. We would
/// want a change from `Account { bal: u64, seq: u64 }` to `Account { seq: u64, bal: u64 }` to be
/// marked as incompatible. Not safe to compare without an enclosing `Struct`.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Field {
    pub name: Identifier,
    pub type_: Type,
}

/// Normalized version of a `StructDefinition`. Not safe to compare without an associated
/// `ModuleId` or `Module`.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Struct {
    pub abilities: AbilitySet,
    pub type_parameters: Vec<StructTypeParameter>,
    pub fields: Vec<Field>,
}

/// Normalized version of a `FunctionDefinition`. Not safe to compare without an associated
/// `ModuleId` or `Module`.
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub struct Function {
    pub visibility: Visibility,
    pub type_parameters: Vec<AbilitySet>,
    pub parameters: Vec<Type>,
    pub return_: Vec<Type>,
}

/// Normalized version of a `CompiledModule`: its address, name, struct declarations, and public
/// function declarations.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Module {
    pub address: AccountAddress,
    pub name: Identifier,
    pub friends: Vec<ModuleId>,
    pub structs: BTreeMap<Identifier, Struct>,
    pub exposed_functions: BTreeMap<Identifier, Function>,
}

impl Module {
    /// Extract a normalized module from a `CompiledModule`. The module `m` should be verified.
    /// Nothing will break here if that is not the case, but there is little point in computing a
    /// normalized representation of a module that won't verify (since it can't be published).
    pub fn new(m: &CompiledModule) -> Self {
        let friends = m.immediate_friends();
        let structs = m.struct_defs().iter().map(|d| Struct::new(m, d)).collect();
        let exposed_functions = m
            .function_defs()
            .iter()
            .filter(|func_def| match func_def.visibility {
                Visibility::Public | Visibility::Script | Visibility::Friend => true,
                Visibility::Private => false,
            })
            .map(|func_def| Function::new(m, func_def))
            .collect();

        Self {
            address: *m.address(),
            name: m.name().to_owned(),
            friends,
            structs,
            exposed_functions,
        }
    }

    pub fn module_id(&self) -> ModuleId {
        ModuleId::new(self.address, self.name.clone())
    }
}

impl Type {
    /// Create a normalized `Type` for `SignatureToken` `s` in module `m`.
    pub fn new(m: &CompiledModule, s: &SignatureToken) -> Self {
        use SignatureToken::*;
        match s {
            Struct(shi) => {
                let s_handle = m.struct_handle_at(*shi);
                assert!(s_handle.type_parameters.is_empty(), "A struct with N type parameters should be encoded as StructModuleInstantiation with type_arguments = [TypeParameter(1), ..., TypeParameter(N)]");
                let m_handle = m.module_handle_at(s_handle.module);
                Type::Struct {
                    address: *m.address_identifier_at(m_handle.address),
                    module: m.identifier_at(m_handle.name).to_owned(),
                    name: m.identifier_at(s_handle.name).to_owned(),
                    type_arguments: Vec::new(),
                }
            }
            StructInstantiation(shi, type_actuals) => {
                let s_handle = m.struct_handle_at(*shi);
                let m_handle = m.module_handle_at(s_handle.module);
                Type::Struct {
                    address: *m.address_identifier_at(m_handle.address),
                    module: m.identifier_at(m_handle.name).to_owned(),
                    name: m.identifier_at(s_handle.name).to_owned(),
                    type_arguments: type_actuals.iter().map(|t| Type::new(m, t)).collect(),
                }
            }
            Bool => Type::Bool,
            U8 => Type::U8,
            U64 => Type::U64,
            U128 => Type::U128,
            Address => Type::Address,
            Signer => Type::Signer,
            Vector(t) => Type::Vector(Box::new(Type::new(m, t))),
            TypeParameter(i) => Type::TypeParameter(*i),
            Reference(t) => Type::Reference(Box::new(Type::new(m, t))),
            MutableReference(t) => Type::MutableReference(Box::new(Type::new(m, t))),
        }
    }

    /// Return true if `self` is a closed type with no free type variables
    pub fn is_closed(&self) -> bool {
        use Type::*;
        match self {
            TypeParameter(_) => false,
            Bool => true,
            U8 => true,
            U64 => true,
            U128 => true,
            Address => true,
            Signer => true,
            Struct { type_arguments, .. } => type_arguments.iter().all(|t| t.is_closed()),
            Vector(t) | Reference(t) | MutableReference(t) => t.is_closed(),
        }
    }

    pub fn into_type_tag(self) -> Option<TypeTag> {
        use Type::*;
        Some(if self.is_closed() {
            match self {
                Reference(_) | MutableReference(_) => return None,
                Bool => TypeTag::Bool,
                U8 => TypeTag::U8,
                U64 => TypeTag::U64,
                U128 => TypeTag::U128,
                Address => TypeTag::Address,
                Signer => TypeTag::Signer,
                Vector(t) => TypeTag::Vector(Box::new(
                    t.into_type_tag()
                        .expect("Invariant violation: vector type argument contains reference"),
                )),
                Struct {
                    address,
                    module,
                    name,
                    type_arguments,
                } => TypeTag::Struct(StructTag {
                    address,
                    module,
                    name,
                    type_params: type_arguments
                        .into_iter()
                        .map(|t| {
                            t.into_type_tag().expect(
                                "Invariant violation: struct type argument contains reference",
                            )
                        })
                        .collect(),
                }),
                TypeParameter(_) => unreachable!(),
            }
        } else {
            return None;
        })
    }

    pub fn into_struct_tag(self) -> Option<StructTag> {
        match self.into_type_tag()? {
            TypeTag::Struct(s) => Some(s),
            _ => None,
        }
    }

    pub fn subst(&self, type_args: &[Type]) -> Self {
        use Type::*;
        match self {
            Bool | U8 | U64 | U128 | Address | Signer => self.clone(),
            Reference(ty) => Reference(Box::new(ty.subst(type_args))),
            MutableReference(ty) => MutableReference(Box::new(ty.subst(type_args))),
            Vector(t) => Vector(Box::new(t.subst(type_args))),
            Struct {
                address,
                module,
                name,
                type_arguments,
            } => Struct {
                address: *address,
                module: module.clone(),
                name: name.clone(),
                type_arguments: type_arguments
                    .iter()
                    .map(|t| t.subst(type_arguments))
                    .collect::<Vec<_>>(),
            },
            TypeParameter(i) => type_args
                .get(*i as usize)
                .expect("Type parameter index out of bound")
                .clone(),
        }
    }
}

impl Field {
    /// Create a `Field` for `FieldDefinition` `f` in module `m`.
    pub fn new(m: &CompiledModule, f: &FieldDefinition) -> Self {
        Field {
            name: m.identifier_at(f.name).to_owned(),
            type_: Type::new(m, &f.signature.0),
        }
    }
}

impl Struct {
    /// Create a `Struct` for `StructDefinition` `def` in module `m`. Panics if `def` is a
    /// a native struct definition.
    pub fn new(m: &CompiledModule, def: &StructDefinition) -> (Identifier, Self) {
        let handle = m.struct_handle_at(def.struct_handle);
        let fields = match &def.field_information {
            StructFieldInformation::Native => panic!("Can't extract  for native struct"),
            StructFieldInformation::Declared(fields) => {
                fields.iter().map(|f| Field::new(m, f)).collect()
            }
        };
        let name = m.identifier_at(handle.name).to_owned();
        let s = Struct {
            abilities: handle.abilities,
            type_parameters: handle.type_parameters.clone(),
            fields,
        };
        (name, s)
    }

    pub fn type_param_constraints(&self) -> impl ExactSizeIterator<Item = &AbilitySet> {
        self.type_parameters.iter().map(|param| &param.constraints)
    }
}

impl Function {
    /// Create a `FunctionSignature` for `FunctionHandle` `f` in module `m`.
    pub fn new(m: &CompiledModule, def: &FunctionDefinition) -> (Identifier, Self) {
        let fhandle = m.function_handle_at(def.function);
        let name = m.identifier_at(fhandle.name).to_owned();
        let f = Function {
            visibility: def.visibility,
            type_parameters: fhandle.type_parameters.clone(),
            parameters: m
                .signature_at(fhandle.parameters)
                .0
                .iter()
                .map(|s| Type::new(m, s))
                .collect(),
            return_: m
                .signature_at(fhandle.return_)
                .0
                .iter()
                .map(|s| Type::new(m, s))
                .collect(),
        };
        (name, f)
    }
}

impl From<&TypeTag> for Type {
    fn from(ty: &TypeTag) -> Type {
        use Type::*;
        match ty {
            TypeTag::Bool => Bool,
            TypeTag::U8 => U8,
            TypeTag::U64 => U64,
            TypeTag::U128 => U128,
            TypeTag::Address => Address,
            TypeTag::Signer => Signer,
            TypeTag::Vector(ty) => Vector(Box::new(Type::from(&**ty))),
            TypeTag::Struct(s) => Struct {
                address: s.address,
                module: s.module.clone(),
                name: s.name.clone(),
                type_arguments: s.type_params.iter().map(|ty| ty.into()).collect(),
            },
        }
    }
}

impl std::fmt::Display for Type {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            Type::Struct {
                address,
                module,
                name,
                type_arguments,
            } => {
                write!(
                    f,
                    "0x{}::{}::{}",
                    address.short_str_lossless(),
                    module,
                    name
                )?;
                if let Some(first_ty) = type_arguments.first() {
                    write!(f, "<")?;
                    write!(f, "{}", first_ty)?;
                    for ty in type_arguments.iter().skip(1) {
                        write!(f, ", {}", ty)?;
                    }
                    write!(f, ">")?;
                }
                Ok(())
            }
            Type::Vector(ty) => write!(f, "Vector<{}>", ty),
            Type::U8 => write!(f, "U8"),
            Type::U64 => write!(f, "U64"),
            Type::U128 => write!(f, "U128"),
            Type::Address => write!(f, "Address"),
            Type::Signer => write!(f, "Signer"),
            Type::Bool => write!(f, "Bool"),
            Type::Reference(r) => write!(f, "&{}", r),
            Type::MutableReference(r) => write!(f, "&mut {}", r),
            Type::TypeParameter(i) => write!(f, "#{:?}", i),
        }
    }
}
<commit_msg>[move] Nits from #8789<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    access::ModuleAccess,
    file_format::{
        AbilitySet, CompiledModule, FieldDefinition, FunctionDefinition, SignatureToken,
        StructDefinition, StructFieldInformation, StructTypeParameter, TypeParameterIndex,
        Visibility,
    },
};
use move_core_types::{
    account_address::AccountAddress,
    identifier::Identifier,
    language_storage::{ModuleId, StructTag, TypeTag},
};
use std::collections::BTreeMap;

/// Defines normalized representations of Move types, fields, kinds, structs, functions, and
/// modules. These representations are useful in situations that require require comparing
/// functions, resources, and types across modules. This arises in linking, compatibility checks
/// (e.g., "is it safe to deploy this new module without updating its dependents and/or restarting
/// genesis?"), defining schemas for resources stored on-chain, and (possibly in the future)
/// allowing module updates transactions.

/// A normalized version of `SignatureToken`, a type expression appearing in struct or function
/// declarations. Unlike `SignatureToken`s, `normalized::Type`s from different modules can safely be
/// compared.
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub enum Type {
    Bool,
    U8,
    U64,
    U128,
    Address,
    Signer,
    Struct {
        address: AccountAddress,
        module: Identifier,
        name: Identifier,
        type_arguments: Vec<Type>,
    },
    Vector(Box<Type>),
    TypeParameter(TypeParameterIndex),
    Reference(Box<Type>),
    MutableReference(Box<Type>),
}

/// Normalized version of a `FieldDefinition`. The `name` is included even though it is
/// metadata that it is ignored by the VM. The reason: names are important to clients. We would
/// want a change from `Account { bal: u64, seq: u64 }` to `Account { seq: u64, bal: u64 }` to be
/// marked as incompatible. Not safe to compare without an enclosing `Struct`.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Field {
    pub name: Identifier,
    pub type_: Type,
}

/// Normalized version of a `StructDefinition`. Not safe to compare without an associated
/// `ModuleId` or `Module`.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Struct {
    pub abilities: AbilitySet,
    pub type_parameters: Vec<StructTypeParameter>,
    pub fields: Vec<Field>,
}

/// Normalized version of a `FunctionDefinition`. Not safe to compare without an associated
/// `ModuleId` or `Module`.
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
pub struct Function {
    pub visibility: Visibility,
    pub type_parameters: Vec<AbilitySet>,
    pub parameters: Vec<Type>,
    pub return_: Vec<Type>,
}

/// Normalized version of a `CompiledModule`: its address, name, struct declarations, and public
/// function declarations.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Module {
    pub address: AccountAddress,
    pub name: Identifier,
    pub friends: Vec<ModuleId>,
    pub structs: BTreeMap<Identifier, Struct>,
    pub exposed_functions: BTreeMap<Identifier, Function>,
}

impl Module {
    /// Extract a normalized module from a `CompiledModule`. The module `m` should be verified.
    /// Nothing will break here if that is not the case, but there is little point in computing a
    /// normalized representation of a module that won't verify (since it can't be published).
    pub fn new(m: &CompiledModule) -> Self {
        let friends = m.immediate_friends();
        let structs = m.struct_defs().iter().map(|d| Struct::new(m, d)).collect();
        let exposed_functions = m
            .function_defs()
            .iter()
            .filter(|func_def| match func_def.visibility {
                Visibility::Public | Visibility::Script | Visibility::Friend => true,
                Visibility::Private => false,
            })
            .map(|func_def| Function::new(m, func_def))
            .collect();

        Self {
            address: *m.address(),
            name: m.name().to_owned(),
            friends,
            structs,
            exposed_functions,
        }
    }

    pub fn module_id(&self) -> ModuleId {
        ModuleId::new(self.address, self.name.clone())
    }
}

impl Type {
    /// Create a normalized `Type` for `SignatureToken` `s` in module `m`.
    pub fn new(m: &CompiledModule, s: &SignatureToken) -> Self {
        use SignatureToken::*;
        match s {
            Struct(shi) => {
                let s_handle = m.struct_handle_at(*shi);
                assert!(s_handle.type_parameters.is_empty(), "A struct with N type parameters should be encoded as StructModuleInstantiation with type_arguments = [TypeParameter(1), ..., TypeParameter(N)]");
                let m_handle = m.module_handle_at(s_handle.module);
                Type::Struct {
                    address: *m.address_identifier_at(m_handle.address),
                    module: m.identifier_at(m_handle.name).to_owned(),
                    name: m.identifier_at(s_handle.name).to_owned(),
                    type_arguments: Vec::new(),
                }
            }
            StructInstantiation(shi, type_actuals) => {
                let s_handle = m.struct_handle_at(*shi);
                let m_handle = m.module_handle_at(s_handle.module);
                Type::Struct {
                    address: *m.address_identifier_at(m_handle.address),
                    module: m.identifier_at(m_handle.name).to_owned(),
                    name: m.identifier_at(s_handle.name).to_owned(),
                    type_arguments: type_actuals.iter().map(|t| Type::new(m, t)).collect(),
                }
            }
            Bool => Type::Bool,
            U8 => Type::U8,
            U64 => Type::U64,
            U128 => Type::U128,
            Address => Type::Address,
            Signer => Type::Signer,
            Vector(t) => Type::Vector(Box::new(Type::new(m, t))),
            TypeParameter(i) => Type::TypeParameter(*i),
            Reference(t) => Type::Reference(Box::new(Type::new(m, t))),
            MutableReference(t) => Type::MutableReference(Box::new(Type::new(m, t))),
        }
    }

    /// Return true if `self` is a closed type with no free type variables
    pub fn is_closed(&self) -> bool {
        use Type::*;
        match self {
            TypeParameter(_) => false,
            Bool => true,
            U8 => true,
            U64 => true,
            U128 => true,
            Address => true,
            Signer => true,
            Struct { type_arguments, .. } => type_arguments.iter().all(|t| t.is_closed()),
            Vector(t) | Reference(t) | MutableReference(t) => t.is_closed(),
        }
    }

    pub fn into_type_tag(self) -> Option<TypeTag> {
        use Type::*;
        Some(if self.is_closed() {
            match self {
                Reference(_) | MutableReference(_) => return None,
                Bool => TypeTag::Bool,
                U8 => TypeTag::U8,
                U64 => TypeTag::U64,
                U128 => TypeTag::U128,
                Address => TypeTag::Address,
                Signer => TypeTag::Signer,
                Vector(t) => TypeTag::Vector(Box::new(
                    t.into_type_tag()
                        .expect("Invariant violation: vector type argument contains reference"),
                )),
                Struct {
                    address,
                    module,
                    name,
                    type_arguments,
                } => TypeTag::Struct(StructTag {
                    address,
                    module,
                    name,
                    type_params: type_arguments
                        .into_iter()
                        .map(|t| {
                            t.into_type_tag().expect(
                                "Invariant violation: struct type argument contains reference",
                            )
                        })
                        .collect(),
                }),
                TypeParameter(_) => unreachable!(),
            }
        } else {
            return None;
        })
    }

    pub fn into_struct_tag(self) -> Option<StructTag> {
        match self.into_type_tag()? {
            TypeTag::Struct(s) => Some(s),
            _ => None,
        }
    }

    pub fn subst(&self, type_args: &[Type]) -> Self {
        use Type::*;
        match self {
            Bool | U8 | U64 | U128 | Address | Signer => self.clone(),
            Reference(ty) => Reference(Box::new(ty.subst(type_args))),
            MutableReference(ty) => MutableReference(Box::new(ty.subst(type_args))),
            Vector(t) => Vector(Box::new(t.subst(type_args))),
            Struct {
                address,
                module,
                name,
                type_arguments,
            } => Struct {
                address: *address,
                module: module.clone(),
                name: name.clone(),
                type_arguments: type_arguments
                    .iter()
                    .map(|t| t.subst(type_arguments))
                    .collect::<Vec<_>>(),
            },
            TypeParameter(i) => type_args
                .get(*i as usize)
                .expect("Type parameter index out of bound")
                .clone(),
        }
    }
}

impl Field {
    /// Create a `Field` for `FieldDefinition` `f` in module `m`.
    pub fn new(m: &CompiledModule, f: &FieldDefinition) -> Self {
        Field {
            name: m.identifier_at(f.name).to_owned(),
            type_: Type::new(m, &f.signature.0),
        }
    }
}

impl Struct {
    /// Create a `Struct` for `StructDefinition` `def` in module `m`. Panics if `def` is a
    /// a native struct definition.
    pub fn new(m: &CompiledModule, def: &StructDefinition) -> (Identifier, Self) {
        let handle = m.struct_handle_at(def.struct_handle);
        let fields = match &def.field_information {
            StructFieldInformation::Native => panic!("Can't extract  for native struct"),
            StructFieldInformation::Declared(fields) => {
                fields.iter().map(|f| Field::new(m, f)).collect()
            }
        };
        let name = m.identifier_at(handle.name).to_owned();
        let s = Struct {
            abilities: handle.abilities,
            type_parameters: handle.type_parameters.clone(),
            fields,
        };
        (name, s)
    }

    pub fn type_param_constraints(&self) -> impl ExactSizeIterator<Item = &AbilitySet> {
        self.type_parameters.iter().map(|param| &param.constraints)
    }
}

impl Function {
    /// Create a `FunctionSignature` for `FunctionHandle` `f` in module `m`.
    pub fn new(m: &CompiledModule, def: &FunctionDefinition) -> (Identifier, Self) {
        let fhandle = m.function_handle_at(def.function);
        let name = m.identifier_at(fhandle.name).to_owned();
        let f = Function {
            visibility: def.visibility,
            type_parameters: fhandle.type_parameters.clone(),
            parameters: m
                .signature_at(fhandle.parameters)
                .0
                .iter()
                .map(|s| Type::new(m, s))
                .collect(),
            return_: m
                .signature_at(fhandle.return_)
                .0
                .iter()
                .map(|s| Type::new(m, s))
                .collect(),
        };
        (name, f)
    }
}

impl From<TypeTag> for Type {
    fn from(ty: TypeTag) -> Type {
        use Type::*;
        match ty {
            TypeTag::Bool => Bool,
            TypeTag::U8 => U8,
            TypeTag::U64 => U64,
            TypeTag::U128 => U128,
            TypeTag::Address => Address,
            TypeTag::Signer => Signer,
            TypeTag::Vector(ty) => Vector(Box::new(Type::from(*ty))),
            TypeTag::Struct(s) => Struct {
                address: s.address,
                module: s.module,
                name: s.name,
                type_arguments: s.type_params.into_iter().map(|ty| ty.into()).collect(),
            },
        }
    }
}

impl std::fmt::Display for Type {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            Type::Struct {
                address,
                module,
                name,
                type_arguments,
            } => {
                write!(
                    f,
                    "0x{}::{}::{}",
                    address.short_str_lossless(),
                    module,
                    name
                )?;
                if let Some(first_ty) = type_arguments.first() {
                    write!(f, "<")?;
                    write!(f, "{}", first_ty)?;
                    for ty in type_arguments.iter().skip(1) {
                        write!(f, ", {}", ty)?;
                    }
                    write!(f, ">")?;
                }
                Ok(())
            }
            Type::Vector(ty) => write!(f, "Vector<{}>", ty),
            Type::U8 => write!(f, "U8"),
            Type::U64 => write!(f, "U64"),
            Type::U128 => write!(f, "U128"),
            Type::Address => write!(f, "Address"),
            Type::Signer => write!(f, "Signer"),
            Type::Bool => write!(f, "Bool"),
            Type::Reference(r) => write!(f, "&{}", r),
            Type::MutableReference(r) => write!(f, "&mut {}", r),
            Type::TypeParameter(i) => write!(f, "#{:?}", i),
        }
    }
}
<|endoftext|>


<commit_before>use crate::errors::OscError;

use alloc::borrow::ToOwned;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use nom::branch::alt;
use nom::bytes::complete::{is_not, tag, take_till1};
use nom::character::complete::char;
use nom::combinator::{all_consuming, map_parser};
use nom::multi::many1;
use nom::sequence::{delimited, preceded};
use nom::{IResult, Parser};
use regex::Regex;

/// With a Matcher OSC method addresses can be [matched](Matcher::match_address) against an OSC address pattern.
/// Refer to the OSC specification for details about OSC address spaces: <http://opensoundcontrol.org/spec-1_0.html#osc-address-spaces-and-osc-addresses>
#[derive(Debug)]
pub struct Matcher {
    res: Vec<Regex>,
}

impl Matcher {

    /// Instantiates a new `Matcher` with the given address pattern.
    /// An error will be returned if the given address pattern is invalid.
    ///
    /// Matcher should be instantiated once per pattern and reused because its construction requires parsing the address pattern which is computationally expensive.
    ///
    /// A valid address pattern begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// OSC defines a couple of rules that look like regular expression but are subtly different:
    ///
    /// - `?` matches a single character
    /// - `*` matches zero or more characters
    /// - `[a-z]` are basically regex [character classes](https://www.regular-expressions.info/charclass.html)
    /// - `{foo,bar}` is an alternative, matching either `foo` or `bar`
    /// - everything else is matched literally
    ///
    /// Refer to the OSC specification for details about address pattern matching: <osc-message-dispatching-and-pattern-matching>.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// Matcher::new("/tempo").expect("valid address");
    /// Matcher::new("").expect_err("address does not start with a slash");
    /// ```
    pub fn new(pattern: &str) -> Result<Self, OscError> {
        let res = parse_address_pattern(pattern)?;
        Ok(Matcher { res })
    }

    /// Match an OSC address against an address pattern.
    /// If the address matches the pattern the result will be `true`, otherwise `false`.
    /// An error is returned if the given OSC address is not valid.
    ///
    /// A valid OSC address begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// Despite OSC address patterns a plain address must not include any of the following characters `#*,/?[]{}`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// let matcher = Matcher::new("/oscillator/[0-9]/{frequency,phase}").unwrap();
    /// assert!(matcher.match_address("/oscillator/1/frequency").unwrap());
    /// assert!(matcher.match_address("/oscillator/8/phase").unwrap());
    /// assert_eq!(matcher.match_address("/oscillator/4/detune").unwrap(), false);
    /// ```
    pub fn match_address(&self, address: &str) -> Result<bool, OscError> {
        let (_, parts) = all_consuming(many1(parse_address_part))(address)
            .map_err(|_| OscError::BadAddress("bad address".to_string()))?;
        if parts.len() != self.res.len() {
            return Ok(false);
        }
        Ok(self
            .res
            .iter()
            .zip(parts)
            .all(|(re, part)| re.is_match(part)))
    }
}

fn map_alternative(s: &str) -> String {
    wrap_with(&s.replace(',', "|"), "(", ")")
}

fn wrap_with(s: &str, pre: &str, post: &str) -> String {
    pre.to_string() + s + post
}

fn map_wildcard(_: &str) -> String {
    r"\w*".into()
}

fn map_question_mark(_: &str) -> String {
    r"\w?".into()
}

fn parse_address_part(input: &str) -> IResult<&str, &str> {
    preceded(char('/'), take_till1(|c| " \t\r\n#*,/?[]{}".contains(c)))(input)
}

fn parse_address_pattern_part(input: &str) -> IResult<&str, &str> {
    preceded(char('/'), take_till1(|c| " \n\t\r/".contains(c)))(input)
}

// Translate OSC pattern rules into an regular expression.
// A pattern part can contain more than one rule, e.g. `{voice,synth}-[1-9]` contains two rules, an alternative and a number range.
fn parse_pattern_part(input: &str) -> IResult<&str, String> {
    many1(alt((
        delimited(char('{'), is_not("}"), char('}')).map(map_alternative),
        delimited(tag("[!"), is_not("]"), char(']')).map(|s: &str| wrap_with(s, "[^", "]")),
        delimited(char('['), is_not("]"), char(']')).map(|s: &str| wrap_with(s, "[", "]")),
        tag("*").map(map_wildcard),
        tag("?").map(map_question_mark),
        is_not("[{").map(|s: &str| s.to_owned()),
    )))(input)
    .map(|(input, parts)| (input, parts.concat()))
}

fn parse_address_pattern(input: &str) -> Result<Vec<Regex>, OscError> {
    let (_, patterns) = all_consuming(many1(map_parser(
        parse_address_pattern_part,
        parse_pattern_part,
    )))(input)
    .map_err(|_| OscError::BadAddressPattern("bad address pattern".to_string()))?;
    patterns
        .iter()
        .map(|p| Regex::new(p))
        .collect::<Result<Vec<Regex>, regex::Error>>()
        .map_err(|err| OscError::RegexError(err.to_string()))
}
<commit_msg>Add nom-based matching<commit_after>use crate::errors::OscError;

use alloc::borrow::ToOwned;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use nom::branch::alt;
use nom::bytes::complete::{is_not, tag, take_till1, take, is_a};
use nom::character::complete::{alphanumeric1, char, satisfy};
use nom::combinator::{all_consuming, complete, map_parser, verify};
use nom::multi::{many1, separated_list0};
use nom::sequence::{delimited, preceded, separated_pair};
use nom::{IResult, Parser};
use nom::error::{ErrorKind, ParseError};
use regex::Regex;

/// With a Matcher OSC method addresses can be [matched](Matcher::match_address) against an OSC address pattern.
/// Refer to the OSC specification for details about OSC address spaces: <http://opensoundcontrol.org/spec-1_0.html#osc-address-spaces-and-osc-addresses>
#[derive(Debug)]
pub struct Matcher {
    pub pattern: String,
    pattern_parts: Vec<AddressPatternComponent>,
}

impl Matcher {
    /// Instantiates a new `Matcher` with the given address pattern.
    /// An error will be returned if the given address pattern is invalid.
    ///
    /// Matcher should be instantiated once per pattern and reused because its construction requires parsing the address pattern which is computationally expensive.
    ///
    /// A valid address pattern begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// OSC defines a couple of rules that look like regular expression but are subtly different:
    ///
    /// - `?` matches a single character
    /// - `*` matches zero or more characters
    /// - `[a-z]` are basically regex [character classes](https://www.regular-expressions.info/charclass.html)
    /// - `{foo,bar}` is an alternative, matching either `foo` or `bar`
    /// - everything else is matched literally
    ///
    /// Refer to the OSC specification for details about address pattern matching: <osc-message-dispatching-and-pattern-matching>.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// Matcher::new("/tempo").expect("valid address");
    /// Matcher::new("").expect_err("address does not start with a slash");
    /// ```
    pub fn new(pattern: &str) -> Result<Self, OscError> {
        let pattern_parts = match all_consuming(many1(get_address_pattern_components))(pattern) {
            Ok((_, parts)) => { parts }
            Err(_) => panic!("Address must be valid")
        };

        Ok(Matcher { pattern: String::from(pattern), pattern_parts })
    }

    /// Match an OSC address against an address pattern.
    /// If the address matches the pattern the result will be `true`, otherwise `false`.
    /// An error is returned if the given OSC address is not valid.
    ///
    /// A valid OSC address begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// Despite OSC address patterns a plain address must not include any of the following characters `#*,/?[]{}`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// let matcher = Matcher::new("/oscillator/[0-9]/{frequency,phase}").unwrap();
    /// assert!(matcher.match_address("/oscillator/1/frequency").unwrap());
    /// assert!(matcher.match_address("/oscillator/8/phase").unwrap());
    /// assert_eq!(matcher.match_address("/oscillator/4/detune").unwrap(), false);
    /// ```
    pub fn match_address(&self, address: &str) -> Result<bool, OscError> {
        let mut remainder: &str = address;
        for (index, part) in self.pattern_parts.as_slice().iter().enumerate() {
            let result = match part {
                AddressPatternComponent::Tag(s) => match_literally(remainder, s.as_str()),
                AddressPatternComponent::WildcardSingle => match_wildcard_single(remainder),
                AddressPatternComponent::Wildcard(l) => {
                    // Check if this is the last pattern component
                    if index < self.pattern_parts.len() - 1 {
                        let next = &self.pattern_parts[index + 1];
                        match next {
                            // If the next component is a '/', there are no more components in the current part and it can be wholly consumed
                            AddressPatternComponent::Tag(s) if s == "/" => match_wildcard(remainder, l.clone(), None),
                            _ => match_wildcard(remainder, l.clone(), Some(next))
                        }
                    } else {
                        match_wildcard(remainder, l.clone(), None)
                    }
                }
                AddressPatternComponent::CharacterClass(cc) => match_character_class(remainder, cc),
                AddressPatternComponent::Choice(s) => match_choice(remainder, s),
            };

            match result {
                Ok((i, _)) => remainder = i,
                Err(_) => return Err(OscError::Unimplemented) // TODO
            };
        }
        return if remainder.len() == 0 {
            Ok(true)
        } else {
            Err(OscError::Unimplemented)     // TODO
        };
    }
}

fn map_alternative(s: &str) -> String {
    wrap_with(&s.replace(',', "|"), "(", ")")
}

fn wrap_with(s: &str, pre: &str, post: &str) -> String {
    pre.to_string() + s + post
}

fn map_wildcard(_: &str) -> String {
    r"\w*".into()
}

fn map_question_mark(_: &str) -> String {
    r"\w?".into()
}

fn parse_address_part(input: &str) -> IResult<&str, &str> {
    preceded(char('/'), take_till1(|c| " \t\r\n#*,/?[]{}".contains(c)))(input)
}

fn parse_address_pattern_part(input: &str) -> IResult<&str, &str> {
    preceded(char('/'), take_till1(|c| " \n\t\r/".contains(c)))(input)
}

// Translate OSC pattern rules into an regular expression.
// A pattern part can contain more than one rule, e.g. `{voice,synth}-[1-9]` contains two rules, an alternative and a number range.
fn parse_pattern_part(input: &str) -> IResult<&str, String> {
    many1(alt((
        delimited(char('{'), is_not("}"), char('}')).map(map_alternative),
        delimited(tag("[!"), is_not("]"), char(']')).map(|s: &str| wrap_with(s, "[^", "]")),
        delimited(char('['), is_not("]"), char(']')).map(|s: &str| wrap_with(s, "[", "]")),
        tag("*").map(map_wildcard),
        tag("?").map(map_question_mark),
        is_not("[{").map(|s: &str| s.to_owned()),
    )))(input)
    .map(|(input, parts)| (input, parts.concat()))
}

fn parse_address_pattern(input: &str) -> Result<Vec<Regex>, OscError> {
    let (_, patterns) = all_consuming(many1(map_parser(
        parse_address_pattern_part,
        parse_pattern_part,
    )))(input)
    .map_err(|_| OscError::BadAddressPattern("bad address pattern".to_string()))?;
    patterns
        .iter()
        .map(|p| Regex::new(p))
        .collect::<Result<Vec<Regex>, regex::Error>>()
        .map_err(|err| OscError::RegexError(err.to_string()))
}

#[derive(Debug)]
struct CharacterClass {
    pub negated: bool,
    pub characters: String,
}

/// Expand a character range like 'a-d' to all the letters contained in the range, e.g. 'abcd'
fn expand_character_range<'a>(first: char, second: char) -> String {
    let start = first as u8;
    let end = second as u8;

    let range;
    if start >= end {
        range = end..=start;
    } else {
        range = start..=end;
    }

    let mut out = String::from("");
    for c in range {
        out.push(c as char);
    }
    // TODO: for funky but legal ranges like '0-a' we need to clean up the ranges
    out
}

fn is_alphanumeric_char(x: char) -> bool {
    x.is_alphanumeric()
}

impl CharacterClass {
    pub fn new(s: &str) -> Self {
        let mut input = s;
        let negated;
        match char::<_, nom::error::Error<&str>>('!')(input) {
            Ok((i, _)) => {
                negated = true;
                input = i;
            }
            Err(_) => negated = false
        }

        let characters = complete(many1(alt((
            // '!' besides at beginning has no special meaning, but is legal
            char::<_, nom::error::Error<&str>>('!').map(|_| String::from("")),
            // attempt to match a range like a-z or 0-9
            separated_pair(satisfy(is_alphanumeric_char), char('-'), satisfy(is_alphanumeric_char)).map(|(first, second)| expand_character_range(first, second)),
            // Match characters literally
            satisfy(is_alphanumeric_char).map(|x| x.to_string()),
            // Trailing dash
            char('-').map(|_| { String::from("-") })
        ))))(input);

        match characters {
            // TODO: Deduplicate?
            Ok((_, o)) => CharacterClass { negated, characters: o.concat() },
            _ => { panic!("Invalid character class formatting {}", s) }
        }
    }
}

#[derive(Debug)]
enum AddressPatternComponent {
    Tag(String),
    Wildcard(usize),
    WildcardSingle,
    CharacterClass(CharacterClass),
    Choice(Vec<String>),
}

fn get_address_pattern_components(input: &str) -> IResult<&str, AddressPatternComponent>
{
    alt((
        // Anything that's alphanumeric gets matched literally
        alphanumeric1.map(|s: &str| { AddressPatternComponent::Tag(String::from(s)) }), // TODO: this should be all printable ascii characters, not just alphanum!
        // Slashes must be seperated into their own tag for the non-greedy implementation of wildcards
        char('/').map(|c: char| { AddressPatternComponent::Tag(c.to_string()) }),
        tag("?").map(|_| { AddressPatternComponent::WildcardSingle }),
        // Combinations of wildcards are a bit tricky.
        // Multiple '*' wildcards in a row are equal to a single '*'.
        // A '*' wildcard followed by any number of '?' wildcards is also equal to '*' but must
        // match at least the same amount of characters as there are '?' wildcards in the combination.
        // For example, '*??' must match at least 2 characters.
        is_a("*?").map(|x: &str| { AddressPatternComponent::Wildcard(x.matches("?").count()) }),
        map_parser(
            delimited(char('{'), is_not("}"), char('}')),
            separated_list0(char(','.into()), alphanumeric1),
        ).map(|alternatives: Vec<&str>| { AddressPatternComponent::Choice(alternatives.iter().map(|x| x.to_string()).collect()) }),
        delimited(char('['), is_not("]") /*TODO: Only allowed: alphanum and '!-' */, char(']')).map(|s: &str| { AddressPatternComponent::CharacterClass(CharacterClass::new(s)) })
    ))(input)
}

fn match_literally<'a>(input: &'a str, pattern: &str) -> IResult<&'a str, &'a str>
{
    tag(pattern)(input)
}

fn match_wildcard_single(input: &str) -> IResult<&str, &str>
{
    // TODO: this has to be possible with a simpler parser?
    verify(take(1usize), |s: &str| s.chars().all(char::is_alphanumeric))(input)
}

fn match_character_class<'a>(input: &'a str, character_class: &'a CharacterClass) -> IResult<&'a str, &'a str> {
    if character_class.negated {
        is_not(character_class.characters.as_str())(input)
    } else {
        is_a(character_class.characters.as_str())(input)
    }
}

/// Try all the tags in a choice element
/// Example choice element: '{foo,bar}'
/// It will get parsed into a vector containing the strings "foo" and "bar", which are then matched
fn match_choice<'a>(input: &'a str, choices: &Vec<String>) -> IResult<&'a str, &'a str> {
    for choice in choices
    {
        match tag::<_, _, nom::error::Error<&str>>(choice.as_str())(input) {
            Ok((i, o)) => return Ok((i, o)),
            Err(_) => {}
        }
    }
    return Err(nom::Err::Error(nom::error::Error::from_error_kind(input, ErrorKind::Tag)));
}

/// Match Wildcard '*' by either consuming the rest of the part, or, if it's not the last component
/// in the part, by looking ahead and matching the next component
fn match_wildcard<'a>(input: &'a str, minimum_length: usize, next: Option<&AddressPatternComponent>) -> IResult<&'a str, &'a str>
{
    match next {
        // No next component, consume all alphanumeric characters until end or next '/'
        None => verify(alphanumeric1, |s: &str| s.len() >= minimum_length)(input),
        // There is another element in this part, so logic gets a bit more complicated
        Some(component) => {
            // Wildcards can only match within the current address part, discard the rest
            let address_part = match input.split_once("/") {
                Some((p, _)) => p,
                None => input
            };

            // Attempt to find the latest matching occurrence of the next pattern component
            // This is a greedy wildcard implementation
            let mut longest: usize = 0;
            for i in 0..address_part.len() {
                let (_, substring) = input.split_at(i);
                let result: IResult<_, _, nom::error::Error<&str>> = match component {
                    AddressPatternComponent::Tag(s) => match_literally(substring, s.as_str()),
                    AddressPatternComponent::CharacterClass(cc) => match_character_class(substring, cc),
                    AddressPatternComponent::Choice(s) => match_choice(substring, s),
                    // These two cases are prevented from happening by get_address_pattern_part
                    AddressPatternComponent::WildcardSingle => panic!("Single wildcard ('?') must not follow wildcard ('*')"),
                    AddressPatternComponent::Wildcard(_) => panic!("Double wildcards must be condensed into one"),
                };

                match result {
                    Ok(_) => longest = i,
                    _ => {}
                }
            }
            verify(take(longest), |s: &str| s.len() >= minimum_length)(input)
        }
    }
}<|endoftext|>


<commit_before>use crate::errors::OscError;
use crate::types::{
    Result
};
use regex::Regex;

/// Check if the address of an OSC method is valid
pub fn validate_method_address(addr: &String) -> Result<()>
{
    if !addr.is_ascii() {
        return Err(OscError::BadAddress("Address must only contain ASCII characters"));
    }
    if addr.len() < 1 {
        return Err(OscError::BadAddress("Address must be at least 1 character long"));
    }
    let mut chars = addr.chars();

    // Check if address starts with '/'
    let first = chars.next().unwrap();
    if first != '/' {
        return Err(OscError::BadAddress("Address must start with '/'"));
    }

    // Check if address contains illegal characters
    for char in chars
    {
        if ((char as u8) < 0x20) | ((char as u8) > 0x7E) {
            return Err(OscError::BadAddress("Address may only contain printable ASCII characters"));
        }
        if " #*,?[]{}".chars().any(|x| char == x) {
            return Err(OscError::BadAddress("Address may not contain any of the following characters: ' #*,?[]{}'"));
        }
    }

    return Ok(());
}

/// Check if the address of an OSC message is valid
pub fn validate_message_address(addr: &String) -> Result<()>
{
    if !addr.is_ascii() {
        return Err(OscError::BadAddress("Address must only contain ASCII characters"));
    }
    if addr.len() < 1 {
        return Err(OscError::BadAddress("Address must be at least 1 character long"));
    }
    let mut chars = addr.chars();

    // Check if address starts with '/'
    let first = chars.next().unwrap();
    if first != '/' {
        return Err(OscError::BadAddress("Address must start with '/'"));
    }

    // Validate rest of address
    let mut in_character_range = false;
    let mut in_string_list = false;
    for char in chars
    {
        if ((char as u8) < 0x20) | ((char as u8) > 0x7E) {
            return Err(OscError::BadAddress("Address may only contain printable ASCII characters"));
        }
        if " #".chars().any(|x| char == x) {
            return Err(OscError::BadAddress("Address may not contain any of the following characters: ' #'"));
        }
        if !in_string_list && char == ',' {
            return Err(OscError::BadAddress("Address may not contain any of the following characters outside of string lists: ','"));
        }
        if char == '[' {
            in_character_range = true;
            if in_string_list {
                return Err(OscError::BadAddress("Can not start a character range (square brackets) within string list (curly brackets)"));
            }
        }
        if (char == '/') & in_character_range {
            return Err(OscError::BadAddress("Character range (square brackets) was started but not closed before the next address part started"));
        }
        if char == ']' {
            in_character_range = false;
        }
        if char == '{' {
            in_string_list = true;
        }
        if (char == '/') & in_string_list {
            return Err(OscError::BadAddress("String list (curly brackets) was started but not closed before the next address part started"));
        }
        if char == '}' {
            in_string_list = false;
            if in_character_range {
                return Err(OscError::BadAddress("Can not start a string list (curly brackets) within character range (square brackets)"));
            }
        }
    }
    if in_character_range {
        return Err(OscError::BadAddress("Character range (square brackets) was started but not closed before the end of the address"));
    }
    if in_string_list {
        return Err(OscError::BadAddress("String list (curly brackets) was started but not closed before the end of the address"));
    }

    return Ok(());
}

/// Match a single part of two OSC addresses
fn match_part(message_part: &str, method_part: &str) -> bool
{
    // direct match
    if message_part == method_part {
        return true;
    }

    // Use regex for everything else
    let mut pattern = message_part.to_string()
        .replace("*", ".*")
        .replace("?", ".")
        .replace("[!", "[^")
        .replace(",", ")|(?:")
        .replace("{", "(?:")
        .replace("}", ")");

    // Anchor the pattern to beginning and end of string
    pattern = format!("^{}$", pattern);

    let re = Regex::new(pattern.as_str()).unwrap();

    re.is_match(method_part)
}

/// Check if a message address matches a method address
pub fn match_address(message_addr: &String, method_addr: &String) -> Result<bool>
{
    match validate_message_address(message_addr) {
        Ok(()) => {},
        Err(e) => return Err(e)
    }
    match validate_method_address(method_addr) {
        Ok(()) => {},
        Err(e) => return Err(e)
    }

    let message_addr_parts: Vec<&str> = message_addr.split('/').collect();
    let method_addr_parts: Vec<&str> = method_addr.split('/').collect();

    if message_addr_parts.len() != method_addr_parts.len() {
        // Both addresses must have the same amount of parts
        return Ok(false);
    }

    for i in 1..message_addr_parts.len() {
        //println!("Parts: '{}' '{}' {}", message_addr_parts[i], method_addr_parts[i], match_part(message_addr_parts[i], method_addr_parts[i]));
        if !match_part(message_addr_parts[i], method_addr_parts[i]) {
            return Ok(false);
        }
    }

    Ok(true)
}<commit_msg>Only match word characters<commit_after>use crate::errors::OscError;
use crate::types::{
    Result
};
use regex::Regex;

/// Check if the address of an OSC method is valid
pub fn validate_method_address(addr: &String) -> Result<()>
{
    if !addr.is_ascii() {
        return Err(OscError::BadAddress("Address must only contain ASCII characters"));
    }
    if addr.len() < 1 {
        return Err(OscError::BadAddress("Address must be at least 1 character long"));
    }
    let mut chars = addr.chars();

    // Check if address starts with '/'
    let first = chars.next().unwrap();
    if first != '/' {
        return Err(OscError::BadAddress("Address must start with '/'"));
    }

    // Check if address contains illegal characters
    for char in chars
    {
        if ((char as u8) < 0x20) | ((char as u8) > 0x7E) {
            return Err(OscError::BadAddress("Address may only contain printable ASCII characters"));
        }
        if " #*,?[]{}".chars().any(|x| char == x) {
            return Err(OscError::BadAddress("Address may not contain any of the following characters: ' #*,?[]{}'"));
        }
    }

    return Ok(());
}

/// Check if the address of an OSC message is valid
pub fn validate_message_address(addr: &String) -> Result<()>
{
    if !addr.is_ascii() {
        return Err(OscError::BadAddress("Address must only contain ASCII characters"));
    }
    if addr.len() < 1 {
        return Err(OscError::BadAddress("Address must be at least 1 character long"));
    }
    let mut chars = addr.chars();

    // Check if address starts with '/'
    let first = chars.next().unwrap();
    if first != '/' {
        return Err(OscError::BadAddress("Address must start with '/'"));
    }

    // Validate rest of address
    let mut in_character_range = false;
    let mut in_string_list = false;
    for char in chars
    {
        if ((char as u8) < 0x20) | ((char as u8) > 0x7E) {
            return Err(OscError::BadAddress("Address may only contain printable ASCII characters"));
        }
        if " #".chars().any(|x| char == x) {
            return Err(OscError::BadAddress("Address may not contain any of the following characters: ' #'"));
        }
        if !in_string_list && char == ',' {
            return Err(OscError::BadAddress("Address may not contain any of the following characters outside of string lists: ','"));
        }
        if char == '[' {
            in_character_range = true;
            if in_string_list {
                return Err(OscError::BadAddress("Can not start a character range (square brackets) within string list (curly brackets)"));
            }
        }
        if (char == '/') & in_character_range {
            return Err(OscError::BadAddress("Character range (square brackets) was started but not closed before the next address part started"));
        }
        if char == ']' {
            in_character_range = false;
        }
        if char == '{' {
            in_string_list = true;
        }
        if (char == '/') & in_string_list {
            return Err(OscError::BadAddress("String list (curly brackets) was started but not closed before the next address part started"));
        }
        if char == '}' {
            in_string_list = false;
            if in_character_range {
                return Err(OscError::BadAddress("Can not start a string list (curly brackets) within character range (square brackets)"));
            }
        }
    }
    if in_character_range {
        return Err(OscError::BadAddress("Character range (square brackets) was started but not closed before the end of the address"));
    }
    if in_string_list {
        return Err(OscError::BadAddress("String list (curly brackets) was started but not closed before the end of the address"));
    }

    return Ok(());
}

/// Match a single part of two OSC addresses
fn match_part(message_part: &str, method_part: &str) -> bool
{
    // direct match
    if message_part == method_part {
        return true;
    }

    // Use regex for everything else
    let mut pattern = message_part.to_string()
        .replace("*", "\\w*")
        .replace("?", "\\w")
        .replace("[!", "[^")
        .replace(",", ")|(?:")
        .replace("{", "(?:")
        .replace("}", ")");

    // Anchor the pattern to beginning and end of string
    pattern = format!("^{}$", pattern);

    let re = Regex::new(pattern.as_str()).unwrap();

    re.is_match(method_part)
}

/// Check if a message address matches a method address
pub fn match_address(message_addr: &String, method_addr: &String) -> Result<bool>
{
    match validate_message_address(message_addr) {
        Ok(()) => {},
        Err(e) => return Err(e)
    }
    match validate_method_address(method_addr) {
        Ok(()) => {},
        Err(e) => return Err(e)
    }

    let message_addr_parts: Vec<&str> = message_addr.split('/').collect();
    let method_addr_parts: Vec<&str> = method_addr.split('/').collect();

    if message_addr_parts.len() != method_addr_parts.len() {
        // Both addresses must have the same amount of parts
        return Ok(false);
    }

    for i in 1..message_addr_parts.len() {
        //println!("Parts: '{}' '{}' {}", message_addr_parts[i], method_addr_parts[i], match_part(message_addr_parts[i], method_addr_parts[i]));
        if !match_part(message_addr_parts[i], method_addr_parts[i]) {
            return Ok(false);
        }
    }

    Ok(true)
}<|endoftext|>


<commit_before>
extern crate glfw;

extern crate mog;

use glfw::Context;

fn main() {
    let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

    // Create a windowed mode window and its OpenGL context
    let (window, events) = glfw.create_window(300, 300, "Hello this is window", glfw::Windowed)
        .expect("Failed to create GLFW window.");

    window.set_key_polling(true);
    window.make_current();

    mog::load_with(|s| window.get_proc_address(s));

    let mut ctx = mog::Context::new();
    let vbo = ctx.new_vertex_buffer();
    let vertices = [-0.5f32,-0.5f32,0f32, 0.5f32,-0.5f32,0f32, 0f32,0.5f32,0f32];
    ctx.vertex_data(&vbo, &vertices);

    while !window.should_close() {
        glfw.poll_events();
        for (_, event) in glfw::flush_messages(&events) {
            handle_window_event(&window, event);
        }
    }
}

fn handle_window_event(window: &glfw::Window, event: glfw::WindowEvent) {
    match event {
        glfw::KeyEvent(glfw::KeyEscape, _, glfw::Press, _) => {
            window.set_should_close(true)
        }
        _ => {}
    }
}<commit_msg>Add more functionality to the test app.<commit_after>
extern crate glfw;

extern crate mog;

use glfw::Context;

use mog::{AttributeFloat,AttributeUnsignedByte};

#[allow(dead_code)]
struct Vec3 {
    x: f32,
    y: f32,
    z: f32,
}

#[allow(dead_code)]
struct Rgba {
    r: u8,
    g: u8,
    b: u8,
    a: u8,
}

#[allow(dead_code)]
struct Vertex {
    position: Vec3,
    color: Rgba
}

impl Vertex {
    fn new(x: f32, y: f32, z: f32, r: u8, g: u8, b: u8, a: u8) -> Vertex {
        Vertex { position: Vec3 { x: x, y: y, z: z }, color: Rgba { r: r, g: g, b: b, a: a } }
    }
}

fn main() {
    let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

    // Create a windowed mode window and its OpenGL context
    let (window, events) = glfw.create_window(300, 300, "Hello this is window", glfw::Windowed)
        .expect("Failed to create GLFW window.");

    window.set_key_polling(true);
    window.make_current();

    mog::load_with(|s| window.get_proc_address(s));

    let mut ctx = mog::Context::new();
    let vbo = ctx.new_vertex_buffer();
    let vertices = [
        Vertex::new(-0.5f32, -0.5f32, 0f32, 255, 0, 0, 0),
        Vertex::new(0.5f32, -0.5f32, 0f32, 0, 255, 0, 0),
        Vertex::new(0f32, 0.5f32, 0f32, 0, 0, 255, 0)
        ];
    ctx.vertex_data(&vbo, &vertices);
    let vao = ctx.new_vertex_array_simple([(3, AttributeFloat, false), (4, AttributeUnsignedByte, true)], vbo, None);

    while !window.should_close() {
        glfw.poll_events();
        for (_, event) in glfw::flush_messages(&events) {
            handle_window_event(&window, event);
        }

        ctx.draw_arrays(0, 3);
    }
}

fn handle_window_event(window: &glfw::Window, event: glfw::WindowEvent) {
    match event {
        glfw::KeyEvent(glfw::KeyEscape, _, glfw::Press, _) => {
            window.set_should_close(true)
        }
        _ => {}
    }
}
<|endoftext|>


<commit_before>use std::env;

use cargo::ops;
use cargo::util::{CliResult, CliError, Config};

#[derive(RustcDecodable)]
struct Options {
    flag_verbose: bool,
    flag_quiet: bool,
    flag_color: Option<String>,
    flag_bin: bool,
    arg_path: String,
    flag_name: Option<String>,
    flag_vcs: Option<ops::VersionControl>,
}

pub const USAGE: &'static str = "
Create a new cargo package at <path>

Usage:
    cargo new [options] <path>
    cargo new -h | --help

Options:
    -h, --help          Print this message
    --vcs <vcs>         Initialize a new repository for the given version
                        control system (git or hg) or do not initialize any version
                        control at all (none) overriding a global configuration.
    --bin               Use a binary instead of a library template
    --name <name>       Set the resulting package name
    -v, --verbose       Use verbose output
    -q, --quiet         No output printed to stdout
    --color WHEN        Coloring: auto, always, never
";

pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
    debug!("executing; cmd=cargo-new; args={:?}", env::args().collect::<Vec<_>>());
    try!(config.shell().set_verbosity(options.flag_verbose, options.flag_quiet));
    try!(config.shell().set_color_config(options.flag_color.as_ref().map(|s| &s[..])));

    let Options { flag_bin, arg_path, flag_name, flag_vcs, .. } = options;

    let opts = ops::NewOptions {
        version_control: flag_vcs,
        bin: flag_bin,
        path: &arg_path,
        name: flag_name.as_ref().map(|s| s.as_ref()),
    };

    ops::new(opts, config).map(|_| None).map_err(|err| {
        CliError::from_boxed(err, 101)
    })
}

<commit_msg>Auto merge of #1920 - Turbo87:uppercase-docopts, r=alexcrichton<commit_after>use std::env;

use cargo::ops;
use cargo::util::{CliResult, CliError, Config};

#[derive(RustcDecodable)]
struct Options {
    flag_verbose: bool,
    flag_quiet: bool,
    flag_color: Option<String>,
    flag_bin: bool,
    arg_path: String,
    flag_name: Option<String>,
    flag_vcs: Option<ops::VersionControl>,
}

pub const USAGE: &'static str = "
Create a new cargo package at <path>

Usage:
    cargo new [options] <path>
    cargo new -h | --help

Options:
    -h, --help          Print this message
    --vcs VCS           Initialize a new repository for the given version
                        control system (git or hg) or do not initialize any version
                        control at all (none) overriding a global configuration.
    --bin               Use a binary instead of a library template
    --name NAME         Set the resulting package name
    -v, --verbose       Use verbose output
    -q, --quiet         No output printed to stdout
    --color WHEN        Coloring: auto, always, never
";

pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
    debug!("executing; cmd=cargo-new; args={:?}", env::args().collect::<Vec<_>>());
    try!(config.shell().set_verbosity(options.flag_verbose, options.flag_quiet));
    try!(config.shell().set_color_config(options.flag_color.as_ref().map(|s| &s[..])));

    let Options { flag_bin, arg_path, flag_name, flag_vcs, .. } = options;

    let opts = ops::NewOptions {
        version_control: flag_vcs,
        bin: flag_bin,
        path: &arg_path,
        name: flag_name.as_ref().map(|s| s.as_ref()),
    };

    ops::new(opts, config).map(|_| None).map_err(|err| {
        CliError::from_boxed(err, 101)
    })
}

<|endoftext|>


<commit_before>use std::mem::size_of_val;

struct Point {
    x: f64,
    y: f64,
}

struct Rectangle {
    p1: Point,
    p2: Point,
}

fn origin() -> Point {
    Point { x: 0.0, y: 0.0 }
}

fn boxed_origin() -> ~Point {
    // heap allocate this point, and return a pointer to it
    ~Point { x: 0.0, y: 0.0 }
}

fn main() {
    // stack allocated variables
    let point = origin();
    let rectangle = Rectangle {
        p1: origin(),
        p2: Point { x: 3.0, y: 4.0 }
    };

    // heap allocated rectangle
    let boxed_rectangle = ~Rectangle { p1: origin(), p2: origin() };

    // function output can be boxed
    let boxed_point = ~origin();

    // double indirection
    let box_in_a_box: ~~Point = ~boxed_origin();

    println!("Point occupies {} bytes in the stack",
             size_of_val(&point));
    println!("Rectangle occupies {} bytes in the stack",
             size_of_val(&rectangle));

    // box size = pointer size
    println!("Boxed point occupies {} bytes in the stack",
             size_of_val(&boxed_point));
    println!("Boxed rectangle occupies {} bytes in the stack",
             size_of_val(&boxed_rectangle));

    // bring boxed point data back into the stack (i.e. unbox)
    let unboxed_point = *boxed_point;
    println!("Unboxed point occupies {} bytes in the stack",
             size_of_val(&unboxed_point));
}
<commit_msg>box: Briefly explain references. Closes #11<commit_after>use std::mem::size_of_val;

struct Point {
    x: f64,
    y: f64,
}

struct Rectangle {
    p1: Point,
    p2: Point,
}

fn origin() -> Point {
    Point { x: 0.0, y: 0.0 }
}

fn boxed_origin() -> ~Point {
    // heap allocate this point, and return a pointer to it
    ~Point { x: 0.0, y: 0.0 }
}

fn main() {
    // stack allocated variables
    let point = origin();
    let rectangle = Rectangle {
        p1: origin(),
        p2: Point { x: 3.0, y: 4.0 }
    };

    // heap allocated rectangle
    let boxed_rectangle = ~Rectangle { p1: origin(), p2: origin() };

    // function output can be boxed
    let boxed_point = ~origin();

    // double indirection
    let box_in_a_box: ~~Point = ~boxed_origin();

    println!("Point occupies {} bytes in the stack",
             size_of_val(&point));
    // The `&` means that we are passing to the function, a reference to
    // the object rather than a copy of it. In Rust, this is known as
    // *borrowing* and will be covered in detail in its own section.
    println!("Rectangle occupies {} bytes in the stack",
             size_of_val(&rectangle));

    // box size = pointer size
    println!("Boxed point occupies {} bytes in the stack",
             size_of_val(&boxed_point));
    println!("Boxed rectangle occupies {} bytes in the stack",
             size_of_val(&boxed_rectangle));

    // bring boxed point data back into the stack (i.e. unbox)
    let unboxed_point = *boxed_point;
    println!("Unboxed point occupies {} bytes in the stack",
             size_of_val(&unboxed_point));
}
<|endoftext|>


<commit_before>//! Functions for interacting with browser processes.

use std::process::{Command, Child, Stdio};
use std::io::Result;

use url::Url;

/// Opens a browser window at the specified URL in a new process.
///
/// Returns an `io::Result` containing the child process.
///
/// This function uses platform-specific utilities to determine the user's default browser. The
/// following platforms are supported:
///
/// | Platform | Program    |
/// | -------- | ---------- |
/// | Linux    | `xdg-open` |
/// | OS X     | `open -g   |
/// | Windows  | `start`    |
///
/// # Panics
/// Panics if called on an unsupported operating system.
pub fn open(url: &str) -> Result<Child> {
    let (browser, args) = if cfg!(target_os = "linux") {
        ("xdg-open", vec![])
    } else if cfg!(target_os = "macos") {
        ("open", vec!["-g"])
    } else if cfg!(target_os = "windows") {
        // `start` requires an empty string as its first parameter.
        ("start", vec![""])
    } else {
        panic!("unsupported OS")
    };
    open_specific(url, &browser, &args)
}

/// Opens a specified browser in a new process.
///
/// The browser will be called with any supplied arguments in addition to the URL as an additional
/// argument.
///
/// Returns an `io::Result` containing the child process.
pub fn open_specific(url: &str, browser: &str, browser_args: &[&str]) -> Result<Child> {
    let url = Url::parse(url).unwrap();
    debug!("starting process '{:?}' with url {:?}", browser, url);

    Command::new(browser)
        .args(browser_args)
        .arg(url.to_string())
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn()
}
<commit_msg>use explorer to open URLs on windows<commit_after>//! Functions for interacting with browser processes.

use std::process::{Command, Child, Stdio};
use std::io::Result;

use url::Url;

/// Opens a browser window at the specified URL in a new process.
///
/// Returns an `io::Result` containing the child process.
///
/// This function uses platform-specific utilities to determine the user's default browser. The
/// following platforms are supported:
///
/// | Platform | Program    |
/// | -------- | ---------- |
/// | Linux    | `xdg-open` |
/// | OS X     | `open -g   |
/// | Windows  | `explorer`    |
///
/// # Panics
/// Panics if called on an unsupported operating system.
pub fn open(url: &str) -> Result<Child> {
    let (browser, args) = if cfg!(target_os = "linux") {
        ("xdg-open", vec![])
    } else if cfg!(target_os = "macos") {
        ("open", vec!["-g"])
    } else if cfg!(target_os = "windows") {
        ("explorer", vec![])
    } else {
        panic!("unsupported OS")
    };
    open_specific(url, &browser, &args)
}

/// Opens a specified browser in a new process.
///
/// The browser will be called with any supplied arguments in addition to the URL as an additional
/// argument.
///
/// Returns an `io::Result` containing the child process.
pub fn open_specific(url: &str, browser: &str, browser_args: &[&str]) -> Result<Child> {
    let url = Url::parse(url).unwrap();
    debug!("starting process '{:?}' with url {:?}", browser, url);

    Command::new(browser)
        .args(browser_args)
        .arg(url.to_string())
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn()
}
<|endoftext|>