<commit_before>use ffi;
use libc;

use std::ffi::CString;
use std::ffi::IntoBytes;
use std::io::Cursor;
use std::io::Read;
use std::io::Error as IoError;
use std::mem;
use std::ptr;

use AsMutLua;

use LuaRead;
use LuaError;
use PushGuard;

///
pub struct LuaFunction<L> {
    variable: L
}

struct ReadData {
    reader: Box<Read + 'static>,
    buffer: [u8 ; 128],
    triggered_error: Option<IoError>,
}

extern fn reader(_: *mut ffi::lua_State, data_raw: *mut libc::c_void, size: *mut libc::size_t) -> *const libc::c_char {
    let data: &mut ReadData = unsafe { mem::transmute(data_raw) };

    if data.triggered_error.is_some() {
        unsafe { (*size) = 0 }
        return data.buffer.as_ptr() as *const libc::c_char;
    }

    match data.reader.read(data.buffer.as_mut_slice()) {
        Ok(len) =>
            unsafe { (*size) = len as libc::size_t },
        Err(e) => {
            unsafe { (*size) = 0 }
            data.triggered_error = Some(e)
        },
    };

    data.buffer.as_ptr() as *const libc::c_char
}

impl<L> LuaFunction<L> where L: AsMutLua {
    pub fn call<V>(&mut self) -> Result<V, LuaError>
                   where V: for<'a> LuaRead<PushGuard<&'a mut L>> +
                            for<'a> LuaRead<&'a mut L>
    {
        // calling pcall pops the parameters and pushes output
        let pcall_return_value = unsafe { ffi::lua_pcall(self.variable.as_mut_lua().0, 0, 1, 0) };     // TODO:
        let pushed_value = PushGuard { lua: &mut self.variable, size: 1 };

        // if pcall succeeded, returning
        if pcall_return_value == 0 {
            return match LuaRead::lua_read(pushed_value) {
                None => Err(LuaError::WrongType),
                Some(x) => Ok(x)
            };
        }

        // an error occured during execution
        if pcall_return_value == ffi::LUA_ERRMEM {
            panic!("lua_pcall returned LUA_ERRMEM");
        }

        if pcall_return_value == ffi::LUA_ERRRUN {
            let error_msg: String = LuaRead::lua_read(pushed_value).expect("can't find error \
                                                                            message at the top of \
                                                                            the Lua stack");
            return Err(LuaError::ExecutionError(error_msg));
        }

        panic!("Unknown error code returned by lua_pcall: {}", pcall_return_value)
    }

    pub fn load_from_reader<R>(mut lua: L, code: R) -> Result<LuaFunction<PushGuard<L>>, LuaError>
                               where R: Read + 'static
    {
        let readdata = ReadData {
            reader: Box::new(code),
            buffer: unsafe { ::std::mem::uninitialized() },
            triggered_error: None,
        };

        let chunk_name = CString::new("chunk").unwrap();
        let load_return_value = unsafe { ffi::lua_load(lua.as_mut_lua().0, reader, mem::transmute(&readdata), chunk_name.as_ptr(), ptr::null()) };

        if readdata.triggered_error.is_some() {
            let error = readdata.triggered_error.unwrap();
            return Err(LuaError::ReadError(error));
        }

        if load_return_value == 0 {
            return Ok(LuaFunction{
                variable: PushGuard {
                    lua: lua,
                    size: 1
                }
            });
        }

        let error_msg: String = LuaRead::lua_read(&mut lua).expect("can't find error message at the top of the Lua stack");
        unsafe { ffi::lua_pop(lua.as_mut_lua().0, 1) };

        if load_return_value == ffi::LUA_ERRMEM {
            panic!("LUA_ERRMEM");
        }
        if load_return_value == ffi::LUA_ERRSYNTAX {
            return Err(LuaError::SyntaxError(error_msg));
        }

        panic!("Unknown error while calling lua_load");
    }

    pub fn load(lua: L, code: &str) -> Result<LuaFunction<PushGuard<L>>, LuaError> {
        let code = code.into_bytes();
        let reader = Cursor::new(code);
        LuaFunction::load_from_reader(lua, reader)
    }
}

// TODO: return Result<Ret, ExecutionError> instead
/*impl<'a, 'lua, Ret: CopyRead> ::std::ops::FnMut<(), Ret> for LuaFunction<'a,'lua> {
    fn call_mut(&mut self, _: ()) -> Ret {
        self.call().unwrap()
    }
}*/

impl<L> LuaRead<L> for LuaFunction<L> where L: AsMutLua {
    fn lua_read_at_position(mut lua: L, index: i32)
                            -> Option<LuaFunction<L>>
    {
        assert!(index == -1);   // FIXME:
        if unsafe { ffi::lua_isfunction(lua.as_mut_lua().0, -1) } {
            Some(LuaFunction { variable: lua })
        } else {
            None
        }
    }
}
<commit_msg>Cleaner implementation for function::call<commit_after>use ffi;
use libc;

use std::ffi::CString;
use std::ffi::IntoBytes;
use std::io::Cursor;
use std::io::Read;
use std::io::Error as IoError;
use std::mem;
use std::ptr;

use AsMutLua;

use LuaRead;
use LuaError;
use PushGuard;

///
pub struct LuaFunction<L> {
    variable: L
}

struct ReadData {
    reader: Box<Read + 'static>,
    buffer: [u8 ; 128],
    triggered_error: Option<IoError>,
}

extern fn reader(_: *mut ffi::lua_State, data_raw: *mut libc::c_void, size: *mut libc::size_t) -> *const libc::c_char {
    let data: &mut ReadData = unsafe { mem::transmute(data_raw) };

    if data.triggered_error.is_some() {
        unsafe { (*size) = 0 }
        return data.buffer.as_ptr() as *const libc::c_char;
    }

    match data.reader.read(data.buffer.as_mut_slice()) {
        Ok(len) =>
            unsafe { (*size) = len as libc::size_t },
        Err(e) => {
            unsafe { (*size) = 0 }
            data.triggered_error = Some(e)
        },
    };

    data.buffer.as_ptr() as *const libc::c_char
}

impl<L> LuaFunction<L> where L: AsMutLua {
    pub fn call<V>(&mut self) -> Result<V, LuaError>
                   where V: for<'a> LuaRead<PushGuard<&'a mut L>> +
                            for<'a> LuaRead<&'a mut L>
    {
        // calling pcall pops the parameters and pushes output
        let (pcall_return_value, pushed_value) = unsafe {
            let pcall_return_value = ffi::lua_pcall(self.variable.as_mut_lua().0, 0, 1, 0);     // TODO:
            (pcall_return_value, PushGuard { lua: &mut self.variable, size: 1 })
        };

        // if pcall succeeded, returning
        if pcall_return_value == 0 {
            return match LuaRead::lua_read(pushed_value) {
                None => Err(LuaError::WrongType),
                Some(x) => Ok(x)
            };
        }

        // an error occured during execution
        if pcall_return_value == ffi::LUA_ERRMEM {
            panic!("lua_pcall returned LUA_ERRMEM");
        }

        if pcall_return_value == ffi::LUA_ERRRUN {
            let error_msg: String = LuaRead::lua_read(pushed_value).expect("can't find error \
                                                                            message at the top of \
                                                                            the Lua stack");
            return Err(LuaError::ExecutionError(error_msg));
        }

        panic!("Unknown error code returned by lua_pcall: {}", pcall_return_value)
    }

    pub fn load_from_reader<R>(mut lua: L, code: R) -> Result<LuaFunction<PushGuard<L>>, LuaError>
                               where R: Read + 'static
    {
        let readdata = ReadData {
            reader: Box::new(code),
            buffer: unsafe { ::std::mem::uninitialized() },
            triggered_error: None,
        };

        let (load_return_value, pushed_value) = unsafe {
            let chunk_name = CString::new("chunk").unwrap();
            let code = ffi::lua_load(lua.as_mut_lua().0, reader, mem::transmute(&readdata),
                                     chunk_name.as_ptr(), ptr::null());
            (code, PushGuard { lua: lua, size: 1 })
        };

        if readdata.triggered_error.is_some() {
            let error = readdata.triggered_error.unwrap();
            return Err(LuaError::ReadError(error));
        }

        if load_return_value == 0 {
            return Ok(LuaFunction{
                variable: pushed_value,
            });
        }

        let error_msg: String = LuaRead::lua_read(pushed_value).expect("can't find error message \
                                                                        at the top of the Lua \
                                                                        stack");

        if load_return_value == ffi::LUA_ERRMEM {
            panic!("LUA_ERRMEM");
        }

        if load_return_value == ffi::LUA_ERRSYNTAX {
            return Err(LuaError::SyntaxError(error_msg));
        }

        panic!("Unknown error while calling lua_load");
    }

    pub fn load(lua: L, code: &str) -> Result<LuaFunction<PushGuard<L>>, LuaError> {
        let code = code.into_bytes();
        let reader = Cursor::new(code);
        LuaFunction::load_from_reader(lua, reader)
    }
}

// TODO: return Result<Ret, ExecutionError> instead
/*impl<'a, 'lua, Ret: CopyRead> ::std::ops::FnMut<(), Ret> for LuaFunction<'a,'lua> {
    fn call_mut(&mut self, _: ()) -> Ret {
        self.call().unwrap()
    }
}*/

impl<L> LuaRead<L> for LuaFunction<L> where L: AsMutLua {
    fn lua_read_at_position(mut lua: L, index: i32)
                            -> Option<LuaFunction<L>>
    {
        assert!(index == -1);   // FIXME:
        if unsafe { ffi::lua_isfunction(lua.as_mut_lua().0, -1) } {
            Some(LuaFunction { variable: lua })
        } else {
            None
        }
    }
}
<|endoftext|>


<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/.

/// Test the functionality of stratis pools.
extern crate devicemapper;
extern crate env_logger;
extern crate uuid;
extern crate nix;
extern crate tempdir;

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

use self::nix::mount::{mount, MsFlags, umount};
use self::tempdir::TempDir;

use self::devicemapper::{Device, DmName, DM, SECTOR_SIZE, Sectors, ThinDev};

use libstratis::engine::{Engine, Pool};
use libstratis::engine::engine::HasUuid;
use libstratis::engine::strat_engine::StratEngine;
use libstratis::engine::strat_engine::device::resolve_devices;
use libstratis::engine::strat_engine::pool::{DATA_BLOCK_SIZE, DATA_LOWATER, INITIAL_DATA_SIZE,
                                             StratPool};
use libstratis::engine::types::{Redundancy, RenameAction};

/// Verify that the physical space allocated to a pool is expanded when
/// the number of sectors written to a thin-dev in the pool exceeds the
/// INITIAL_DATA_SIZE.  If we are able to write more sectors to the filesystem
/// than are initially allocated to the pool, the pool must have been expanded.
pub fn test_thinpool_expand(paths: &[&Path]) -> () {
    let (mut pool, _) = StratPool::initialize("stratis_test_pool",
                                              &DM::new().unwrap(),
                                              paths,
                                              Redundancy::NONE,
                                              true)
            .unwrap();

    let &(_, fs_uuid) = pool.create_filesystems(&[("stratis_test_filesystem", None)])
        .unwrap()
        .first()
        .unwrap();

    let devnode = pool.get_filesystem(&fs_uuid).unwrap().devnode();
    // Braces to ensure f is closed before destroy
    {
        let mut f = OpenOptions::new().write(true).open(devnode).unwrap();
        // Write 1 more sector than is initially allocated to a pool
        let write_size = *INITIAL_DATA_SIZE * DATA_BLOCK_SIZE + Sectors(1);
        let buf = &[1u8; SECTOR_SIZE];
        for i in 0..*write_size {
            f.write_all(buf).unwrap();
            // Simulate handling a DM event by running a pool check at the point where
            // the amount of free space in pool has decreased to the DATA_LOWATER value.
            // TODO: Actually handle DM events and possibly call extend() directly,
            // depending on the specificity of the events.
            if i == *(*(INITIAL_DATA_SIZE - DATA_LOWATER) * DATA_BLOCK_SIZE) {
                pool.check().unwrap();
            }
        }
    }
    pool.destroy_filesystems(&[&fs_uuid]).unwrap();
    pool.teardown().unwrap();
}

/// Verify a snapshot has the same files and same contents as the origin.
pub fn test_filesystem_snapshot(paths: &[&Path]) {
    let dm = DM::new().unwrap();
    let (mut pool, _) =
        StratPool::initialize("stratis_test_pool", &dm, paths, Redundancy::NONE, true).unwrap();
    let &(_, fs_uuid) = pool.create_filesystems(&[("stratis_test_filesystem", None)])
        .unwrap()
        .first()
        .unwrap();
    let write_buf = &[8u8; SECTOR_SIZE];
    let file_count = 10;
    let source_tmp_dir = TempDir::new("stratis_testing").unwrap();
    {
        // to allow mutable borrow of pool
        let filesystem = pool.get_filesystem(&fs_uuid).unwrap();
        mount(Some(&filesystem.devnode()),
              source_tmp_dir.path(),
              Some("xfs"),
              MsFlags::empty(),
              None as Option<&str>)
                .unwrap();
        for i in 0..file_count {
            let file_path = source_tmp_dir
                .path()
                .join(format!("stratis_test{}.txt", i));
            let mut f = OpenOptions::new()
                .create(true)
                .write(true)
                .open(file_path)
                .unwrap();
            f.write_all(write_buf).unwrap();
            f.flush().unwrap();

        }

    }
    // Run a check to expand the pool. The space initially allocated
    // to a pool is close to consumed by the filesystem and few files
    // written above. If we attempt to update the UUID on the snapshot
    // without expanding the pool, the pool will go into out-of-data-space
    // (queue IO) mode, causing the test to fail. Calling pool.check() will
    // compare the data space used by the pool to the data space allocated
    // and expand when there is less than DATA_LOWATER space remaining.
    // TODO: Make use of a way (not as yet existing) to explicitly extend
    // pool to the necessary size
    pool.check().unwrap();

    let snapshot_uuid = pool.snapshot_filesystem(fs_uuid).unwrap();
    let mut read_buf = [0u8; SECTOR_SIZE];
    let snapshot_tmp_dir = TempDir::new("stratis_testing").unwrap();
    {
        let snapshot_filesystem = pool.get_filesystem(&snapshot_uuid).unwrap();
        mount(Some(&snapshot_filesystem.devnode()),
              snapshot_tmp_dir.path(),
              Some("xfs"),
              MsFlags::empty(),
              None as Option<&str>)
                .unwrap();
        for i in 0..file_count {
            let file_path = snapshot_tmp_dir
                .path()
                .join(format!("stratis_test{}.txt", i));
            let mut f = OpenOptions::new().read(true).open(file_path).unwrap();
            f.read(&mut read_buf).unwrap();
            assert!(read_buf[0..SECTOR_SIZE] == write_buf[0..SECTOR_SIZE]);
        }
    }
    umount(source_tmp_dir.path()).unwrap();
    umount(snapshot_tmp_dir.path()).unwrap();
    pool.destroy_filesystems(&[&fs_uuid, &snapshot_uuid])
        .unwrap();
    pool.teardown().unwrap();
}

/// Verify that a filesystem rename causes the filesystem metadata to be
/// updated.
pub fn test_filesystem_rename(paths: &[&Path]) {
    let mut engine = StratEngine::initialize().unwrap();

    let name1 = "name1";
    let name2 = "name2";
    let (uuid1, _) = engine.create_pool(&name1, paths, None, false).unwrap();
    let fs_uuid = {
        let mut pool = engine.get_mut_pool(&uuid1).unwrap();
        let &(fs_name, fs_uuid) = pool.create_filesystems(&[(name1, None)])
            .unwrap()
            .first()
            .unwrap();

        assert_eq!(name1, fs_name);

        let action = pool.rename_filesystem(&fs_uuid, name2).unwrap();
        assert_eq!(action, RenameAction::Renamed);
        fs_uuid
    };
    engine.teardown().unwrap();

    let engine = StratEngine::initialize().unwrap();
    let filesystem_name: String = engine
        .get_pool(&uuid1)
        .unwrap()
        .get_filesystem(&fs_uuid)
        .unwrap()
        .name()
        .into();
    assert_eq!(filesystem_name, name2);

    engine.teardown().unwrap();
}

/// Verify that destroy_filesystems actually deallocates the space
/// from the thinpool, by attempting to reinstantiate it using the
/// same thin id and verifying that it fails.
pub fn test_thinpool_thindev_destroy(paths: &[&Path]) -> () {
    let (mut pool, _) = StratPool::initialize("stratis_test_pool",
                                              &DM::new().unwrap(),
                                              paths,
                                              Redundancy::NONE,
                                              true)
            .unwrap();
    let &(_, fs_uuid) = pool.create_filesystems(&[("stratis_test_filesystem", None)])
        .unwrap()
        .first()
        .unwrap();

    let fs_id = pool.get_mut_strat_filesystem(&fs_uuid)
        .unwrap()
        .thin_id();

    pool.destroy_filesystems(&[&fs_uuid]).unwrap();

    let pool_uuid = *pool.uuid();

    // Try to setup a thindev that has been destroyed
    let dm = DM::new().unwrap();
    let thindev = ThinDev::setup(&dm,
                                 DmName::new("stratis_test_thin_dev").expect("valid format"),
                                 None,
                                 pool.thinpooldev(),
                                 fs_id,
                                 Sectors(128u64));
    assert!(thindev.is_err());
    pool.teardown().unwrap();

    // Check that destroyed fs is not present in MDV. If the record
    // had been left on the MDV that didn't match a thin_id in the
    // thinpool, ::setup() will fail.
    let paths2: HashMap<Device, PathBuf> = resolve_devices(paths)
        .unwrap()
        .into_iter()
        .map(|(d, p)| (d, p.to_owned()))
        .collect();
    let pool = StratPool::setup(pool_uuid, &paths2).unwrap();

    // This also should never happen, given the previous two parts of
    // this test.
    assert!(pool.get_filesystem(&fs_uuid).is_none());
    pool.teardown().unwrap();
}
<commit_msg>removed un-necessary mut from the pool variable in test_filesystem_rename()<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/.

/// Test the functionality of stratis pools.
extern crate devicemapper;
extern crate env_logger;
extern crate uuid;
extern crate nix;
extern crate tempdir;

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

use self::nix::mount::{mount, MsFlags, umount};
use self::tempdir::TempDir;

use self::devicemapper::{Device, DmName, DM, SECTOR_SIZE, Sectors, ThinDev};

use libstratis::engine::{Engine, Pool};
use libstratis::engine::engine::HasUuid;
use libstratis::engine::strat_engine::StratEngine;
use libstratis::engine::strat_engine::device::resolve_devices;
use libstratis::engine::strat_engine::pool::{DATA_BLOCK_SIZE, DATA_LOWATER, INITIAL_DATA_SIZE,
                                             StratPool};
use libstratis::engine::types::{Redundancy, RenameAction};

/// Verify that the physical space allocated to a pool is expanded when
/// the number of sectors written to a thin-dev in the pool exceeds the
/// INITIAL_DATA_SIZE.  If we are able to write more sectors to the filesystem
/// than are initially allocated to the pool, the pool must have been expanded.
pub fn test_thinpool_expand(paths: &[&Path]) -> () {
    let (mut pool, _) = StratPool::initialize("stratis_test_pool",
                                              &DM::new().unwrap(),
                                              paths,
                                              Redundancy::NONE,
                                              true)
            .unwrap();

    let &(_, fs_uuid) = pool.create_filesystems(&[("stratis_test_filesystem", None)])
        .unwrap()
        .first()
        .unwrap();

    let devnode = pool.get_filesystem(&fs_uuid).unwrap().devnode();
    // Braces to ensure f is closed before destroy
    {
        let mut f = OpenOptions::new().write(true).open(devnode).unwrap();
        // Write 1 more sector than is initially allocated to a pool
        let write_size = *INITIAL_DATA_SIZE * DATA_BLOCK_SIZE + Sectors(1);
        let buf = &[1u8; SECTOR_SIZE];
        for i in 0..*write_size {
            f.write_all(buf).unwrap();
            // Simulate handling a DM event by running a pool check at the point where
            // the amount of free space in pool has decreased to the DATA_LOWATER value.
            // TODO: Actually handle DM events and possibly call extend() directly,
            // depending on the specificity of the events.
            if i == *(*(INITIAL_DATA_SIZE - DATA_LOWATER) * DATA_BLOCK_SIZE) {
                pool.check().unwrap();
            }
        }
    }
    pool.destroy_filesystems(&[&fs_uuid]).unwrap();
    pool.teardown().unwrap();
}

/// Verify a snapshot has the same files and same contents as the origin.
pub fn test_filesystem_snapshot(paths: &[&Path]) {
    let dm = DM::new().unwrap();
    let (mut pool, _) =
        StratPool::initialize("stratis_test_pool", &dm, paths, Redundancy::NONE, true).unwrap();
    let &(_, fs_uuid) = pool.create_filesystems(&[("stratis_test_filesystem", None)])
        .unwrap()
        .first()
        .unwrap();
    let write_buf = &[8u8; SECTOR_SIZE];
    let file_count = 10;
    let source_tmp_dir = TempDir::new("stratis_testing").unwrap();
    {
        // to allow mutable borrow of pool
        let filesystem = pool.get_filesystem(&fs_uuid).unwrap();
        mount(Some(&filesystem.devnode()),
              source_tmp_dir.path(),
              Some("xfs"),
              MsFlags::empty(),
              None as Option<&str>)
                .unwrap();
        for i in 0..file_count {
            let file_path = source_tmp_dir
                .path()
                .join(format!("stratis_test{}.txt", i));
            let mut f = OpenOptions::new()
                .create(true)
                .write(true)
                .open(file_path)
                .unwrap();
            f.write_all(write_buf).unwrap();
            f.flush().unwrap();

        }

    }
    // Run a check to expand the pool. The space initially allocated
    // to a pool is close to consumed by the filesystem and few files
    // written above. If we attempt to update the UUID on the snapshot
    // without expanding the pool, the pool will go into out-of-data-space
    // (queue IO) mode, causing the test to fail. Calling pool.check() will
    // compare the data space used by the pool to the data space allocated
    // and expand when there is less than DATA_LOWATER space remaining.
    // TODO: Make use of a way (not as yet existing) to explicitly extend
    // pool to the necessary size
    pool.check().unwrap();

    let snapshot_uuid = pool.snapshot_filesystem(fs_uuid).unwrap();
    let mut read_buf = [0u8; SECTOR_SIZE];
    let snapshot_tmp_dir = TempDir::new("stratis_testing").unwrap();
    {
        let snapshot_filesystem = pool.get_filesystem(&snapshot_uuid).unwrap();
        mount(Some(&snapshot_filesystem.devnode()),
              snapshot_tmp_dir.path(),
              Some("xfs"),
              MsFlags::empty(),
              None as Option<&str>)
                .unwrap();
        for i in 0..file_count {
            let file_path = snapshot_tmp_dir
                .path()
                .join(format!("stratis_test{}.txt", i));
            let mut f = OpenOptions::new().read(true).open(file_path).unwrap();
            f.read(&mut read_buf).unwrap();
            assert!(read_buf[0..SECTOR_SIZE] == write_buf[0..SECTOR_SIZE]);
        }
    }
    umount(source_tmp_dir.path()).unwrap();
    umount(snapshot_tmp_dir.path()).unwrap();
    pool.destroy_filesystems(&[&fs_uuid, &snapshot_uuid])
        .unwrap();
    pool.teardown().unwrap();
}

/// Verify that a filesystem rename causes the filesystem metadata to be
/// updated.
pub fn test_filesystem_rename(paths: &[&Path]) {
    let mut engine = StratEngine::initialize().unwrap();

    let name1 = "name1";
    let name2 = "name2";
    let (uuid1, _) = engine.create_pool(&name1, paths, None, false).unwrap();
    let fs_uuid = {
        let pool = engine.get_mut_pool(&uuid1).unwrap();
        let &(fs_name, fs_uuid) = pool.create_filesystems(&[(name1, None)])
            .unwrap()
            .first()
            .unwrap();

        assert_eq!(name1, fs_name);

        let action = pool.rename_filesystem(&fs_uuid, name2).unwrap();
        assert_eq!(action, RenameAction::Renamed);
        fs_uuid
    };
    engine.teardown().unwrap();

    let engine = StratEngine::initialize().unwrap();
    let filesystem_name: String = engine
        .get_pool(&uuid1)
        .unwrap()
        .get_filesystem(&fs_uuid)
        .unwrap()
        .name()
        .into();
    assert_eq!(filesystem_name, name2);

    engine.teardown().unwrap();
}

/// Verify that destroy_filesystems actually deallocates the space
/// from the thinpool, by attempting to reinstantiate it using the
/// same thin id and verifying that it fails.
pub fn test_thinpool_thindev_destroy(paths: &[&Path]) -> () {
    let (mut pool, _) = StratPool::initialize("stratis_test_pool",
                                              &DM::new().unwrap(),
                                              paths,
                                              Redundancy::NONE,
                                              true)
            .unwrap();
    let &(_, fs_uuid) = pool.create_filesystems(&[("stratis_test_filesystem", None)])
        .unwrap()
        .first()
        .unwrap();

    let fs_id = pool.get_mut_strat_filesystem(&fs_uuid)
        .unwrap()
        .thin_id();

    pool.destroy_filesystems(&[&fs_uuid]).unwrap();

    let pool_uuid = *pool.uuid();

    // Try to setup a thindev that has been destroyed
    let dm = DM::new().unwrap();
    let thindev = ThinDev::setup(&dm,
                                 DmName::new("stratis_test_thin_dev").expect("valid format"),
                                 None,
                                 pool.thinpooldev(),
                                 fs_id,
                                 Sectors(128u64));
    assert!(thindev.is_err());
    pool.teardown().unwrap();

    // Check that destroyed fs is not present in MDV. If the record
    // had been left on the MDV that didn't match a thin_id in the
    // thinpool, ::setup() will fail.
    let paths2: HashMap<Device, PathBuf> = resolve_devices(paths)
        .unwrap()
        .into_iter()
        .map(|(d, p)| (d, p.to_owned()))
        .collect();
    let pool = StratPool::setup(pool_uuid, &paths2).unwrap();

    // This also should never happen, given the previous two parts of
    // this test.
    assert!(pool.get_filesystem(&fs_uuid).is_none());
    pool.teardown().unwrap();
}
<|endoftext|>


<commit_before>pub use std::os::raw::c_ulonglong;
pub use self::types::*;
pub use self::cmds::*;

#[doc(hidden)]
#[allow(dead_code)]
pub fn unloaded_function_panic() -> ! {
    panic!("Attempted to run unloaded vulkan function")
}

macro_rules! handle_nondispatchable {
    ($name: ident) => {
        #[repr(C)]
        #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash)]
        pub struct $name (uint64_t);

        impl fmt::Pointer for $name {
            fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
                write!(f, "0x{:x}", self.0)
            }
        }

        impl fmt::Debug for $name {
            fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
                write!(f, "0x{:x}", self.0)
            }
        }
    }
}

macro_rules! vk_bitflags_wrapped {
    ($name: ident, $all: expr, $flag_type: ty) => {
        #[repr(C)]
        #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
        pub struct $name {flags: $flag_type}

        impl fmt::Debug for $name {
            fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
                write!(f, "{}({:b})", stringify!($name), self.flags)
            }
        }

        impl $name {
            #[inline]
            pub fn empty() -> $name {
                $name {flags: 0}
            }

            #[inline]
            pub fn all() -> $name {
                $name {flags: $all}
            }

            #[inline]
            pub fn flags(self) -> $flag_type {
                self.flags
            }

            #[inline]
            pub fn from_flags(flags: $flag_type) -> Option<$name> {
                if flags & !$all == 0 {
                    Some($name {flags: flags})
                } else {
                    None
                }
            }

            #[inline]
            pub fn from_flags_truncate(flags: $flag_type) -> $name {
                $name {flags: flags & $all}
            }

            #[inline]
            pub fn is_empty(self) -> bool {
                self == $name::empty()
            }

            #[inline]
            pub fn is_all(self) -> bool {
                self & $name::all() == $name::all()
            }

            #[inline]
            pub fn intersects(self, other: $name) -> bool {
                self & other != $name::empty()
            }

            /// Returns true of `other` is a subset of `self`
            #[inline]
            pub fn subset(self, other: $name) -> bool {
                self & other == other
            }
        }

        impl BitOr for $name {
            type Output = $name;

            #[inline]
            fn bitor(self, rhs: $name) -> $name {
                $name {flags: self.flags | rhs.flags }
            }
        }

        impl BitOrAssign for $name {
            #[inline]
            fn bitor_assign(&mut self, rhs: $name) {
                *self = *self | rhs
            }
        }

        impl BitAnd for $name {
            type Output = $name;

            #[inline]
            fn bitand(self, rhs: $name) -> $name {
                $name {flags: self.flags & rhs.flags}
            }
        }

        impl BitAndAssign for $name {
            #[inline]
            fn bitand_assign(&mut self, rhs: $name) {
                *self = *self & rhs
            }
        }

        impl BitXor for $name {
            type Output = $name;

            #[inline]
            fn bitxor(self, rhs: $name) -> $name {
                $name {flags: self.flags ^ rhs.flags}
            }
        }

        impl BitXorAssign for $name {
            #[inline]
            fn bitxor_assign(&mut self, rhs: $name) {
                *self = *self ^ rhs
            }
        }

        impl Sub for $name {
            type Output = $name;

            #[inline]
            fn sub(self, rhs: $name) -> $name {
                self & !rhs
            }
        }

        impl SubAssign for $name {
            #[inline]
            fn sub_assign(&mut self, rhs: $name) {
                *self = *self - rhs
            }
        }

        impl Not for $name {
            type Output = $name;

            #[inline]
            fn not(self) -> $name {
                self ^ $name::all()
            }
        }
    }
}
<commit_msg>Fix warning with bitflag wrapping disabled<commit_after>pub use std::os::raw::c_ulonglong;
pub use self::types::*;
pub use self::cmds::*;

#[doc(hidden)]
#[allow(dead_code)]
pub fn unloaded_function_panic() -> ! {
    panic!("Attempted to run unloaded vulkan function")
}

macro_rules! handle_nondispatchable {
    ($name: ident) => {
        #[repr(C)]
        #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash)]
        pub struct $name (uint64_t);

        impl fmt::Pointer for $name {
            fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
                write!(f, "0x{:x}", self.0)
            }
        }

        impl fmt::Debug for $name {
            fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
                write!(f, "0x{:x}", self.0)
            }
        }
    }
}

#[allow(unused_macros)]
macro_rules! vk_bitflags_wrapped {
    ($name: ident, $all: expr, $flag_type: ty) => {
        #[repr(C)]
        #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
        pub struct $name {flags: $flag_type}

        impl fmt::Debug for $name {
            fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
                write!(f, "{}({:b})", stringify!($name), self.flags)
            }
        }

        impl $name {
            #[inline]
            pub fn empty() -> $name {
                $name {flags: 0}
            }

            #[inline]
            pub fn all() -> $name {
                $name {flags: $all}
            }

            #[inline]
            pub fn flags(self) -> $flag_type {
                self.flags
            }

            #[inline]
            pub fn from_flags(flags: $flag_type) -> Option<$name> {
                if flags & !$all == 0 {
                    Some($name {flags: flags})
                } else {
                    None
                }
            }

            #[inline]
            pub fn from_flags_truncate(flags: $flag_type) -> $name {
                $name {flags: flags & $all}
            }

            #[inline]
            pub fn is_empty(self) -> bool {
                self == $name::empty()
            }

            #[inline]
            pub fn is_all(self) -> bool {
                self & $name::all() == $name::all()
            }

            #[inline]
            pub fn intersects(self, other: $name) -> bool {
                self & other != $name::empty()
            }

            /// Returns true of `other` is a subset of `self`
            #[inline]
            pub fn subset(self, other: $name) -> bool {
                self & other == other
            }
        }

        impl BitOr for $name {
            type Output = $name;

            #[inline]
            fn bitor(self, rhs: $name) -> $name {
                $name {flags: self.flags | rhs.flags }
            }
        }

        impl BitOrAssign for $name {
            #[inline]
            fn bitor_assign(&mut self, rhs: $name) {
                *self = *self | rhs
            }
        }

        impl BitAnd for $name {
            type Output = $name;

            #[inline]
            fn bitand(self, rhs: $name) -> $name {
                $name {flags: self.flags & rhs.flags}
            }
        }

        impl BitAndAssign for $name {
            #[inline]
            fn bitand_assign(&mut self, rhs: $name) {
                *self = *self & rhs
            }
        }

        impl BitXor for $name {
            type Output = $name;

            #[inline]
            fn bitxor(self, rhs: $name) -> $name {
                $name {flags: self.flags ^ rhs.flags}
            }
        }

        impl BitXorAssign for $name {
            #[inline]
            fn bitxor_assign(&mut self, rhs: $name) {
                *self = *self ^ rhs
            }
        }

        impl Sub for $name {
            type Output = $name;

            #[inline]
            fn sub(self, rhs: $name) -> $name {
                self & !rhs
            }
        }

        impl SubAssign for $name {
            #[inline]
            fn sub_assign(&mut self, rhs: $name) {
                *self = *self - rhs
            }
        }

        impl Not for $name {
            type Output = $name;

            #[inline]
            fn not(self) -> $name {
                self ^ $name::all()
            }
        }
    }
}
<|endoftext|>


<commit_before>//! The forwarder module shall act as wrapper for the ffi mangled lib.rs
//! calls, which shall be very thin and spare there, but extensive here
//! to prepare clean calls to adbfflutter.
use crate::{
    common::paths::SearchPath,
    ctrl::{ForwardNetMsg, UiUpdateMsg},
    data::{
        collection::Collection,
        ipc::{
            IFCollectionOutputData,
            IPC::{self, DoneSearching},
        },
    },
    net::subs::peer_representation::peer_to_hash_string,
    shared,
};
use async_std::task;
use crossbeam::{
    channel::{unbounded, Receiver, Sender},
    sync::WaitGroup,
};
use libp2p::core::PeerId;
use serde_json;
use std::{
    sync::{Arc, Mutex},
    thread,
};

lazy_static! {
    /// a static immutable runtime for all network activity
    static ref NET_RUNTIME: Mutex<(Receiver<UiUpdateMsg>,Sender<IPC>)> = Mutex::new(create_net_runtime());
    /// a static mutable data collection
    static ref NET_UI : Mutex<UIList> = Mutex::new(UIList { cnt: Vec::new() });
}

/// just return the number of audio files found for now
pub fn ffi_file_count_good(input_path: Vec<String>) -> u32 {
    // prepare data
    let cleaned_paths = SearchPath::new(&input_path);
    let search_path = Arc::new(Mutex::new(cleaned_paths));

    let (tx, _rx) = unbounded::<UiUpdateMsg>();
    let synced_to_ui_messages = Arc::new(Mutex::new(tx.clone()));
    let has_ui = false;

    // set up data and run search
    let collection_protected = Arc::new(Mutex::new(Collection::new()));
    let output_data_return_handle = shared::collection_search(
        collection_protected,
        search_path,
        synced_to_ui_messages,
        has_ui,
    );

    // scope and block trickery for lifetime and mutability
    output_data_return_handle.nr_found_songs
}

/// Return the json of peer uis which holds a vector of UIListInner
pub async fn ffi_ui_messages_as_json() -> String {
    // todo: unwrap using NET_UI is save unless you
    //       run this function simultaneously many instances, secure this because
    //       at least 2 functions now use net component, and simultaneous usage
    //       is more probable now!

    // get network runtime
    let (net_receiver, _) = &*NET_RUNTIME.lock().unwrap();

    // break loop if a yet noticeable change happens
    loop {
        if let Ok(reaction) = net_receiver.recv() {
            match reaction {
                UiUpdateMsg::CollectionUpdate(_, _) => {}
                UiUpdateMsg::NetUpdate(net_message) => match net_message {
                    ForwardNetMsg::Add(peer) => {
                        let ui_list = &mut NET_UI.lock().unwrap();
                        {
                            ui_list.add_peer(&peer.id);
                            break;
                        }
                    }
                    ForwardNetMsg::Delete(peer_id) => {
                        let ui_list = &mut NET_UI.lock().unwrap();
                        {
                            ui_list.remove_peer(&peer_id);
                            break;
                        }
                    }
                    ForwardNetMsg::Stats(_) => {}
                },
                UiUpdateMsg::PeerSearchFinished(peer_id, data) => {
                    let ui_list = &mut NET_UI.lock().unwrap();
                    {
                        ui_list.add_search_finished(&peer_id, &data);
                        break;
                    }
                }
                UiUpdateMsg::StopUI => unreachable!(),
            }
        }
    }
    let ui_list = &NET_UI.lock().unwrap();
    {
        let json: &UIList = &*ui_list;
        let peers_ui_json = serde_json::to_string(&json.cnt).unwrap();
        peers_ui_json
    }
}

/// Send ipc found files (yet only 2 of 4 values)
pub async fn ffi_send_ipc_search_done(nr_searched_files: u32, nr_found_songs: u32) -> bool {
    //
    // get network runtime
    let (_, ipc_sender) = &mut *NET_RUNTIME.lock().unwrap();
    let sending = IFCollectionOutputData {
        nr_searched_files,
        nr_found_songs,
        size_of_data_in_kb: 0,
        nr_internal_duplicates: 0,
    };
    ipc_sender.send(DoneSearching(sending)).is_ok()
}

// ------------------------------------------------------------------------------------------

/// Opens a net thread and return ui message receiver and ipc message sender
/// to be used as static instance for Dart-to-backend communication.
fn create_net_runtime() -> (Receiver<UiUpdateMsg>, Sender<IPC>) {
    // outgoing crossbeam receiver
    let (ui_sender, reactor) = unbounded::<UiUpdateMsg>();
    let (ipc_sender, ipc_receive) = unbounded::<IPC>();
    thread::Builder::new()
        .name("app_net".into())
        .spawn(move || {
            task::block_on(async move {
                // mock input parameters
                let dummy_wait_net_thread = WaitGroup::new();

                match shared::net_search(dummy_wait_net_thread, Some(ui_sender), ipc_receive).await
                {
                    Ok(_) => {}
                    Err(e) => error!("network error: {}", e),
                }
            });
        })
        .unwrap();
    (reactor, ipc_sender)
}

/// Struct viewable for ffi inner part and used as json export
/// !!! change Dart names as well, cause variable names MUST be in sync
#[derive(Serialize, Deserialize, Clone)]
#[allow(non_camel_case_types)]
struct UIListInner {
    // normally peer would be the hash key, but since we use it as json container
    // it's a bit itchy
    peerid: String,
    finished: i32,
    searched: u32,
}
/// Container and helper to fill UIListInner easier
struct UIList {
    pub cnt: Vec<UIListInner>,
}
impl UIList {
    fn add_peer(&mut self, peer_id: &PeerId) {
        let peer_string = peer_to_hash_string(peer_id);
        if self.cnt.iter().find(|e| e.peerid == peer_string).is_none() {
            self.cnt.push(UIListInner {
                peerid: peer_string,
                finished: -1, // -1 on dart side should show that it is not yet completed
                searched: 0,
            });
        }
    }
    fn remove_peer(&mut self, peer_id: &PeerId) {
        //todo: as soon as this is not experimental:
        //      self.cnt.drain_filter(|e| e.peer_id == peer_to_hash(peer_id));
        let peer_string = peer_to_hash_string(peer_id);
        let mut i = 0;
        while i != self.cnt.len() {
            if self.cnt[i].peerid == peer_string {
                self.cnt.remove(i);
                break;
            } else {
                i += 1;
            }
        }
    }
    fn add_search_finished(&mut self, peer_id: &PeerId, data: &IFCollectionOutputData) {
        let peer_string = peer_to_hash_string(peer_id);
        let mut i = 0;
        while i != self.cnt.len() {
            if self.cnt[i].peerid == peer_string {
                self.cnt[i].finished = data.nr_found_songs as i32;
                self.cnt[i].searched = data.nr_searched_files;
                break;
            } else {
                i += 1;
            }
        }
    }
}
<commit_msg>[documentation] add todo for better interface<commit_after>//! The forwarder module shall act as wrapper for the ffi mangled lib.rs
//! calls, which shall be very thin and spare there, but extensive here
//! to prepare clean calls to adbfflutter.
use crate::{
    common::paths::SearchPath,
    ctrl::{ForwardNetMsg, UiUpdateMsg},
    data::{
        collection::Collection,
        ipc::{
            IFCollectionOutputData,
            IPC::{self, DoneSearching},
        },
    },
    net::subs::peer_representation::peer_to_hash_string,
    shared,
};
use async_std::task;
use crossbeam::{
    channel::{unbounded, Receiver, Sender},
    sync::WaitGroup,
};
use libp2p::core::PeerId;
use serde_json;
use std::{
    sync::{Arc, Mutex},
    thread,
};

lazy_static! {
    /// a static immutable runtime for all network activity
    static ref NET_RUNTIME: Mutex<(Receiver<UiUpdateMsg>,Sender<IPC>)> = Mutex::new(create_net_runtime());
    /// a static mutable data collection
    static ref NET_UI : Mutex<UIList> = Mutex::new(UIList { cnt: Vec::new() });
}

/// Return the number of audio files found for now
pub fn ffi_file_count_good(input_path: Vec<String>) -> u32 {
    // todo: return other values of IFInternalCollectionOutputData

    // prepare data
    let cleaned_paths = SearchPath::new(&input_path);
    let search_path = Arc::new(Mutex::new(cleaned_paths));

    let (tx, _rx) = unbounded::<UiUpdateMsg>();
    let synced_to_ui_messages = Arc::new(Mutex::new(tx.clone()));
    let has_ui = false;

    // set up data and run search
    let collection_protected = Arc::new(Mutex::new(Collection::new()));
    let output_data_return_handle = shared::collection_search(
        collection_protected,
        search_path,
        synced_to_ui_messages,
        has_ui,
    );

    // scope and block trickery for lifetime and mutability
    output_data_return_handle.nr_found_songs
}

/// Return the json of peer uis which holds a vector of UIListInner
pub async fn ffi_ui_messages_as_json() -> String {
    // todo: unwrap using NET_UI is save unless you
    //       run this function simultaneously many instances, secure this because
    //       at least 2 functions now use net component, and simultaneous usage
    //       is more probable now!

    // get network runtime
    let (net_receiver, _) = &*NET_RUNTIME.lock().unwrap();

    // break loop if a yet noticeable change happens
    loop {
        if let Ok(reaction) = net_receiver.recv() {
            match reaction {
                UiUpdateMsg::CollectionUpdate(_, _) => {}
                UiUpdateMsg::NetUpdate(net_message) => match net_message {
                    ForwardNetMsg::Add(peer) => {
                        let ui_list = &mut NET_UI.lock().unwrap();
                        {
                            ui_list.add_peer(&peer.id);
                            break;
                        }
                    }
                    ForwardNetMsg::Delete(peer_id) => {
                        let ui_list = &mut NET_UI.lock().unwrap();
                        {
                            ui_list.remove_peer(&peer_id);
                            break;
                        }
                    }
                    ForwardNetMsg::Stats(_) => {}
                },
                UiUpdateMsg::PeerSearchFinished(peer_id, data) => {
                    let ui_list = &mut NET_UI.lock().unwrap();
                    {
                        ui_list.add_search_finished(&peer_id, &data);
                        break;
                    }
                }
                UiUpdateMsg::StopUI => unreachable!(),
            }
        }
    }
    let ui_list = &NET_UI.lock().unwrap();
    {
        let json: &UIList = &*ui_list;
        let peers_ui_json = serde_json::to_string(&json.cnt).unwrap();
        peers_ui_json
    }
}

/// Send ipc found files (yet only 2 of 4 values)
pub async fn ffi_send_ipc_search_done(nr_searched_files: u32, nr_found_songs: u32) -> bool {
    //
    // get network runtime
    let (_, ipc_sender) = &mut *NET_RUNTIME.lock().unwrap();
    let sending = IFCollectionOutputData {
        nr_searched_files,
        nr_found_songs,
        size_of_data_in_kb: 0,
        nr_internal_duplicates: 0,
    };
    ipc_sender.send(DoneSearching(sending)).is_ok()
}

// ------------------------------------------------------------------------------------------

/// Opens a net thread and return ui message receiver and ipc message sender
/// to be used as static instance for Dart-to-backend communication.
fn create_net_runtime() -> (Receiver<UiUpdateMsg>, Sender<IPC>) {
    // outgoing crossbeam receiver
    let (ui_sender, reactor) = unbounded::<UiUpdateMsg>();
    let (ipc_sender, ipc_receive) = unbounded::<IPC>();
    thread::Builder::new()
        .name("app_net".into())
        .spawn(move || {
            task::block_on(async move {
                // mock input parameters
                let dummy_wait_net_thread = WaitGroup::new();

                match shared::net_search(dummy_wait_net_thread, Some(ui_sender), ipc_receive).await
                {
                    Ok(_) => {}
                    Err(e) => error!("network error: {}", e),
                }
            });
        })
        .unwrap();
    (reactor, ipc_sender)
}

/// Struct viewable for ffi inner part and used as json export
/// !!! change Dart names as well, cause variable names MUST be in sync
#[derive(Serialize, Deserialize, Clone)]
#[allow(non_camel_case_types)]
struct UIListInner {
    // normally peer would be the hash key, but since we use it as json container
    // it's a bit itchy
    peerid: String,
    finished: i32,
    searched: u32,
}
/// Container and helper to fill UIListInner easier
struct UIList {
    pub cnt: Vec<UIListInner>,
}
impl UIList {
    fn add_peer(&mut self, peer_id: &PeerId) {
        let peer_string = peer_to_hash_string(peer_id);
        if self.cnt.iter().find(|e| e.peerid == peer_string).is_none() {
            self.cnt.push(UIListInner {
                peerid: peer_string,
                finished: -1, // -1 on dart side should show that it is not yet completed
                searched: 0,
            });
        }
    }
    fn remove_peer(&mut self, peer_id: &PeerId) {
        //todo: as soon as this is not experimental:
        //      self.cnt.drain_filter(|e| e.peer_id == peer_to_hash(peer_id));
        let peer_string = peer_to_hash_string(peer_id);
        let mut i = 0;
        while i != self.cnt.len() {
            if self.cnt[i].peerid == peer_string {
                self.cnt.remove(i);
                break;
            } else {
                i += 1;
            }
        }
    }
    fn add_search_finished(&mut self, peer_id: &PeerId, data: &IFCollectionOutputData) {
        let peer_string = peer_to_hash_string(peer_id);
        let mut i = 0;
        while i != self.cnt.len() {
            if self.cnt[i].peerid == peer_string {
                self.cnt[i].finished = data.nr_found_songs as i32;
                self.cnt[i].searched = data.nr_searched_files;
                break;
            } else {
                i += 1;
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::{
    env,
    path::{Path, PathBuf},
};

pub static BACKUP_CONTROL_VALUES: &[&str] = &[
    "simple", "never", "numbered", "t", "existing", "nil", "none", "off",
];

pub static BACKUP_CONTROL_LONG_HELP: &str =
"The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.
The version control method may be selected via the --backup option or through
the VERSION_CONTROL environment variable.  Here are the values:

  none, off       never make backups (even if --backup is given)
  numbered, t     make numbered backups
  existing, nil   numbered if numbered backups exist, simple otherwise
  simple, never   always make simple backups";

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum BackupMode {
    NoBackup,
    SimpleBackup,
    NumberedBackup,
    ExistingBackup,
}

pub fn determine_backup_suffix(supplied_suffix: Option<&str>) -> String {
    if let Some(suffix) = supplied_suffix {
        String::from(suffix)
    } else {
        env::var("SIMPLE_BACKUP_SUFFIX").unwrap_or_else(|_| "~".to_owned())
    }
}

/// # TODO
///
/// This function currently deviates slightly from how the [manual][1] describes
/// that it should work. In particular, the current implementation:
///
/// 1. Doesn't strictly respect the order in which to determine the backup type,
///    which is (in order of precedence)
///     1. Take a valid value to the '--backup' option
///     2. Take the value of the `VERSION_CONTROL` env var
///     3. default to 'existing'
/// 2. Doesn't accept abbreviations to the 'backup_option' parameter
///
/// [1]: https://www.gnu.org/software/coreutils/manual/html_node/Backup-options.html
pub fn determine_backup_mode(backup_opt_exists: bool, backup_opt: Option<&str>) -> BackupMode {
    if backup_opt_exists {
        match backup_opt.map(String::from) {
            // default is existing, see:
            // https://www.gnu.org/software/coreutils/manual/html_node/Backup-options.html
            None => BackupMode::ExistingBackup,
            Some(mode) => match &mode[..] {
                "simple" | "never" => BackupMode::SimpleBackup,
                "numbered" | "t" => BackupMode::NumberedBackup,
                "existing" | "nil" => BackupMode::ExistingBackup,
                "none" | "off" => BackupMode::NoBackup,
                _ => panic!(), // cannot happen as it is managed by clap
            },
        }
    } else {
        BackupMode::NoBackup
    }
}

pub fn get_backup_path(
    backup_mode: BackupMode,
    backup_path: &Path,
    suffix: &str,
) -> Option<PathBuf> {
    match backup_mode {
        BackupMode::NoBackup => None,
        BackupMode::SimpleBackup => Some(simple_backup_path(backup_path, suffix)),
        BackupMode::NumberedBackup => Some(numbered_backup_path(backup_path)),
        BackupMode::ExistingBackup => Some(existing_backup_path(backup_path, suffix)),
    }
}

pub fn simple_backup_path(path: &Path, suffix: &str) -> PathBuf {
    let mut p = path.to_string_lossy().into_owned();
    p.push_str(suffix);
    PathBuf::from(p)
}

pub fn numbered_backup_path(path: &Path) -> PathBuf {
    for i in 1_u64.. {
        let path_str = &format!("{}.~{}~", path.to_string_lossy(), i);
        let path = Path::new(path_str);
        if !path.exists() {
            return path.to_path_buf();
        }
    }
    panic!("cannot create backup")
}

pub fn existing_backup_path(path: &Path, suffix: &str) -> PathBuf {
    let test_path_str = &format!("{}.~1~", path.to_string_lossy());
    let test_path = Path::new(test_path_str);
    if test_path.exists() {
        numbered_backup_path(path)
    } else {
        simple_backup_path(path, suffix)
    }
}
<commit_msg>backup_control: Match abbreviated backup options<commit_after>use std::{
    env,
    path::{Path, PathBuf},
};

pub static BACKUP_CONTROL_VALUES: &[&str] = &[
    "simple", "never", "numbered", "t", "existing", "nil", "none", "off",
];

pub static BACKUP_CONTROL_LONG_HELP: &str =
"The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.
The version control method may be selected via the --backup option or through
the VERSION_CONTROL environment variable.  Here are the values:

  none, off       never make backups (even if --backup is given)
  numbered, t     make numbered backups
  existing, nil   numbered if numbered backups exist, simple otherwise
  simple, never   always make simple backups";

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum BackupMode {
    NoBackup,
    SimpleBackup,
    NumberedBackup,
    ExistingBackup,
}

pub fn determine_backup_suffix(supplied_suffix: Option<&str>) -> String {
    if let Some(suffix) = supplied_suffix {
        String::from(suffix)
    } else {
        env::var("SIMPLE_BACKUP_SUFFIX").unwrap_or_else(|_| "~".to_owned())
    }
}

/// # TODO
///
/// This function currently deviates slightly from how the [manual][1] describes
/// that it should work. In particular, the current implementation:
///
/// 1. Doesn't strictly respect the order in which to determine the backup type,
///    which is (in order of precedence)
///     1. Take a valid value to the '--backup' option
///     2. Take the value of the `VERSION_CONTROL` env var
///     3. default to 'existing'
/// 2. Doesn't accept abbreviations to the 'backup_option' parameter
///
/// [1]: https://www.gnu.org/software/coreutils/manual/html_node/Backup-options.html
pub fn determine_backup_mode(backup_opt_exists: bool, backup_opt: Option<&str>) -> BackupMode {
    if backup_opt_exists {
        match backup_opt.map(String::from) {
            // default is existing, see:
            // https://www.gnu.org/software/coreutils/manual/html_node/Backup-options.html
            None => BackupMode::ExistingBackup,
            Some(mode) => match &mode[..] {
                "simple" | "never" => BackupMode::SimpleBackup,
                "numbered" | "t" => BackupMode::NumberedBackup,
                "existing" | "nil" => BackupMode::ExistingBackup,
                "none" | "off" => BackupMode::NoBackup,
                _ => panic!(), // cannot happen as it is managed by clap
            },
        }
    } else {
        BackupMode::NoBackup
    }
}

/// Match a backup option string to a `BackupMode`.
///
/// The GNU manual specifies that abbreviations to options are valid as long as
/// they aren't ambiguous. This function matches the given `method` argument
/// against all valid backup options (via `starts_with`), and returns a valid
/// [`BackupMode`] if exactly one backup option matches the `method` given.
///
/// `origin` is required in order to format the generated error message
/// properly, when an error occurs.
///
///
/// # Errors
///
/// If `method` is ambiguous (i.e. may resolve to multiple backup modes) or
/// invalid, an error is returned. The error contains the formatted error string
/// which may then be passed to the [`show_usage_error`] macro.
fn match_method(method: &str, origin: &str) -> Result<BackupMode, String> {
    let x = vec!["simple", "never", "numbered", "t",
                 "existing", "nil", "none", "off"];

    let matches: Vec<&&str> = x.iter()
        .filter(|val| val.starts_with(method))
        .collect();
    if matches.len() == 1 {
        match *matches[0] {
            "simple" | "never" => Ok(BackupMode::SimpleBackup),
            "numbered" | "t" => Ok(BackupMode::NumberedBackup),
            "existing" | "nil" => Ok(BackupMode::ExistingBackup),
            "none" | "off" => Ok(BackupMode::NoBackup),
            _ => panic!(),  // cannot happen as we must have exactly one match
                            // from the list above.
        }
    } else {
        let error_type = if matches.len() == 0 { "invalid" } else { "ambiguous" };
        Err(format!(
"{0} argument ‘{1}’ for ‘{2}’
Valid arguments are:
  - ‘none’, ‘off’
  - ‘simple’, ‘never’
  - ‘existing’, ‘nil’
  - ‘numbered’, ‘t’", error_type, method, origin))
    }
}

pub fn get_backup_path(
    backup_mode: BackupMode,
    backup_path: &Path,
    suffix: &str,
) -> Option<PathBuf> {
    match backup_mode {
        BackupMode::NoBackup => None,
        BackupMode::SimpleBackup => Some(simple_backup_path(backup_path, suffix)),
        BackupMode::NumberedBackup => Some(numbered_backup_path(backup_path)),
        BackupMode::ExistingBackup => Some(existing_backup_path(backup_path, suffix)),
    }
}

pub fn simple_backup_path(path: &Path, suffix: &str) -> PathBuf {
    let mut p = path.to_string_lossy().into_owned();
    p.push_str(suffix);
    PathBuf::from(p)
}

pub fn numbered_backup_path(path: &Path) -> PathBuf {
    for i in 1_u64.. {
        let path_str = &format!("{}.~{}~", path.to_string_lossy(), i);
        let path = Path::new(path_str);
        if !path.exists() {
            return path.to_path_buf();
        }
    }
    panic!("cannot create backup")
}

pub fn existing_backup_path(path: &Path, suffix: &str) -> PathBuf {
    let test_path_str = &format!("{}.~1~", path.to_string_lossy());
    let test_path = Path::new(test_path_str);
    if test_path.exists() {
        numbered_backup_path(path)
    } else {
        simple_backup_path(path, suffix)
    }
}
<|endoftext|>


<commit_before>//
// Copyright 2018 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! Wrappers for Oak SDK types to allow their use with std::io.

use crate::{channel_write, OakStatus};
use std::io;

/// Wrapper for a WriteHandle to implement the [`std::io::Write`] trait.
///
/// Methods taking `Write` trait objects require mutable references,
/// so use a distinct type rather than implementing the trait on the
/// base [`WriteHandle`] type.
///
/// [`WriteHandle`]: crate::WriteHandle
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Channel {
    handle: crate::WriteHandle,
}

impl Channel {
    /// Create a new `io::Channel` that uses the given channel handle.
    pub fn new(handle: crate::WriteHandle) -> Self {
        Channel { handle }
    }
}

/// Implement the [`std::io::Write`] trait for `io::Channel`, to allow logging
/// and use of protobuf serialization methods.
impl std::io::Write for Channel {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        let status = channel_write(self.handle, buf, &[]);
        result_from_status(Some(status), buf.len())
    }
    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

/// Map an [`OakStatus`] value to the nearest available [`std::io::Result`].
fn result_from_status<T>(status: Option<OakStatus>, val: T) -> std::io::Result<T> {
    match status {
        Some(OakStatus::OAK_STATUS_UNSPECIFIED) => Err(io::Error::new(
            io::ErrorKind::Other,
            "Unspecified Oak status value",
        )),
        Some(OakStatus::OK) => Ok(val),
        Some(OakStatus::ERR_BAD_HANDLE) => {
            Err(io::Error::new(io::ErrorKind::NotConnected, "Bad handle"))
        }
        Some(OakStatus::ERR_INVALID_ARGS) => Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "Invalid arguments",
        )),
        Some(OakStatus::ERR_CHANNEL_CLOSED) => Err(io::Error::new(
            io::ErrorKind::ConnectionReset,
            "Channel closed",
        )),
        Some(OakStatus::ERR_BUFFER_TOO_SMALL) => Err(io::Error::new(
            io::ErrorKind::UnexpectedEof,
            "Buffer too small",
        )),
        Some(OakStatus::ERR_HANDLE_SPACE_TOO_SMALL) => Err(io::Error::new(
            io::ErrorKind::UnexpectedEof,
            "Handle space too small",
        )),
        Some(OakStatus::ERR_OUT_OF_RANGE) => {
            Err(io::Error::new(io::ErrorKind::NotConnected, "Out of range"))
        }
        Some(OakStatus::ERR_INTERNAL) => {
            Err(io::Error::new(io::ErrorKind::Other, "Internal error"))
        }
        None => Err(io::Error::new(
            io::ErrorKind::Other,
            "Unknown Oak status value",
        )),
    }
}

#[test]
fn test_result_from_status() {
    assert_matches!(result_from_status(Some(OakStatus::OK), 12), Ok(12));
    assert_matches!(result_from_status(None, 12), Err(_));
}
<commit_msg>Use a 2-level match in oak::io result conversion<commit_after>//
// Copyright 2018 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! Wrappers for Oak SDK types to allow their use with std::io.

use crate::{channel_write, OakStatus};
use std::io;

/// Wrapper for a WriteHandle to implement the [`std::io::Write`] trait.
///
/// Methods taking `Write` trait objects require mutable references,
/// so use a distinct type rather than implementing the trait on the
/// base [`WriteHandle`] type.
///
/// [`WriteHandle`]: crate::WriteHandle
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Channel {
    handle: crate::WriteHandle,
}

impl Channel {
    /// Create a new `io::Channel` that uses the given channel handle.
    pub fn new(handle: crate::WriteHandle) -> Self {
        Channel { handle }
    }
}

/// Implement the [`std::io::Write`] trait for `io::Channel`, to allow logging
/// and use of protobuf serialization methods.
impl std::io::Write for Channel {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        let status = channel_write(self.handle, buf, &[]);
        result_from_status(Some(status), buf.len())
    }
    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

/// Map an [`OakStatus`] value to the nearest available [`std::io::Result`].
fn result_from_status<T>(status: Option<OakStatus>, val: T) -> std::io::Result<T> {
    match status {
        Some(status) => match status {
            OakStatus::OAK_STATUS_UNSPECIFIED => Err(io::Error::new(
                io::ErrorKind::Other,
                "Unspecified Oak status value",
            )),
            OakStatus::OK => Ok(val),
            OakStatus::ERR_BAD_HANDLE => {
                Err(io::Error::new(io::ErrorKind::NotConnected, "Bad handle"))
            }
            OakStatus::ERR_INVALID_ARGS => Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "Invalid arguments",
            )),
            OakStatus::ERR_CHANNEL_CLOSED => Err(io::Error::new(
                io::ErrorKind::ConnectionReset,
                "Channel closed",
            )),
            OakStatus::ERR_BUFFER_TOO_SMALL => Err(io::Error::new(
                io::ErrorKind::UnexpectedEof,
                "Buffer too small",
            )),
            OakStatus::ERR_HANDLE_SPACE_TOO_SMALL => Err(io::Error::new(
                io::ErrorKind::UnexpectedEof,
                "Handle space too small",
            )),
            OakStatus::ERR_OUT_OF_RANGE => {
                Err(io::Error::new(io::ErrorKind::NotConnected, "Out of range"))
            }
            OakStatus::ERR_INTERNAL => Err(io::Error::new(io::ErrorKind::Other, "Internal error")),
        },
        None => Err(io::Error::new(
            io::ErrorKind::Other,
            "Unknown Oak status value",
        )),
    }
}

#[test]
fn test_result_from_status() {
    assert_matches!(result_from_status(Some(OakStatus::OK), 12), Ok(12));
    assert_matches!(result_from_status(None, 12), Err(_));
}
<|endoftext|>


<commit_before>use {Point, Result};
use scanifc_sys;
use std::collections::VecDeque;
use std::path::{Path, PathBuf};

// Cribbed from rivlib's examples.
const DEFAULT_WANT: u32 = 1024;
const DEFAULT_SYNC_TO_PPS: bool = true;

/// A stream of 3D points.
///
/// Follows the builder pattern to set the options for the stream.
#[derive(Debug)]
pub struct Stream {
    log: Option<PathBuf>,
    sync_to_pps: bool,
    uri: Uri,
    want: u32,
}

/// An open stream of points, used for reading.
#[derive(Debug)]
pub struct OpenStream {
    buffer: VecDeque<Point>,
    handle: scanifc_sys::point3dstream_handle,
    want: u32,
}

#[derive(Debug, PartialEq)]
enum Uri {
    File(String),
    Rdtp(String),
}

impl Stream {
    /// Creates a new stream for the provided file path.
    ///
    /// # Examples
    ///
    /// ```
    /// use scanifc::point3d::Stream;
    /// let stream = Stream::from_path("data/scan.rxp");
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Stream {
        Stream {
            log: None,
            sync_to_pps: DEFAULT_SYNC_TO_PPS,
            uri: Uri::from_path(path),
            want: DEFAULT_WANT,
        }
    }

    /// Creates a new stream for the provided rdtp uri.
    ///
    /// # Examples
    ///
    /// ```
    /// use scanifc::point3d::Stream;
    /// let stream = Stream::from_rdtp("192.168.0.33/current?type=mon");
    /// ```
    pub fn from_rdtp(rdtp: &str) -> Stream {
        Stream {
            log: None,
            sync_to_pps: DEFAULT_SYNC_TO_PPS,
            uri: Uri::from_rdtp(rdtp),
            want: DEFAULT_WANT,
        }
    }

    /// Sets the `sync_to_pps` field.
    ///
    /// # Examples
    ///
    /// ```
    /// use scanifc::point3d::Stream;
    /// let stream = Stream::from_path("data/scan.rxp").sync_to_pps(false);
    /// ```
    pub fn sync_to_pps(mut self, sync_to_pps: bool) -> Stream {
        self.sync_to_pps = sync_to_pps;
        self
    }

    /// Sets the number of points requested on each read.
    ///
    /// # Examples
    ///
    /// ```
    /// use scanifc::point3d::Stream;
    /// let stream = Stream::from_path("data/scan.rxp").want(1);
    /// ```
    pub fn want(mut self, want: u32) -> Stream {
        self.want = want;
        self
    }

    /// Sets the log filename for this stream.
    ///
    /// If `None`, the stream is opened without logging.
    ///
    /// # Examples
    ///
    /// ```
    /// extern crate tempfile;
    /// # extern crate scanifc;
    /// # fn main() {
    /// use scanifc::point3d::Stream;
    /// use tempfile::NamedTempFile;
    /// let tempfile = NamedTempFile::new().unwrap();
    /// let stream = Stream::from_path("data/scan.rxp").log(Some(tempfile.path()));
    /// # }
    pub fn log<P: AsRef<Path>>(mut self, log: Option<P>) -> Stream {
        self.log = log.map(|path| path.as_ref().to_path_buf());
        self
    }

    /// Opens the stream.
    ///
    /// # Examples
    ///
    /// ```
    /// use scanifc::point3d::Stream;
    /// let stream = Stream::from_path("data/scan.rxp").open().unwrap();
    /// ```
    pub fn open(&self) -> Result<OpenStream> {
        use std::ffi::CString;
        use std::ptr;

        let mut handle: scanifc_sys::point3dstream_handle = ptr::null_mut();
        let uri = CString::new(self.uri.as_str())?;
        scanifc_try!(scanifc_sys::scanifc_point3dstream_open(
            uri.as_ptr(),
            if self.sync_to_pps { 1 } else { 0 },
            &mut handle,
        ));
        Ok(OpenStream {
            buffer: VecDeque::new(),
            handle: handle,
            want: self.want,
        })
    }
}

impl OpenStream {
    /// Consumes this stream and returns a vector of points.
    ///
    /// If this stream is mid-read, the returned points will be only the remaining points in the
    /// stream.
    ///
    /// # Examples
    ///
    /// ```
    /// use scanifc::point3d::Stream;
    /// let stream = Stream::from_path("data/scan.rxp").open().unwrap();
    /// let points = stream.into_points().unwrap();
    /// ```
    pub fn into_points(self) -> Result<Vec<Point>> {
        self.collect()
    }

    fn fill_buffer(&mut self) -> Result<Option<()>> {
        if let Some(points) = self.read()? {
            self.buffer.extend(points.into_iter());
            Ok(Some(()))
        } else {
            Ok(None)
        }
    }

    fn read(&mut self) -> Result<Option<Vec<Point>>> {
        let mut pxyz32 = vec![Default::default(); self.want as usize];
        let mut pattributes = vec![Default::default(); self.want as usize];
        let mut ptime = vec![Default::default(); self.want as usize];
        let mut got = 0;
        let mut end_of_frame = 0;

        scanifc_try!(scanifc_sys::scanifc_point3dstream_read(
            self.handle,
            self.want,
            pxyz32.as_mut_ptr(),
            pattributes.as_mut_ptr(),
            ptime.as_mut_ptr(),
            &mut got,
            &mut end_of_frame,
        ));
        Ok(if got == 0 && end_of_frame == 0 {
            None
        } else {
            Some(
                pxyz32
                    .into_iter()
                    .zip(pattributes.into_iter())
                    .zip(ptime.into_iter())
                    .take(got as usize)
                    .map(|((xyz32, attributes), time)| {
                        Point::from((xyz32, attributes, time))
                    })
                    .collect(),
            )
        })
    }
}

impl Iterator for OpenStream {
    type Item = Result<Point>;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(point) = self.buffer.pop_front() {
            Some(Ok(point))
        } else {
            match self.fill_buffer() {
                Ok(Some(())) => self.next(),
                Ok(None) => None,
                Err(err) => Some(Err(err)),
            }
        }
    }
}

impl Drop for OpenStream {
    fn drop(&mut self) {
        unsafe { scanifc_sys::scanifc_point3dstream_close(self.handle) };
    }
}

impl Uri {
    fn from_path<P: AsRef<Path>>(path: P) -> Uri {
        Uri::File(format!("file:{}", path.as_ref().display()))
    }

    fn from_rdtp(rdtp: &str) -> Uri {
        Uri::Rdtp(format!("rdtp://{}", rdtp))
    }

    fn as_str(&self) -> &str {
        match *self {
            Uri::File(ref s) | Uri::Rdtp(ref s) => s,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::NamedTempFile;

    #[test]
    fn uri_from_path() {
        let uri = Uri::from_path("a/path/to/a/file.rxp");
        assert_eq!(Uri::File("file:a/path/to/a/file.rxp".to_string()), uri);
    }

    #[test]
    fn uri_from_rdtp() {
        let uri = Uri::from_rdtp("192.168.0.33/current?type=mon");
        assert_eq!(
            Uri::Rdtp("rdtp://192.168.0.33/current?type=mon".to_string()),
            uri
        );
    }

    #[test]
    fn stream() {
        let stream = Stream::from_path("data/scan.rxp").open().unwrap();
        assert_eq!(24390, stream.into_points().unwrap().len());
    }

    #[test]
    fn stream_sync_to_pps() {
        let stream = Stream::from_path("data/scan.rxp")
            .sync_to_pps(false)
            .open()
            .unwrap();
        assert_eq!(24390, stream.into_points().unwrap().len());
    }

    #[test]
    fn stream_want() {
        let mut stream = Stream::from_path("data/scan.rxp").want(1).open().unwrap();
        assert_eq!(1, stream.read().unwrap().unwrap().len());
    }

    #[test]
    fn stream_log() {
        let tempfile = NamedTempFile::new().unwrap();
        let stream = Stream::from_path("data/scan.rxp")
            .log(Some(tempfile.path()))
            .open()
            .unwrap();
        assert_eq!(24390, stream.into_points().unwrap().len());
    }
}
<commit_msg>Add logging<commit_after>use {Point, Result};
use scanifc_sys;
use std::collections::VecDeque;
use std::path::{Path, PathBuf};

// Cribbed from rivlib's examples.
const DEFAULT_WANT: u32 = 1024;
const DEFAULT_SYNC_TO_PPS: bool = true;

/// A stream of 3D points.
///
/// Follows the builder pattern to set the options for the stream.
#[derive(Debug)]
pub struct Stream {
    log: Option<PathBuf>,
    sync_to_pps: bool,
    uri: Uri,
    want: u32,
}

/// An open stream of points, used for reading.
#[derive(Debug)]
pub struct OpenStream {
    buffer: VecDeque<Point>,
    handle: scanifc_sys::point3dstream_handle,
    want: u32,
}

#[derive(Debug, PartialEq)]
enum Uri {
    File(String),
    Rdtp(String),
}

impl Stream {
    /// Creates a new stream for the provided file path.
    ///
    /// # Examples
    ///
    /// ```
    /// use scanifc::point3d::Stream;
    /// let stream = Stream::from_path("data/scan.rxp");
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Stream {
        Stream {
            log: None,
            sync_to_pps: DEFAULT_SYNC_TO_PPS,
            uri: Uri::from_path(path),
            want: DEFAULT_WANT,
        }
    }

    /// Creates a new stream for the provided rdtp uri.
    ///
    /// # Examples
    ///
    /// ```
    /// use scanifc::point3d::Stream;
    /// let stream = Stream::from_rdtp("192.168.0.33/current?type=mon");
    /// ```
    pub fn from_rdtp(rdtp: &str) -> Stream {
        Stream {
            log: None,
            sync_to_pps: DEFAULT_SYNC_TO_PPS,
            uri: Uri::from_rdtp(rdtp),
            want: DEFAULT_WANT,
        }
    }

    /// Sets the `sync_to_pps` field.
    ///
    /// # Examples
    ///
    /// ```
    /// use scanifc::point3d::Stream;
    /// let stream = Stream::from_path("data/scan.rxp").sync_to_pps(false);
    /// ```
    pub fn sync_to_pps(mut self, sync_to_pps: bool) -> Stream {
        self.sync_to_pps = sync_to_pps;
        self
    }

    /// Sets the number of points requested on each read.
    ///
    /// # Examples
    ///
    /// ```
    /// use scanifc::point3d::Stream;
    /// let stream = Stream::from_path("data/scan.rxp").want(1);
    /// ```
    pub fn want(mut self, want: u32) -> Stream {
        self.want = want;
        self
    }

    /// Sets the log filename for this stream.
    ///
    /// If `None`, the stream is opened without logging.
    ///
    /// # Examples
    ///
    /// ```
    /// extern crate tempfile;
    /// # extern crate scanifc;
    /// # fn main() {
    /// use scanifc::point3d::Stream;
    /// use tempfile::NamedTempFile;
    /// let tempfile = NamedTempFile::new().unwrap();
    /// let stream = Stream::from_path("data/scan.rxp").log(Some(tempfile.path()));
    /// # }
    pub fn log<P: AsRef<Path>>(mut self, log: Option<P>) -> Stream {
        self.log = log.map(|path| path.as_ref().to_path_buf());
        self
    }

    /// Opens the stream.
    ///
    /// # Examples
    ///
    /// ```
    /// use scanifc::point3d::Stream;
    /// let stream = Stream::from_path("data/scan.rxp").open().unwrap();
    /// ```
    pub fn open(&self) -> Result<OpenStream> {
        use std::ffi::CString;
        use std::ptr;

        let mut handle: scanifc_sys::point3dstream_handle = ptr::null_mut();
        let uri = CString::new(self.uri.as_str())?;
        let sync_to_pps = if self.sync_to_pps { 1 } else { 0 };
        if let Some(path) = self.log.as_ref() {
            let log = CString::new(path.to_string_lossy().into_owned())?;
            scanifc_try!(scanifc_sys::scanifc_point3dstream_open_with_logging(
                uri.as_ptr(),
                sync_to_pps,
                log.as_ptr(),
                &mut handle,
            ));
        } else {
            scanifc_try!(scanifc_sys::scanifc_point3dstream_open(
                uri.as_ptr(),
                sync_to_pps,
                &mut handle,
            ));
        }
        Ok(OpenStream {
            buffer: VecDeque::new(),
            handle: handle,
            want: self.want,
        })
    }
}

impl OpenStream {
    /// Consumes this stream and returns a vector of points.
    ///
    /// If this stream is mid-read, the returned points will be only the remaining points in the
    /// stream.
    ///
    /// # Examples
    ///
    /// ```
    /// use scanifc::point3d::Stream;
    /// let stream = Stream::from_path("data/scan.rxp").open().unwrap();
    /// let points = stream.into_points().unwrap();
    /// ```
    pub fn into_points(self) -> Result<Vec<Point>> {
        self.collect()
    }

    fn fill_buffer(&mut self) -> Result<Option<()>> {
        if let Some(points) = self.read()? {
            self.buffer.extend(points.into_iter());
            Ok(Some(()))
        } else {
            Ok(None)
        }
    }

    fn read(&mut self) -> Result<Option<Vec<Point>>> {
        let mut pxyz32 = vec![Default::default(); self.want as usize];
        let mut pattributes = vec![Default::default(); self.want as usize];
        let mut ptime = vec![Default::default(); self.want as usize];
        let mut got = 0;
        let mut end_of_frame = 0;

        scanifc_try!(scanifc_sys::scanifc_point3dstream_read(
            self.handle,
            self.want,
            pxyz32.as_mut_ptr(),
            pattributes.as_mut_ptr(),
            ptime.as_mut_ptr(),
            &mut got,
            &mut end_of_frame,
        ));
        Ok(if got == 0 && end_of_frame == 0 {
            None
        } else {
            Some(
                pxyz32
                    .into_iter()
                    .zip(pattributes.into_iter())
                    .zip(ptime.into_iter())
                    .take(got as usize)
                    .map(|((xyz32, attributes), time)| {
                        Point::from((xyz32, attributes, time))
                    })
                    .collect(),
            )
        })
    }
}

impl Iterator for OpenStream {
    type Item = Result<Point>;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(point) = self.buffer.pop_front() {
            Some(Ok(point))
        } else {
            match self.fill_buffer() {
                Ok(Some(())) => self.next(),
                Ok(None) => None,
                Err(err) => Some(Err(err)),
            }
        }
    }
}

impl Drop for OpenStream {
    fn drop(&mut self) {
        unsafe { scanifc_sys::scanifc_point3dstream_close(self.handle) };
    }
}

impl Uri {
    fn from_path<P: AsRef<Path>>(path: P) -> Uri {
        Uri::File(format!("file:{}", path.as_ref().display()))
    }

    fn from_rdtp(rdtp: &str) -> Uri {
        Uri::Rdtp(format!("rdtp://{}", rdtp))
    }

    fn as_str(&self) -> &str {
        match *self {
            Uri::File(ref s) | Uri::Rdtp(ref s) => s,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::NamedTempFile;

    #[test]
    fn uri_from_path() {
        let uri = Uri::from_path("a/path/to/a/file.rxp");
        assert_eq!(Uri::File("file:a/path/to/a/file.rxp".to_string()), uri);
    }

    #[test]
    fn uri_from_rdtp() {
        let uri = Uri::from_rdtp("192.168.0.33/current?type=mon");
        assert_eq!(
            Uri::Rdtp("rdtp://192.168.0.33/current?type=mon".to_string()),
            uri
        );
    }

    #[test]
    fn stream() {
        let stream = Stream::from_path("data/scan.rxp").open().unwrap();
        assert_eq!(24390, stream.into_points().unwrap().len());
    }

    #[test]
    fn stream_sync_to_pps() {
        let stream = Stream::from_path("data/scan.rxp")
            .sync_to_pps(false)
            .open()
            .unwrap();
        assert_eq!(24390, stream.into_points().unwrap().len());
    }

    #[test]
    fn stream_want() {
        let mut stream = Stream::from_path("data/scan.rxp").want(1).open().unwrap();
        assert_eq!(1, stream.read().unwrap().unwrap().len());
    }

    #[test]
    fn stream_log() {
        let tempfile = NamedTempFile::new().unwrap();
        let stream = Stream::from_path("data/scan.rxp")
            .log(Some(tempfile.path()))
            .open()
            .unwrap();
        assert_eq!(24390, stream.into_points().unwrap().len());
    }
}
<|endoftext|>


<commit_before>//
// Copyright 2019 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! Macro to derive standard boilerplate code for an Oak Node.

extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;

/// Implements the necessary bindings to make the annotated struct act as a
/// gRPC-processing Oak Node.
///
/// May only be used on struct objects.  Assumes that the default pre-defined
/// port names (`"grpc_in"`) is used to identify the gRPC input channel.
///
/// At most one struct may be annotated with this, as it produces global symbols
/// that would otherwise conflict if implemented multiple times.
///
/// ```rust
/// extern crate oak;
/// extern crate protobuf;
/// use oak::grpc::OakNode;
/// use protobuf::ProtobufEnum;
///
/// #[derive(oak_derive::OakExports)]
/// struct Node;
///
/// impl OakNode for Node {
///     fn new() -> Self { Node }
///     fn invoke(&mut self, method: &str, req: &[u8], writer: oak::grpc::ChannelResponseWriter) { /* ... */ }
/// }
/// ```
#[proc_macro_derive(OakExports)]
pub fn derive_oak_exports(input: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    let name = input.ident;
    match input.data {
        syn::Data::Struct(_) => (),
        _ => panic!("#[derive(OakExports)] is only defined for structs"),
    };

    let expanded = quote! {
        #[no_mangle]
        pub extern "C" fn oak_main(handle: u64) -> i32 {
            // A panic in the Rust module code cannot safely pass through the FFI
            // boundary, so catch any panics here and translate to an error return.
            // https://doc.rust-lang.org/nomicon/ffi.html#ffi-and-panics
            match std::panic::catch_unwind(||{
                let mut node = <#name>::new();
                oak::grpc::event_loop(node, oak::ReadHandle{ handle })
            }) {
                Ok(rc) => rc,
                Err(_) => oak::OakStatus::ERR_INTERNAL.value(),
            }
        }
    };

    TokenStream::from(expanded)
}
<commit_msg>sdk: use unwrap_or() instead of explicit match<commit_after>//
// Copyright 2019 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! Macro to derive standard boilerplate code for an Oak Node.

extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;

/// Implements the necessary bindings to make the annotated struct act as a
/// gRPC-processing Oak Node.
///
/// May only be used on struct objects.  Assumes that the default pre-defined
/// port names (`"grpc_in"`) is used to identify the gRPC input channel.
///
/// At most one struct may be annotated with this, as it produces global symbols
/// that would otherwise conflict if implemented multiple times.
///
/// ```rust
/// extern crate oak;
/// extern crate protobuf;
/// use oak::grpc::OakNode;
/// use protobuf::ProtobufEnum;
///
/// #[derive(oak_derive::OakExports)]
/// struct Node;
///
/// impl OakNode for Node {
///     fn new() -> Self { Node }
///     fn invoke(&mut self, method: &str, req: &[u8], writer: oak::grpc::ChannelResponseWriter) { /* ... */ }
/// }
/// ```
#[proc_macro_derive(OakExports)]
pub fn derive_oak_exports(input: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    let name = input.ident;
    match input.data {
        syn::Data::Struct(_) => (),
        _ => panic!("#[derive(OakExports)] is only defined for structs"),
    };

    let expanded = quote! {
        #[no_mangle]
        pub extern "C" fn oak_main(handle: u64) -> i32 {
            // A panic in the Rust module code cannot safely pass through the FFI
            // boundary, so catch any panics here and translate to an error return.
            // https://doc.rust-lang.org/nomicon/ffi.html#ffi-and-panics
            std::panic::catch_unwind(||{
                let mut node = <#name>::new();
                oak::grpc::event_loop(node, oak::ReadHandle{ handle })
            }).unwrap_or(oak::OakStatus::ERR_INTERNAL.value())
        }
    };

    TokenStream::from(expanded)
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![allow(unstable)]

extern crate regex;
#[plugin] #[no_link] extern crate regex_macros;

use std::collections::{BTreeMap, btree_map};

macro_rules! try_opt {
    ($e: expr) => {  match $e { Some(x) => x, None => return None } }
}

mod intrinsic;
fn main() {
    let s = std::io::stdin().read_to_string().unwrap();
    let defs = regex!(r"(?sm)def\sint_.*?^\}");

    let mut modules = BTreeMap::new();
    for capture in defs.captures_iter(&s[]) {
        let found = capture.at(0).unwrap();
        let intr = match found.parse::<intrinsic::Intrinsic>() {
            None => panic!("failed to parse {}", found),
            Some(intr) => intr
        };

        (match modules.entry(intr.arch) {
            btree_map::Entry::Occupied(o) => o.into_mut(),
            btree_map::Entry::Vacant(v) => v.insert(vec![])
        }).push(intr)
    }

    println!("\
#![feature(simd, simd_ffi, link_llvm_intrinsics)]
#![allow(non_snake_case)]

//! Bindings to (almost) all LLVM intrinsics.
//!
//! Intrinsics are categorised into modules by the architecture on
//! which they are supported (however, see [Platform
//! support][#platform-support] for a caveat), with certain intrinsics
//! available at the top level. These are raw bindings and absolutely
//! zero error checking is performed.
//!
//! # Naming
//!
//! The intrinsics are named entirely for their internal LLVM names,
//! with the `llvm` prefix stripped, `.` replaced by `_` and, if it
//! exists, a platform prefix replaced by being placed in a module of
//! that name. For example, `llvm.adjust.trampoline` becomes
//! `::adjust_trampoline` and `llvm.x86.addcarry.u32` becomes
//! `::x86::addcarry_u32`.
//!
//! # Platform support
//!
//! An intrinsic being available in a certain module (or at the top
//! level) does not guarantee that it is supported on all CPUs of that
//! architecture (resp. all CPUs), e.g. the x86::avx512 are only
//! supported on the very latest x86-64 CPUs, not on older x86
//! processors.
//!
//! Using an intrinsic in a configuration that is not supported will
//! likely cause LLVM assertions or general badness along those lines.

extern crate simdty;
");
    for (module, items) in modules.iter() {
        let (indent, close, strip) = match *module {
            None => {
                println!("extern {{");
                ("    ", "}", "int_".len())
            }
            Some(arch) => {
                println!("\
/// LLVM intrinsics for the {arch} architecture.
pub mod {arch} {{
    extern {{", arch=arch);
                ("        ", "    }\n}", "int_".len() + arch.as_str().len() + 1)
            }
        };

        for intr in items.iter() {
            let params = intr.params.iter()
                .enumerate()
                .map(|(i, ty)| {
                    ty.to_concrete_rust_string()
                        .map(|s| {
                            if s == "..." {
                                s
                            } else {
                                format!("{}: {}", (b'a' + i as u8) as char, s)
                            }
                        })
                })
                .collect::<Option<Vec<_>>>();
            let p = match params {
                Some(p) => p.connect(", "),
                None => continue
            };
            let ret = match &intr.ret[] {
                [] => "()".to_string(),
                [ref ret] => match ret.to_concrete_rust_string() {
                    Some(r) => r,
                    None => continue
                },
                _ => continue
            };
            let link_name = format!("llvm.{}", intr.name["int_".len()..].replace("_", "."));
            let mut docs = format!("The `{}` intrinsic", link_name);
            if let Some(ref name) = intr.gcc_name {
                docs.push_str("; known as `");
                docs.push_str(&name[]);
                docs.push_str("` in GCC");
            }
            docs.push_str(".");

            println!("\
{indent}/// {docs}
{indent}#[link_name = \"{link_name}\"]
{indent}pub fn {fn_name}({params}) -> {ret};",
                     indent = indent,
                     docs = docs,
                     link_name = link_name,
                     fn_name = avoid_keywords(&intr.name[strip..]),
                     params = p,
                     ret = ret);
        }

        println!("{}", close);
    }
}

fn avoid_keywords(s: &str) -> &str {
    match s {
        "in" => "in_",
        _ => s
    }
}
<commit_msg>Fix link syntax.<commit_after>#![feature(plugin)]
#![allow(unstable)]

extern crate regex;
#[plugin] #[no_link] extern crate regex_macros;

use std::collections::{BTreeMap, btree_map};

macro_rules! try_opt {
    ($e: expr) => {  match $e { Some(x) => x, None => return None } }
}

mod intrinsic;
fn main() {
    let s = std::io::stdin().read_to_string().unwrap();
    let defs = regex!(r"(?sm)def\sint_.*?^\}");

    let mut modules = BTreeMap::new();
    for capture in defs.captures_iter(&s[]) {
        let found = capture.at(0).unwrap();
        let intr = match found.parse::<intrinsic::Intrinsic>() {
            None => panic!("failed to parse {}", found),
            Some(intr) => intr
        };

        (match modules.entry(intr.arch) {
            btree_map::Entry::Occupied(o) => o.into_mut(),
            btree_map::Entry::Vacant(v) => v.insert(vec![])
        }).push(intr)
    }

    println!("\
#![feature(simd, simd_ffi, link_llvm_intrinsics)]
#![allow(non_snake_case)]

//! Bindings to (almost) all LLVM intrinsics.
//!
//! Intrinsics are categorised into modules by the architecture on
//! which they are supported (however, see [Platform
//! support](#platform-support) for a caveat), with certain intrinsics
//! available at the top level. These are raw bindings and absolutely
//! zero error checking is performed.
//!
//! # Naming
//!
//! The intrinsics are named entirely for their internal LLVM names,
//! with the `llvm` prefix stripped, `.` replaced by `_` and, if it
//! exists, a platform prefix replaced by being placed in a module of
//! that name. For example, `llvm.adjust.trampoline` becomes
//! `::adjust_trampoline` and `llvm.x86.addcarry.u32` becomes
//! `::x86::addcarry_u32`.
//!
//! # Platform support
//!
//! An intrinsic being available in a certain module (or at the top
//! level) does not guarantee that it is supported on all CPUs of that
//! architecture (resp. all CPUs), e.g. the x86::avx512 are only
//! supported on the very latest x86-64 CPUs, not on older x86
//! processors.
//!
//! Using an intrinsic in a configuration that is not supported will
//! likely cause LLVM assertions or general badness along those lines.

extern crate simdty;
");
    for (module, items) in modules.iter() {
        let (indent, close, strip) = match *module {
            None => {
                println!("extern {{");
                ("    ", "}", "int_".len())
            }
            Some(arch) => {
                println!("\
/// LLVM intrinsics for the {arch} architecture.
pub mod {arch} {{
    extern {{", arch=arch);
                ("        ", "    }\n}", "int_".len() + arch.as_str().len() + 1)
            }
        };

        for intr in items.iter() {
            let params = intr.params.iter()
                .enumerate()
                .map(|(i, ty)| {
                    ty.to_concrete_rust_string()
                        .map(|s| {
                            if s == "..." {
                                s
                            } else {
                                format!("{}: {}", (b'a' + i as u8) as char, s)
                            }
                        })
                })
                .collect::<Option<Vec<_>>>();
            let p = match params {
                Some(p) => p.connect(", "),
                None => continue
            };
            let ret = match &intr.ret[] {
                [] => "()".to_string(),
                [ref ret] => match ret.to_concrete_rust_string() {
                    Some(r) => r,
                    None => continue
                },
                _ => continue
            };
            let link_name = format!("llvm.{}", intr.name["int_".len()..].replace("_", "."));
            let mut docs = format!("The `{}` intrinsic", link_name);
            if let Some(ref name) = intr.gcc_name {
                docs.push_str("; known as `");
                docs.push_str(&name[]);
                docs.push_str("` in GCC");
            }
            docs.push_str(".");

            println!("\
{indent}/// {docs}
{indent}#[link_name = \"{link_name}\"]
{indent}pub fn {fn_name}({params}) -> {ret};",
                     indent = indent,
                     docs = docs,
                     link_name = link_name,
                     fn_name = avoid_keywords(&intr.name[strip..]),
                     params = p,
                     ret = ret);
        }

        println!("{}", close);
    }
}

fn avoid_keywords(s: &str) -> &str {
    match s {
        "in" => "in_",
        _ => s
    }
}
<|endoftext|>


<commit_before>#[test]
fn it_works() {
}
<commit_msg>Start on parser lib<commit_after>#[macro_use]
extern crate nom;<|endoftext|>


<commit_before><commit_msg>tink-proto: remove warning suppression<commit_after><|endoftext|>