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

use std::{
    clone::Clone,
    collections::HashMap,
    path::{Path, PathBuf},
};

use libudev;

use devicemapper::{Device, DmNameBuf};

#[cfg(test)]
use crate::engine::strat_engine::cleanup::teardown_pools;

use crate::{
    engine::{
        engine::Eventable,
        event::get_engine_listener_list,
        shared::create_pool_idempotent_or_err,
        strat_engine::{
            backstore::{find_all, get_metadata, identify_block_device},
            cmd::verify_binaries,
            devlinks,
            dm::{get_dm, get_dm_init},
            names::validate_name,
            pool::{check_metadata, StratPool},
        },
        structures::Table,
        types::{CreateAction, DeleteAction, RenameAction},
        Engine, EngineEvent, Name, Pool, PoolUuid,
    },
    stratis::{ErrorEnum, StratisError, StratisResult},
};

const REQUIRED_DM_MINOR_VERSION: u32 = 37;

/// Setup a pool from constituent devices in the context of some already
/// setup pools. Return an error on anything that prevents the pool
/// being set up.
/// Precondition: every device in devices has already been determined to belong
/// to the pool with pool_uuid.
fn setup_pool(
    pool_uuid: PoolUuid,
    devices: &HashMap<Device, PathBuf>,
    pools: &Table<StratPool>,
) -> StratisResult<(Name, StratPool)> {
    // FIXME: In this method, various errors are assembled from various
    // sources and combined into strings, so that they
    // can be printed as log messages if necessary. Instead, some kind of
    // error-chaining should be used here and if it is necessary
    // to log the error, the log code should be able to reduce the error
    // chain to something that can be sensibly logged.
    let info_string = || {
        let dev_paths = devices
            .values()
            .map(|p| p.to_str().expect("Unix is utf-8"))
            .collect::<Vec<&str>>()
            .join(" ,");
        format!("(pool UUID: {}, devnodes: {})", pool_uuid, dev_paths)
    };

    let (timestamp, metadata) = get_metadata(pool_uuid, devices)?.ok_or_else(|| {
        let err_msg = format!("no metadata found for {}", info_string());
        StratisError::Engine(ErrorEnum::NotFound, err_msg)
    })?;

    if pools.contains_name(&metadata.name) {
        let err_msg = format!(
            "pool with name \"{}\" set up; metadata specifies same name for {}",
            &metadata.name,
            info_string()
        );
        return Err(StratisError::Engine(ErrorEnum::AlreadyExists, err_msg));
    }

    check_metadata(&metadata)
        .or_else(|e| {
            let err_msg = format!(
                "inconsistent metadata for {}: reason: {:?}",
                info_string(),
                e
            );
            Err(StratisError::Engine(ErrorEnum::Error, err_msg))
        })
        .and_then(|_| {
            StratPool::setup(pool_uuid, devices, timestamp, &metadata).or_else(|e| {
                let err_msg = format!(
                    "failed to set up pool for {}: reason: {:?}",
                    info_string(),
                    e
                );
                Err(StratisError::Engine(ErrorEnum::Error, err_msg))
            })
        })
        .and_then(|(pool_name, pool)| {
            devlinks::setup_pool_devlinks(&pool_name, &pool);
            Ok((pool_name, pool))
        })
}

#[derive(Debug)]
pub struct StratEngine {
    pools: Table<StratPool>,

    // Map of stratis devices that have been found but one or more stratis block devices are missing
    // which prevents the associated pools from being setup.
    incomplete_pools: HashMap<PoolUuid, HashMap<Device, PathBuf>>,

    // Maps name of DM devices we are watching to the most recent event number
    // we've handled for each
    watched_dev_last_event_nrs: HashMap<DmNameBuf, u32>,
}

impl StratEngine {
    /// Setup a StratEngine.
    /// 1. Verify the existence of Stratis /dev directory.
    /// 2. Setup all the pools belonging to the engine.
    ///    a. Places any devices which belong to a pool, but are not complete
    ///       in the incomplete pools data structure.
    ///
    /// Returns an error if the kernel doesn't support required DM features.
    /// Returns an error if there was an error reading device nodes.
    /// Returns an error if the binaries on which it depends can not be found.
    pub fn initialize() -> StratisResult<StratEngine> {
        let dm = get_dm_init()?;
        verify_binaries()?;
        let minor_dm_version = dm.version()?.1;
        if minor_dm_version < REQUIRED_DM_MINOR_VERSION {
            let err_msg = format!(
                "Requires DM minor version {} but kernel only supports {}",
                REQUIRED_DM_MINOR_VERSION, minor_dm_version
            );
            return Err(StratisError::Engine(ErrorEnum::Error, err_msg));
        }

        devlinks::setup_dev_path()?;

        let pools = find_all()?;

        let mut table = Table::default();
        let mut incomplete_pools = HashMap::new();
        for (pool_uuid, devices) in pools {
            match setup_pool(pool_uuid, &devices, &table) {
                Ok((pool_name, pool)) => {
                    table.insert(pool_name, pool_uuid, pool);
                }
                Err(err) => {
                    warn!("no pool set up, reason: {:?}", err);
                    incomplete_pools.insert(pool_uuid, devices);
                }
            }
        }

        let engine = StratEngine {
            pools: table,
            incomplete_pools,
            watched_dev_last_event_nrs: HashMap::new(),
        };

        devlinks::cleanup_devlinks(engine.pools().iter());

        Ok(engine)
    }

    /// Teardown Stratis, preparatory to a shutdown.
    #[cfg(test)]
    pub fn teardown(self) -> StratisResult<()> {
        teardown_pools(self.pools)
    }

    /// Given a udev database entry, process the entry.
    ///
    /// If all the devices are present in the pool and the pool isn't already
    /// up and running, it will get setup and the newly created pool and UUID
    /// will be returned.
    ///
    /// Logs a warning if the block devices appears to be a Stratis block
    /// device and no pool is set up.
    fn block_evaluate(&mut self, device: &libudev::Device) -> Option<(PoolUuid, &mut dyn Pool)> {
        identify_block_device(device).and_then(move |(pool_uuid, _, device, dev_node)| {
            if self.pools.contains_uuid(pool_uuid) {
                None
            } else {
                let mut devices = self
                    .incomplete_pools
                    .remove(&pool_uuid)
                    .or_else(|| Some(HashMap::new()))
                    .expect("We just retrieved or created a HashMap");
                devices.insert(device, dev_node);
                match setup_pool(pool_uuid, &devices, &self.pools) {
                    Ok((pool_name, pool)) => {
                        self.pools.insert(pool_name, pool_uuid, pool);
                        Some((
                            pool_uuid,
                            self.get_mut_pool(pool_uuid)
                                .expect("pool was just inserted")
                                .1,
                        ))
                    }
                    Err(err) => {
                        warn!("no pool set up, reason: {:?}", err);
                        self.incomplete_pools.insert(pool_uuid, devices);
                        None
                    }
                }
            }
        })
    }
}

impl Engine for StratEngine {
    fn handle_event(&mut self, event: &libudev::Event) -> Option<(PoolUuid, &mut dyn Pool)> {
        let event_type = event.event_type();
        if event_type == libudev::EventType::Add || event_type == libudev::EventType::Change {
            self.block_evaluate(event.device())
        } else {
            None
        }
    }

    fn create_pool(
        &mut self,
        name: &str,
        blockdev_paths: &[&Path],
        redundancy: Option<u16>,
    ) -> StratisResult<CreateAction<PoolUuid>> {
        let redundancy = calculate_redundancy!(redundancy);

        validate_name(name)?;

        match self.pools.get_by_name(name) {
            Some((_, pool)) => create_pool_idempotent_or_err(pool, name, blockdev_paths),
            None => {
                let (uuid, pool) = StratPool::initialize(name, blockdev_paths, redundancy)?;

                let name = Name::new(name.to_owned());
                devlinks::pool_added(&name);
                self.pools.insert(name, uuid, pool);
                Ok(CreateAction::Created(uuid))
            }
        }
    }

    fn destroy_pool(&mut self, uuid: PoolUuid) -> StratisResult<DeleteAction<PoolUuid>> {
        if let Some((_, pool)) = self.pools.get_by_uuid(uuid) {
            if pool.has_filesystems() {
                return Err(StratisError::Engine(
                    ErrorEnum::Busy,
                    "filesystems remaining on pool".into(),
                ));
            };
        } else {
            return Ok(DeleteAction::Identity);
        }

        let (pool_name, mut pool) = self
            .pools
            .remove_by_uuid(uuid)
            .expect("Must succeed since self.pools.get_by_uuid() returned a value");

        if let Err(err) = pool.destroy() {
            self.pools.insert(pool_name, uuid, pool);
            Err(err)
        } else {
            devlinks::pool_removed(&pool_name);
            Ok(DeleteAction::Deleted(uuid))
        }
    }

    fn rename_pool(
        &mut self,
        uuid: PoolUuid,
        new_name: &str,
    ) -> StratisResult<RenameAction<PoolUuid>> {
        validate_name(new_name)?;
        let old_name = rename_pool_pre_idem!(self; uuid; new_name);

        let (_, mut pool) = self
            .pools
            .remove_by_uuid(uuid)
            .expect("Must succeed since self.pools.get_by_uuid() returned a value");

        let new_name = Name::new(new_name.to_owned());
        if let Err(err) = pool.write_metadata(&new_name) {
            self.pools.insert(old_name, uuid, pool);
            Err(err)
        } else {
            get_engine_listener_list().notify(&EngineEvent::PoolRenamed {
                dbus_path: pool.get_dbus_path(),
                from: &*old_name,
                to: &*new_name,
            });

            self.pools.insert(new_name.clone(), uuid, pool);
            devlinks::pool_renamed(&old_name, &new_name);
            Ok(RenameAction::Renamed(uuid))
        }
    }

    fn get_pool(&self, uuid: PoolUuid) -> Option<(Name, &dyn Pool)> {
        get_pool!(self; uuid)
    }

    fn get_mut_pool(&mut self, uuid: PoolUuid) -> Option<(Name, &mut dyn Pool)> {
        get_mut_pool!(self; uuid)
    }

    fn configure_simulator(&mut self, _denominator: u32) -> StratisResult<()> {
        Ok(()) // we're not the simulator and not configurable, so just say ok
    }

    fn pools(&self) -> Vec<(Name, PoolUuid, &dyn Pool)> {
        self.pools
            .iter()
            .map(|(name, uuid, pool)| (name.clone(), *uuid, pool as &dyn Pool))
            .collect()
    }

    fn pools_mut(&mut self) -> Vec<(Name, PoolUuid, &mut dyn Pool)> {
        self.pools
            .iter_mut()
            .map(|(name, uuid, pool)| (name.clone(), *uuid, pool as &mut dyn Pool))
            .collect()
    }

    fn get_eventable(&self) -> Option<&'static dyn Eventable> {
        Some(get_dm())
    }

    fn evented(&mut self) -> StratisResult<()> {
        let device_list: HashMap<_, _> = get_dm()
            .list_devices()?
            .into_iter()
            .map(|(dm_name, _, event_nr)| {
                (
                    dm_name,
                    event_nr.expect("Supported DM versions always provide a value"),
                )
            })
            .collect();

        for (pool_name, pool_uuid, pool) in &mut self.pools {
            for dm_name in pool.get_eventing_dev_names(*pool_uuid) {
                if device_list.get(&dm_name) > self.watched_dev_last_event_nrs.get(&dm_name) {
                    pool.event_on(*pool_uuid, pool_name, &dm_name)?;
                }
            }
        }

        self.watched_dev_last_event_nrs = device_list;

        Ok(())
    }
}

#[cfg(test)]
mod test {
    use std::fs::remove_dir_all;

    use crate::engine::engine::DEV_PATH;

    use crate::engine::strat_engine::tests::{loopbacked, real};

    use crate::engine::types::EngineAction;

    use super::*;

    /// Verify that a pool rename causes the pool metadata to get the new name.
    fn test_pool_rename(paths: &[&Path]) {
        let mut engine = StratEngine::initialize().unwrap();

        let name1 = "name1";
        let uuid1 = engine
            .create_pool(name1, paths, None)
            .unwrap()
            .changed()
            .unwrap();

        let name2 = "name2";
        let action = engine.rename_pool(uuid1, name2).unwrap();

        assert_eq!(action, RenameAction::Renamed(uuid1));
        engine.teardown().unwrap();

        let engine = StratEngine::initialize().unwrap();
        let pool_name: String = engine.get_pool(uuid1).unwrap().0.to_owned();
        assert_eq!(pool_name, name2);
    }

    #[test]
    pub fn loop_test_pool_rename() {
        loopbacked::test_with_spec(
            &loopbacked::DeviceLimits::Range(1, 3, None),
            test_pool_rename,
        );
    }

    #[test]
    pub fn real_test_pool_rename() {
        real::test_with_spec(
            &real::DeviceLimits::AtLeast(1, None, None),
            test_pool_rename,
        );
    }

    /// Test engine setup.
    /// 1. Create two pools.
    /// 2. Verify that both exist.
    /// 3. Teardown the engine.
    /// 4. Initialize the engine.
    /// 5. Verify that pools can be found again.
    /// 6. Teardown the engine and remove "/stratis".
    /// 7. Initialize the engine one more time.
    /// 8. Verify that both pools are found and that there are no incomplete pools.
    fn test_setup(paths: &[&Path]) {
        assert!(paths.len() > 1);

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

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

        let name1 = "name1";
        let uuid1 = engine
            .create_pool(name1, paths1, None)
            .unwrap()
            .changed()
            .unwrap();

        let name2 = "name2";
        let uuid2 = engine
            .create_pool(name2, paths2, None)
            .unwrap()
            .changed()
            .unwrap();

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();

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

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();
        remove_dir_all(DEV_PATH).unwrap();

        let engine = StratEngine::initialize().unwrap();
        assert_eq!(engine.incomplete_pools, HashMap::new());

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();
    }

    #[test]
    pub fn loop_test_setup() {
        loopbacked::test_with_spec(&loopbacked::DeviceLimits::Range(2, 3, None), test_setup);
    }

    #[test]
    pub fn real_test_setup() {
        real::test_with_spec(&real::DeviceLimits::AtLeast(2, None, None), test_setup);
    }
}
<commit_msg>Remove an unnecessary expect<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/.

use std::{
    clone::Clone,
    collections::HashMap,
    path::{Path, PathBuf},
};

use libudev;

use devicemapper::{Device, DmNameBuf};

#[cfg(test)]
use crate::engine::strat_engine::cleanup::teardown_pools;

use crate::{
    engine::{
        engine::Eventable,
        event::get_engine_listener_list,
        shared::create_pool_idempotent_or_err,
        strat_engine::{
            backstore::{find_all, get_metadata, identify_block_device},
            cmd::verify_binaries,
            devlinks,
            dm::{get_dm, get_dm_init},
            names::validate_name,
            pool::{check_metadata, StratPool},
        },
        structures::Table,
        types::{CreateAction, DeleteAction, RenameAction},
        Engine, EngineEvent, Name, Pool, PoolUuid,
    },
    stratis::{ErrorEnum, StratisError, StratisResult},
};

const REQUIRED_DM_MINOR_VERSION: u32 = 37;

/// Setup a pool from constituent devices in the context of some already
/// setup pools. Return an error on anything that prevents the pool
/// being set up.
/// Precondition: every device in devices has already been determined to belong
/// to the pool with pool_uuid.
fn setup_pool(
    pool_uuid: PoolUuid,
    devices: &HashMap<Device, PathBuf>,
    pools: &Table<StratPool>,
) -> StratisResult<(Name, StratPool)> {
    // FIXME: In this method, various errors are assembled from various
    // sources and combined into strings, so that they
    // can be printed as log messages if necessary. Instead, some kind of
    // error-chaining should be used here and if it is necessary
    // to log the error, the log code should be able to reduce the error
    // chain to something that can be sensibly logged.
    let info_string = || {
        let dev_paths = devices
            .values()
            .map(|p| p.to_str().expect("Unix is utf-8"))
            .collect::<Vec<&str>>()
            .join(" ,");
        format!("(pool UUID: {}, devnodes: {})", pool_uuid, dev_paths)
    };

    let (timestamp, metadata) = get_metadata(pool_uuid, devices)?.ok_or_else(|| {
        let err_msg = format!("no metadata found for {}", info_string());
        StratisError::Engine(ErrorEnum::NotFound, err_msg)
    })?;

    if pools.contains_name(&metadata.name) {
        let err_msg = format!(
            "pool with name \"{}\" set up; metadata specifies same name for {}",
            &metadata.name,
            info_string()
        );
        return Err(StratisError::Engine(ErrorEnum::AlreadyExists, err_msg));
    }

    check_metadata(&metadata)
        .or_else(|e| {
            let err_msg = format!(
                "inconsistent metadata for {}: reason: {:?}",
                info_string(),
                e
            );
            Err(StratisError::Engine(ErrorEnum::Error, err_msg))
        })
        .and_then(|_| {
            StratPool::setup(pool_uuid, devices, timestamp, &metadata).or_else(|e| {
                let err_msg = format!(
                    "failed to set up pool for {}: reason: {:?}",
                    info_string(),
                    e
                );
                Err(StratisError::Engine(ErrorEnum::Error, err_msg))
            })
        })
        .and_then(|(pool_name, pool)| {
            devlinks::setup_pool_devlinks(&pool_name, &pool);
            Ok((pool_name, pool))
        })
}

#[derive(Debug)]
pub struct StratEngine {
    pools: Table<StratPool>,

    // Map of stratis devices that have been found but one or more stratis block devices are missing
    // which prevents the associated pools from being setup.
    incomplete_pools: HashMap<PoolUuid, HashMap<Device, PathBuf>>,

    // Maps name of DM devices we are watching to the most recent event number
    // we've handled for each
    watched_dev_last_event_nrs: HashMap<DmNameBuf, u32>,
}

impl StratEngine {
    /// Setup a StratEngine.
    /// 1. Verify the existence of Stratis /dev directory.
    /// 2. Setup all the pools belonging to the engine.
    ///    a. Places any devices which belong to a pool, but are not complete
    ///       in the incomplete pools data structure.
    ///
    /// Returns an error if the kernel doesn't support required DM features.
    /// Returns an error if there was an error reading device nodes.
    /// Returns an error if the binaries on which it depends can not be found.
    pub fn initialize() -> StratisResult<StratEngine> {
        let dm = get_dm_init()?;
        verify_binaries()?;
        let minor_dm_version = dm.version()?.1;
        if minor_dm_version < REQUIRED_DM_MINOR_VERSION {
            let err_msg = format!(
                "Requires DM minor version {} but kernel only supports {}",
                REQUIRED_DM_MINOR_VERSION, minor_dm_version
            );
            return Err(StratisError::Engine(ErrorEnum::Error, err_msg));
        }

        devlinks::setup_dev_path()?;

        let pools = find_all()?;

        let mut table = Table::default();
        let mut incomplete_pools = HashMap::new();
        for (pool_uuid, devices) in pools {
            match setup_pool(pool_uuid, &devices, &table) {
                Ok((pool_name, pool)) => {
                    table.insert(pool_name, pool_uuid, pool);
                }
                Err(err) => {
                    warn!("no pool set up, reason: {:?}", err);
                    incomplete_pools.insert(pool_uuid, devices);
                }
            }
        }

        let engine = StratEngine {
            pools: table,
            incomplete_pools,
            watched_dev_last_event_nrs: HashMap::new(),
        };

        devlinks::cleanup_devlinks(engine.pools().iter());

        Ok(engine)
    }

    /// Teardown Stratis, preparatory to a shutdown.
    #[cfg(test)]
    pub fn teardown(self) -> StratisResult<()> {
        teardown_pools(self.pools)
    }

    /// Given a udev database entry, process the entry.
    ///
    /// If all the devices are present in the pool and the pool isn't already
    /// up and running, it will get setup and the newly created pool and UUID
    /// will be returned.
    ///
    /// Logs a warning if the block devices appears to be a Stratis block
    /// device and no pool is set up.
    fn block_evaluate(&mut self, device: &libudev::Device) -> Option<(PoolUuid, &mut dyn Pool)> {
        identify_block_device(device).and_then(move |(pool_uuid, _, device, dev_node)| {
            if self.pools.contains_uuid(pool_uuid) {
                None
            } else {
                let mut devices = self
                    .incomplete_pools
                    .remove(&pool_uuid)
                    .unwrap_or_else(HashMap::new);
                devices.insert(device, dev_node);
                match setup_pool(pool_uuid, &devices, &self.pools) {
                    Ok((pool_name, pool)) => {
                        self.pools.insert(pool_name, pool_uuid, pool);
                        Some((
                            pool_uuid,
                            self.get_mut_pool(pool_uuid)
                                .expect("pool was just inserted")
                                .1,
                        ))
                    }
                    Err(err) => {
                        warn!("no pool set up, reason: {:?}", err);
                        self.incomplete_pools.insert(pool_uuid, devices);
                        None
                    }
                }
            }
        })
    }
}

impl Engine for StratEngine {
    fn handle_event(&mut self, event: &libudev::Event) -> Option<(PoolUuid, &mut dyn Pool)> {
        let event_type = event.event_type();
        if event_type == libudev::EventType::Add || event_type == libudev::EventType::Change {
            self.block_evaluate(event.device())
        } else {
            None
        }
    }

    fn create_pool(
        &mut self,
        name: &str,
        blockdev_paths: &[&Path],
        redundancy: Option<u16>,
    ) -> StratisResult<CreateAction<PoolUuid>> {
        let redundancy = calculate_redundancy!(redundancy);

        validate_name(name)?;

        match self.pools.get_by_name(name) {
            Some((_, pool)) => create_pool_idempotent_or_err(pool, name, blockdev_paths),
            None => {
                let (uuid, pool) = StratPool::initialize(name, blockdev_paths, redundancy)?;

                let name = Name::new(name.to_owned());
                devlinks::pool_added(&name);
                self.pools.insert(name, uuid, pool);
                Ok(CreateAction::Created(uuid))
            }
        }
    }

    fn destroy_pool(&mut self, uuid: PoolUuid) -> StratisResult<DeleteAction<PoolUuid>> {
        if let Some((_, pool)) = self.pools.get_by_uuid(uuid) {
            if pool.has_filesystems() {
                return Err(StratisError::Engine(
                    ErrorEnum::Busy,
                    "filesystems remaining on pool".into(),
                ));
            };
        } else {
            return Ok(DeleteAction::Identity);
        }

        let (pool_name, mut pool) = self
            .pools
            .remove_by_uuid(uuid)
            .expect("Must succeed since self.pools.get_by_uuid() returned a value");

        if let Err(err) = pool.destroy() {
            self.pools.insert(pool_name, uuid, pool);
            Err(err)
        } else {
            devlinks::pool_removed(&pool_name);
            Ok(DeleteAction::Deleted(uuid))
        }
    }

    fn rename_pool(
        &mut self,
        uuid: PoolUuid,
        new_name: &str,
    ) -> StratisResult<RenameAction<PoolUuid>> {
        validate_name(new_name)?;
        let old_name = rename_pool_pre_idem!(self; uuid; new_name);

        let (_, mut pool) = self
            .pools
            .remove_by_uuid(uuid)
            .expect("Must succeed since self.pools.get_by_uuid() returned a value");

        let new_name = Name::new(new_name.to_owned());
        if let Err(err) = pool.write_metadata(&new_name) {
            self.pools.insert(old_name, uuid, pool);
            Err(err)
        } else {
            get_engine_listener_list().notify(&EngineEvent::PoolRenamed {
                dbus_path: pool.get_dbus_path(),
                from: &*old_name,
                to: &*new_name,
            });

            self.pools.insert(new_name.clone(), uuid, pool);
            devlinks::pool_renamed(&old_name, &new_name);
            Ok(RenameAction::Renamed(uuid))
        }
    }

    fn get_pool(&self, uuid: PoolUuid) -> Option<(Name, &dyn Pool)> {
        get_pool!(self; uuid)
    }

    fn get_mut_pool(&mut self, uuid: PoolUuid) -> Option<(Name, &mut dyn Pool)> {
        get_mut_pool!(self; uuid)
    }

    fn configure_simulator(&mut self, _denominator: u32) -> StratisResult<()> {
        Ok(()) // we're not the simulator and not configurable, so just say ok
    }

    fn pools(&self) -> Vec<(Name, PoolUuid, &dyn Pool)> {
        self.pools
            .iter()
            .map(|(name, uuid, pool)| (name.clone(), *uuid, pool as &dyn Pool))
            .collect()
    }

    fn pools_mut(&mut self) -> Vec<(Name, PoolUuid, &mut dyn Pool)> {
        self.pools
            .iter_mut()
            .map(|(name, uuid, pool)| (name.clone(), *uuid, pool as &mut dyn Pool))
            .collect()
    }

    fn get_eventable(&self) -> Option<&'static dyn Eventable> {
        Some(get_dm())
    }

    fn evented(&mut self) -> StratisResult<()> {
        let device_list: HashMap<_, _> = get_dm()
            .list_devices()?
            .into_iter()
            .map(|(dm_name, _, event_nr)| {
                (
                    dm_name,
                    event_nr.expect("Supported DM versions always provide a value"),
                )
            })
            .collect();

        for (pool_name, pool_uuid, pool) in &mut self.pools {
            for dm_name in pool.get_eventing_dev_names(*pool_uuid) {
                if device_list.get(&dm_name) > self.watched_dev_last_event_nrs.get(&dm_name) {
                    pool.event_on(*pool_uuid, pool_name, &dm_name)?;
                }
            }
        }

        self.watched_dev_last_event_nrs = device_list;

        Ok(())
    }
}

#[cfg(test)]
mod test {
    use std::fs::remove_dir_all;

    use crate::engine::engine::DEV_PATH;

    use crate::engine::strat_engine::tests::{loopbacked, real};

    use crate::engine::types::EngineAction;

    use super::*;

    /// Verify that a pool rename causes the pool metadata to get the new name.
    fn test_pool_rename(paths: &[&Path]) {
        let mut engine = StratEngine::initialize().unwrap();

        let name1 = "name1";
        let uuid1 = engine
            .create_pool(name1, paths, None)
            .unwrap()
            .changed()
            .unwrap();

        let name2 = "name2";
        let action = engine.rename_pool(uuid1, name2).unwrap();

        assert_eq!(action, RenameAction::Renamed(uuid1));
        engine.teardown().unwrap();

        let engine = StratEngine::initialize().unwrap();
        let pool_name: String = engine.get_pool(uuid1).unwrap().0.to_owned();
        assert_eq!(pool_name, name2);
    }

    #[test]
    pub fn loop_test_pool_rename() {
        loopbacked::test_with_spec(
            &loopbacked::DeviceLimits::Range(1, 3, None),
            test_pool_rename,
        );
    }

    #[test]
    pub fn real_test_pool_rename() {
        real::test_with_spec(
            &real::DeviceLimits::AtLeast(1, None, None),
            test_pool_rename,
        );
    }

    /// Test engine setup.
    /// 1. Create two pools.
    /// 2. Verify that both exist.
    /// 3. Teardown the engine.
    /// 4. Initialize the engine.
    /// 5. Verify that pools can be found again.
    /// 6. Teardown the engine and remove "/stratis".
    /// 7. Initialize the engine one more time.
    /// 8. Verify that both pools are found and that there are no incomplete pools.
    fn test_setup(paths: &[&Path]) {
        assert!(paths.len() > 1);

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

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

        let name1 = "name1";
        let uuid1 = engine
            .create_pool(name1, paths1, None)
            .unwrap()
            .changed()
            .unwrap();

        let name2 = "name2";
        let uuid2 = engine
            .create_pool(name2, paths2, None)
            .unwrap()
            .changed()
            .unwrap();

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();

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

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();
        remove_dir_all(DEV_PATH).unwrap();

        let engine = StratEngine::initialize().unwrap();
        assert_eq!(engine.incomplete_pools, HashMap::new());

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();
    }

    #[test]
    pub fn loop_test_setup() {
        loopbacked::test_with_spec(&loopbacked::DeviceLimits::Range(2, 3, None), test_setup);
    }

    #[test]
    pub fn real_test_setup() {
        real::test_with_spec(&real::DeviceLimits::AtLeast(2, None, None), test_setup);
    }
}
<|endoftext|>


<commit_before>//! Utilities for building with rustdoc.

use crate::core::compiler::context::Context;
use crate::core::compiler::unit::Unit;
use crate::core::compiler::CompileKind;
use crate::sources::CRATES_IO_REGISTRY;
use crate::util::errors::{internal, CargoResult};
use crate::util::ProcessBuilder;
use std::collections::HashMap;
use std::fmt;
use std::hash;
use url::Url;

/// Mode used for `std`.
#[derive(Debug, Hash)]
pub enum RustdocExternMode {
    /// Use a local `file://` URL.
    Local,
    /// Use a remote URL to https://doc.rust-lang.org/ (default).
    Remote,
    /// An arbitrary URL.
    Url(String),
}

impl From<String> for RustdocExternMode {
    fn from(s: String) -> RustdocExternMode {
        match s.as_ref() {
            "local" => RustdocExternMode::Local,
            "remote" => RustdocExternMode::Remote,
            _ => RustdocExternMode::Url(s),
        }
    }
}

impl fmt::Display for RustdocExternMode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            RustdocExternMode::Local => "local".fmt(f),
            RustdocExternMode::Remote => "remote".fmt(f),
            RustdocExternMode::Url(s) => s.fmt(f),
        }
    }
}

impl<'de> serde::de::Deserialize<'de> for RustdocExternMode {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::de::Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        Ok(s.into())
    }
}

#[derive(serde::Deserialize, Debug)]
pub struct RustdocExternMap {
    registries: HashMap<String, String>,
    std: Option<RustdocExternMode>,
}

impl hash::Hash for RustdocExternMap {
    fn hash<H: hash::Hasher>(&self, into: &mut H) {
        self.std.hash(into);
        for (key, value) in &self.registries {
            key.hash(into);
            value.hash(into);
        }
    }
}

pub fn add_root_urls(
    cx: &Context<'_, '_>,
    unit: &Unit,
    rustdoc: &mut ProcessBuilder,
) -> CargoResult<()> {
    let config = cx.bcx.config;
    if !config.cli_unstable().rustdoc_map {
        log::debug!("`doc.extern-map` ignored, requires -Zrustdoc-map flag");
        return Ok(());
    }
    let map = config.doc_extern_map()?;
    if map.registries.is_empty() && map.std.is_none() {
        // Skip doing unnecessary work.
        return Ok(());
    }
    let mut unstable_opts = false;
    // Collect mapping of registry name -> index url.
    let name2url: HashMap<&String, Url> = map
        .registries
        .keys()
        .filter_map(|name| {
            if let Ok(index_url) = config.get_registry_index(name) {
                Some((name, index_url))
            } else {
                log::warn!(
                    "`doc.extern-map.{}` specifies a registry that is not defined",
                    name
                );
                None
            }
        })
        .collect();
    for dep in cx.unit_deps(unit) {
        if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
            for (registry, location) in &map.registries {
                let sid = dep.unit.pkg.package_id().source_id();
                let matches_registry = || -> bool {
                    if !sid.is_registry() {
                        return false;
                    }
                    if sid.is_default_registry() {
                        return registry == CRATES_IO_REGISTRY;
                    }
                    if let Some(index_url) = name2url.get(registry) {
                        return index_url == sid.url();
                    }
                    false
                };
                if matches_registry() {
                    let mut url = location.clone();
                    if !url.contains("{pkg_name}") && !url.contains("{version}") {
                        if !url.ends_with('/') {
                            url.push('/');
                        }
                        url.push_str("{pkg_name}/{version}/");
                    }
                    let url = url
                        .replace("{pkg_name}", &dep.unit.pkg.name())
                        .replace("{version}", &dep.unit.pkg.version().to_string());
                    rustdoc.arg("--extern-html-root-url");
                    rustdoc.arg(format!("{}={}", dep.unit.target.crate_name(), url));
                    unstable_opts = true;
                }
            }
        }
    }
    let std_url = match &map.std {
        None | Some(RustdocExternMode::Remote) => None,
        Some(RustdocExternMode::Local) => {
            let sysroot = &cx.bcx.target_data.info(CompileKind::Host).sysroot;
            let html_root = sysroot.join("share").join("doc").join("rust").join("html");
            if html_root.exists() {
                let url = Url::from_file_path(&html_root).map_err(|()| {
                    internal(format!(
                        "`{}` failed to convert to URL",
                        html_root.display()
                    ))
                })?;
                Some(url.to_string())
            } else {
                log::warn!(
                    "`doc.extern-map.std` is \"local\", but local docs don't appear to exist at {}",
                    html_root.display()
                );
                None
            }
        }
        Some(RustdocExternMode::Url(s)) => Some(s.to_string()),
    };
    if let Some(url) = std_url {
        for name in &["std", "core", "alloc", "proc_macro"] {
            rustdoc.arg("--extern-html-root-url");
            rustdoc.arg(format!("{}={}", name, url));
            unstable_opts = true;
        }
    }

    if unstable_opts {
        rustdoc.arg("-Zunstable-options");
    }
    Ok(())
}
<commit_msg>fix nightly rustdoc-extern tests<commit_after>//! Utilities for building with rustdoc.

use crate::core::compiler::context::Context;
use crate::core::compiler::unit::Unit;
use crate::core::compiler::CompileKind;
use crate::sources::CRATES_IO_REGISTRY;
use crate::util::errors::{internal, CargoResult};
use crate::util::ProcessBuilder;
use std::collections::HashMap;
use std::fmt;
use std::hash;
use url::Url;

/// Mode used for `std`.
#[derive(Debug, Hash)]
pub enum RustdocExternMode {
    /// Use a local `file://` URL.
    Local,
    /// Use a remote URL to https://doc.rust-lang.org/ (default).
    Remote,
    /// An arbitrary URL.
    Url(String),
}

impl From<String> for RustdocExternMode {
    fn from(s: String) -> RustdocExternMode {
        match s.as_ref() {
            "local" => RustdocExternMode::Local,
            "remote" => RustdocExternMode::Remote,
            _ => RustdocExternMode::Url(s),
        }
    }
}

impl fmt::Display for RustdocExternMode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            RustdocExternMode::Local => "local".fmt(f),
            RustdocExternMode::Remote => "remote".fmt(f),
            RustdocExternMode::Url(s) => s.fmt(f),
        }
    }
}

impl<'de> serde::de::Deserialize<'de> for RustdocExternMode {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::de::Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        Ok(s.into())
    }
}

#[derive(serde::Deserialize, Debug, Default)]
#[serde(default)]
pub struct RustdocExternMap {
    registries: HashMap<String, String>,
    std: Option<RustdocExternMode>,
}

impl hash::Hash for RustdocExternMap {
    fn hash<H: hash::Hasher>(&self, into: &mut H) {
        self.std.hash(into);
        for (key, value) in &self.registries {
            key.hash(into);
            value.hash(into);
        }
    }
}

pub fn add_root_urls(
    cx: &Context<'_, '_>,
    unit: &Unit,
    rustdoc: &mut ProcessBuilder,
) -> CargoResult<()> {
    let config = cx.bcx.config;
    if !config.cli_unstable().rustdoc_map {
        log::debug!("`doc.extern-map` ignored, requires -Zrustdoc-map flag");
        return Ok(());
    }
    let map = config.doc_extern_map()?;
    if map.registries.is_empty() && map.std.is_none() {
        // Skip doing unnecessary work.
        return Ok(());
    }
    let mut unstable_opts = false;
    // Collect mapping of registry name -> index url.
    let name2url: HashMap<&String, Url> = map
        .registries
        .keys()
        .filter_map(|name| {
            if let Ok(index_url) = config.get_registry_index(name) {
                Some((name, index_url))
            } else {
                log::warn!(
                    "`doc.extern-map.{}` specifies a registry that is not defined",
                    name
                );
                None
            }
        })
        .collect();
    for dep in cx.unit_deps(unit) {
        if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
            for (registry, location) in &map.registries {
                let sid = dep.unit.pkg.package_id().source_id();
                let matches_registry = || -> bool {
                    if !sid.is_registry() {
                        return false;
                    }
                    if sid.is_default_registry() {
                        return registry == CRATES_IO_REGISTRY;
                    }
                    if let Some(index_url) = name2url.get(registry) {
                        return index_url == sid.url();
                    }
                    false
                };
                if matches_registry() {
                    let mut url = location.clone();
                    if !url.contains("{pkg_name}") && !url.contains("{version}") {
                        if !url.ends_with('/') {
                            url.push('/');
                        }
                        url.push_str("{pkg_name}/{version}/");
                    }
                    let url = url
                        .replace("{pkg_name}", &dep.unit.pkg.name())
                        .replace("{version}", &dep.unit.pkg.version().to_string());
                    rustdoc.arg("--extern-html-root-url");
                    rustdoc.arg(format!("{}={}", dep.unit.target.crate_name(), url));
                    unstable_opts = true;
                }
            }
        }
    }
    let std_url = match &map.std {
        None | Some(RustdocExternMode::Remote) => None,
        Some(RustdocExternMode::Local) => {
            let sysroot = &cx.bcx.target_data.info(CompileKind::Host).sysroot;
            let html_root = sysroot.join("share").join("doc").join("rust").join("html");
            if html_root.exists() {
                let url = Url::from_file_path(&html_root).map_err(|()| {
                    internal(format!(
                        "`{}` failed to convert to URL",
                        html_root.display()
                    ))
                })?;
                Some(url.to_string())
            } else {
                log::warn!(
                    "`doc.extern-map.std` is \"local\", but local docs don't appear to exist at {}",
                    html_root.display()
                );
                None
            }
        }
        Some(RustdocExternMode::Url(s)) => Some(s.to_string()),
    };
    if let Some(url) = std_url {
        for name in &["std", "core", "alloc", "proc_macro"] {
            rustdoc.arg("--extern-html-root-url");
            rustdoc.arg(format!("{}={}", name, url));
            unstable_opts = true;
        }
    }

    if unstable_opts {
        rustdoc.arg("-Zunstable-options");
    }
    Ok(())
}
<|endoftext|>


<commit_before>use rustc::lint::*;
use std::collections::HashMap;
use std::char;
use syntax::ast::*;
use syntax::codemap::Span;
use syntax::visit::FnKind;
use utils::{constants, span_lint, span_help_and_lint, snippet, snippet_opt, span_lint_and_then};

/// **What it does:** Checks for structure field patterns bound to wildcards.
///
/// **Why is this bad?** Using `..` instead is shorter and leaves the focus on
/// the fields that are actually bound.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let { a: _, b: ref b, c: _ } = ..
/// ```
declare_lint! {
    pub UNNEEDED_FIELD_PATTERN,
    Warn,
    "struct fields bound to a wildcard instead of using `..`"
}

/// **What it does:** Checks for function arguments having the similar names
/// differing by an underscore.
///
/// **Why is this bad?** It affects code readability.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn foo(a: i32, _a: i32) {}
/// ```
declare_lint! {
    pub DUPLICATE_UNDERSCORE_ARGUMENT,
    Warn,
    "function arguments having names which only differ by an underscore"
}

/// **What it does:** Detects closures called in the same expression where they are defined.
///
/// **Why is this bad?** It is unnecessarily adding to the expression's complexity.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// (|| 42)()
/// ```
declare_lint! {
    pub REDUNDANT_CLOSURE_CALL,
    Warn,
    "throwaway closures called in the expression they are defined"
}

/// **What it does:** Detects expressions of the form `--x`.
///
/// **Why is this bad?** It can mislead C/C++ programmers to think `x` was
/// decremented.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// --x;
/// ```
declare_lint! {
    pub DOUBLE_NEG,
    Warn,
    "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
}

/// **What it does:** Warns on hexadecimal literals with mixed-case letter digits.
///
/// **Why is this bad?** It looks confusing.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let y = 0x1a9BAcD;
/// ```
declare_lint! {
    pub MIXED_CASE_HEX_LITERALS,
    Warn,
    "hex literals whose letter digits are not consistently upper- or lowercased"
}

/// **What it does:** Warns if literal suffixes are not separated by an underscore.
///
/// **Why is this bad?** It is much less readable.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let y = 123832i32;
/// ```
declare_lint! {
    pub UNSEPARATED_LITERAL_SUFFIX,
    Allow,
    "literals whose suffix is not separated by an underscore"
}

/// **What it does:** Warns if an integral constant literal starts with `0`.
///
/// **Why is this bad?** In some languages (including the infamous C language and most of its
/// familly), this marks an octal constant. In Rust however, this is a decimal constant. This could
/// be confusing for both the writer and a reader of the constant.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// In Rust:
/// ```rust
/// fn main() {
///     let a = 0123;
///     println!("{}", a);
/// }
/// ```
///
/// prints `123`, while in C:
///
/// ```c
/// #include <stdio.h>
///
/// int main() {
///     int a = 0123;
///     printf("%d\n", a);
/// }
/// ```
///
/// prints `83` (as `83 == 0o123` while `123 == 0o173`).
declare_lint! {
    pub ZERO_PREFIXED_LITERAL,
    Warn,
    "integer literals starting with `0`"
}

/// **What it does:** Warns if a generic shadows a built-in type.
///
/// **Why is this bad?** This gives surprising type errors.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// impl<u32> Foo<u32> {
///     fn impl_func(&self) -> u32 {
///         42
///     }
/// }
/// ```
declare_lint! {
    pub BUILTIN_TYPE_SHADOW,
    Warn,
    "shadowing a builtin type"
}

/// **What it does:** Catch casts from `0` to some pointer type
///
/// **Why is this bad?** This generally means `null` and is better expressed as
/// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// ß as *const u32
/// ```
declare_lint! {
    pub ZERO_PTR,
    Warn,
    "using 0 as *{const, mut} T"
}

#[derive(Copy, Clone)]
pub struct MiscEarly;

impl LintPass for MiscEarly {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNNEEDED_FIELD_PATTERN,
                    DUPLICATE_UNDERSCORE_ARGUMENT,
                    REDUNDANT_CLOSURE_CALL,
                    DOUBLE_NEG,
                    MIXED_CASE_HEX_LITERALS,
                    UNSEPARATED_LITERAL_SUFFIX,
                    ZERO_PREFIXED_LITERAL,
                    BUILTIN_TYPE_SHADOW,
                    ZERO_PTR)
    }
}

impl EarlyLintPass for MiscEarly {
    fn check_generics(&mut self, cx: &EarlyContext, gen: &Generics) {
        for ty in &gen.ty_params {
            let name = ty.ident.name.as_str();
            if constants::BUILTIN_TYPES.contains(&&*name) {
                span_lint(cx,
                          BUILTIN_TYPE_SHADOW,
                          ty.span,
                          &format!("This generic shadows the built-in type `{}`", name));
            }
        }
    }

    fn check_pat(&mut self, cx: &EarlyContext, pat: &Pat) {
        if let PatKind::Struct(ref npat, ref pfields, _) = pat.node {
            let mut wilds = 0;
            let type_name = npat.segments.last().expect("A path must have at least one segment").identifier.name;

            for field in pfields {
                if field.node.pat.node == PatKind::Wild {
                    wilds += 1;
                }
            }
            if !pfields.is_empty() && wilds == pfields.len() {
                span_help_and_lint(cx,
                                   UNNEEDED_FIELD_PATTERN,
                                   pat.span,
                                   "All the struct fields are matched to a wildcard pattern, consider using `..`.",
                                   &format!("Try with `{} {{ .. }}` instead", type_name));
                return;
            }
            if wilds > 0 {
                let mut normal = vec![];

                for field in pfields {
                    if field.node.pat.node != PatKind::Wild {
                        if let Ok(n) = cx.sess().codemap().span_to_snippet(field.span) {
                            normal.push(n);
                        }
                    }
                }
                for field in pfields {
                    if field.node.pat.node == PatKind::Wild {
                        wilds -= 1;
                        if wilds > 0 {
                            span_lint(cx,
                                      UNNEEDED_FIELD_PATTERN,
                                      field.span,
                                      "You matched a field with a wildcard pattern. Consider using `..` instead");
                        } else {
                            span_help_and_lint(cx,
                                               UNNEEDED_FIELD_PATTERN,
                                               field.span,
                                               "You matched a field with a wildcard pattern. Consider using `..` \
                                                instead",
                                               &format!("Try with `{} {{ {}, .. }}`",
                                                        type_name,
                                                        normal[..].join(", ")));
                        }
                    }
                }
            }
        }
    }

    fn check_fn(&mut self, cx: &EarlyContext, _: FnKind, decl: &FnDecl, _: Span, _: NodeId) {
        let mut registered_names: HashMap<String, Span> = HashMap::new();

        for arg in &decl.inputs {
            if let PatKind::Ident(_, sp_ident, None) = arg.pat.node {
                let arg_name = sp_ident.node.to_string();

                if arg_name.starts_with('_') {
                    if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
                        span_lint(cx,
                                  DUPLICATE_UNDERSCORE_ARGUMENT,
                                  *correspondence,
                                  &format!("`{}` already exists, having another argument having almost the same \
                                            name makes code comprehension and documentation more difficult",
                                           arg_name[1..].to_owned()));;
                    }
                } else {
                    registered_names.insert(arg_name, arg.pat.span);
                }
            }
        }
    }

    fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
        match expr.node {
            ExprKind::Call(ref paren, _) => {
                if let ExprKind::Paren(ref closure) = paren.node {
                    if let ExprKind::Closure(_, ref decl, ref block, _) = closure.node {
                        span_lint_and_then(cx,
                                           REDUNDANT_CLOSURE_CALL,
                                           expr.span,
                                           "Try not to call a closure in the expression where it is declared.",
                                           |db| if decl.inputs.is_empty() {
                                               let hint = snippet(cx, block.span, "..").into_owned();
                                               db.span_suggestion(expr.span, "Try doing something like: ", hint);
                                           });
                    }
                }
            },
            ExprKind::Unary(UnOp::Neg, ref inner) => {
                if let ExprKind::Unary(UnOp::Neg, _) = inner.node {
                    span_lint(cx,
                              DOUBLE_NEG,
                              expr.span,
                              "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op");
                }
            },
            ExprKind::Lit(ref lit) => {
                if_let_chain! {[
                    let LitKind::Int(value, ..) = lit.node,
                    let Some(src) = snippet_opt(cx, lit.span),
                    let Some(firstch) = src.chars().next(),
                    char::to_digit(firstch, 10).is_some()
                ], {
                    let mut prev = '\0';
                    for ch in src.chars() {
                        if ch == 'i' || ch == 'u' {
                            if prev != '_' {
                                span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span,
                                          "integer type suffix should be separated by an underscore");
                            }
                            break;
                        }
                        prev = ch;
                    }
                    if src.starts_with("0x") {
                        let mut seen = (false, false);
                        for ch in src.chars() {
                            match ch {
                                'a' ... 'f' => seen.0 = true,
                                'A' ... 'F' => seen.1 = true,
                                'i' | 'u'   => break,   // start of suffix already
                                _ => ()
                            }
                        }
                        if seen.0 && seen.1 {
                            span_lint(cx, MIXED_CASE_HEX_LITERALS, lit.span,
                                      "inconsistent casing in hexadecimal literal");
                        }
                    } else if src.starts_with("0b") || src.starts_with("0o") {
                        /* nothing to do */
                    } else if value != 0 && src.starts_with('0') {
                        span_lint_and_then(cx,
                                           ZERO_PREFIXED_LITERAL,
                                           lit.span,
                                           "this is a decimal constant",
                                           |db| {
                            db.span_suggestion(
                                lit.span,
                                "if you mean to use a decimal constant, remove the `0` to remove confusion:",
                                src[1..].to_string(),
                            );
                            db.span_suggestion(
                                lit.span,
                                "if you mean to use an octal constant, use `0o`:",
                                format!("0o{}", &src[1..]),
                            );
                        });
                    }
                }}
                if_let_chain! {[
                    let LitKind::Float(..) = lit.node,
                    let Some(src) = snippet_opt(cx, lit.span),
                    let Some(firstch) = src.chars().next(),
                    char::to_digit(firstch, 10).is_some()
                ], {
                    let mut prev = '\0';
                    for ch in src.chars() {
                        if ch == 'f' {
                            if prev != '_' {
                                span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span,
                                          "float type suffix should be separated by an underscore");
                            }
                            break;
                        }
                        prev = ch;
                    }
                }}
            },
            ExprKind::Cast(ref e, ref ty) => {
                check_cast(cx, expr.span, e, ty);
            },
            _ => (),
        }
    }

    fn check_block(&mut self, cx: &EarlyContext, block: &Block) {
        for w in block.stmts.windows(2) {
            if_let_chain! {[
                let StmtKind::Local(ref local) = w[0].node,
                let Option::Some(ref t) = local.init,
                let ExprKind::Closure(_, _, _, _) = t.node,
                let PatKind::Ident(_, sp_ident, _) = local.pat.node,
                let StmtKind::Semi(ref second) = w[1].node,
                let ExprKind::Assign(_, ref call) = second.node,
                let ExprKind::Call(ref closure, _) = call.node,
                let ExprKind::Path(_, ref path) = closure.node
            ], {
                if sp_ident.node == (&path.segments[0]).identifier {
                    span_lint(
                        cx,
                        REDUNDANT_CLOSURE_CALL,
                        second.span,
                        "Closure called just once immediately after it was declared",
                    );
                }
            }}
        }
    }
}

fn check_cast(cx: &EarlyContext, span: Span, e: &Expr, ty: &Ty) {
    if_let_chain! {[
        let TyKind::Ptr(MutTy { mutbl, .. }) = ty.node,
        let ExprKind::Lit(ref lit) = e.node,
        let LitKind::Int(value, ..) = lit.node,
        value == 0
    ], {
        let msg = match mutbl {
            Mutability::Mutable => "`0 as *mut _` detected. Consider using `ptr::null_mut()`",
            Mutability::Immutable => "`0 as *const _` detected. Consider using `ptr::null()`",
        };
        span_lint(cx, ZERO_PTR, span, msg);
    }}
}
<commit_msg>fix German keyboard fail<commit_after>use rustc::lint::*;
use std::collections::HashMap;
use std::char;
use syntax::ast::*;
use syntax::codemap::Span;
use syntax::visit::FnKind;
use utils::{constants, span_lint, span_help_and_lint, snippet, snippet_opt, span_lint_and_then};

/// **What it does:** Checks for structure field patterns bound to wildcards.
///
/// **Why is this bad?** Using `..` instead is shorter and leaves the focus on
/// the fields that are actually bound.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let { a: _, b: ref b, c: _ } = ..
/// ```
declare_lint! {
    pub UNNEEDED_FIELD_PATTERN,
    Warn,
    "struct fields bound to a wildcard instead of using `..`"
}

/// **What it does:** Checks for function arguments having the similar names
/// differing by an underscore.
///
/// **Why is this bad?** It affects code readability.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn foo(a: i32, _a: i32) {}
/// ```
declare_lint! {
    pub DUPLICATE_UNDERSCORE_ARGUMENT,
    Warn,
    "function arguments having names which only differ by an underscore"
}

/// **What it does:** Detects closures called in the same expression where they are defined.
///
/// **Why is this bad?** It is unnecessarily adding to the expression's complexity.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// (|| 42)()
/// ```
declare_lint! {
    pub REDUNDANT_CLOSURE_CALL,
    Warn,
    "throwaway closures called in the expression they are defined"
}

/// **What it does:** Detects expressions of the form `--x`.
///
/// **Why is this bad?** It can mislead C/C++ programmers to think `x` was
/// decremented.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// --x;
/// ```
declare_lint! {
    pub DOUBLE_NEG,
    Warn,
    "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
}

/// **What it does:** Warns on hexadecimal literals with mixed-case letter digits.
///
/// **Why is this bad?** It looks confusing.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let y = 0x1a9BAcD;
/// ```
declare_lint! {
    pub MIXED_CASE_HEX_LITERALS,
    Warn,
    "hex literals whose letter digits are not consistently upper- or lowercased"
}

/// **What it does:** Warns if literal suffixes are not separated by an underscore.
///
/// **Why is this bad?** It is much less readable.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let y = 123832i32;
/// ```
declare_lint! {
    pub UNSEPARATED_LITERAL_SUFFIX,
    Allow,
    "literals whose suffix is not separated by an underscore"
}

/// **What it does:** Warns if an integral constant literal starts with `0`.
///
/// **Why is this bad?** In some languages (including the infamous C language and most of its
/// familly), this marks an octal constant. In Rust however, this is a decimal constant. This could
/// be confusing for both the writer and a reader of the constant.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// In Rust:
/// ```rust
/// fn main() {
///     let a = 0123;
///     println!("{}", a);
/// }
/// ```
///
/// prints `123`, while in C:
///
/// ```c
/// #include <stdio.h>
///
/// int main() {
///     int a = 0123;
///     printf("%d\n", a);
/// }
/// ```
///
/// prints `83` (as `83 == 0o123` while `123 == 0o173`).
declare_lint! {
    pub ZERO_PREFIXED_LITERAL,
    Warn,
    "integer literals starting with `0`"
}

/// **What it does:** Warns if a generic shadows a built-in type.
///
/// **Why is this bad?** This gives surprising type errors.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// impl<u32> Foo<u32> {
///     fn impl_func(&self) -> u32 {
///         42
///     }
/// }
/// ```
declare_lint! {
    pub BUILTIN_TYPE_SHADOW,
    Warn,
    "shadowing a builtin type"
}

/// **What it does:** Catch casts from `0` to some pointer type
///
/// **Why is this bad?** This generally means `null` and is better expressed as
/// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 0 as *const u32
/// ```
declare_lint! {
    pub ZERO_PTR,
    Warn,
    "using 0 as *{const, mut} T"
}

#[derive(Copy, Clone)]
pub struct MiscEarly;

impl LintPass for MiscEarly {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNNEEDED_FIELD_PATTERN,
                    DUPLICATE_UNDERSCORE_ARGUMENT,
                    REDUNDANT_CLOSURE_CALL,
                    DOUBLE_NEG,
                    MIXED_CASE_HEX_LITERALS,
                    UNSEPARATED_LITERAL_SUFFIX,
                    ZERO_PREFIXED_LITERAL,
                    BUILTIN_TYPE_SHADOW,
                    ZERO_PTR)
    }
}

impl EarlyLintPass for MiscEarly {
    fn check_generics(&mut self, cx: &EarlyContext, gen: &Generics) {
        for ty in &gen.ty_params {
            let name = ty.ident.name.as_str();
            if constants::BUILTIN_TYPES.contains(&&*name) {
                span_lint(cx,
                          BUILTIN_TYPE_SHADOW,
                          ty.span,
                          &format!("This generic shadows the built-in type `{}`", name));
            }
        }
    }

    fn check_pat(&mut self, cx: &EarlyContext, pat: &Pat) {
        if let PatKind::Struct(ref npat, ref pfields, _) = pat.node {
            let mut wilds = 0;
            let type_name = npat.segments.last().expect("A path must have at least one segment").identifier.name;

            for field in pfields {
                if field.node.pat.node == PatKind::Wild {
                    wilds += 1;
                }
            }
            if !pfields.is_empty() && wilds == pfields.len() {
                span_help_and_lint(cx,
                                   UNNEEDED_FIELD_PATTERN,
                                   pat.span,
                                   "All the struct fields are matched to a wildcard pattern, consider using `..`.",
                                   &format!("Try with `{} {{ .. }}` instead", type_name));
                return;
            }
            if wilds > 0 {
                let mut normal = vec![];

                for field in pfields {
                    if field.node.pat.node != PatKind::Wild {
                        if let Ok(n) = cx.sess().codemap().span_to_snippet(field.span) {
                            normal.push(n);
                        }
                    }
                }
                for field in pfields {
                    if field.node.pat.node == PatKind::Wild {
                        wilds -= 1;
                        if wilds > 0 {
                            span_lint(cx,
                                      UNNEEDED_FIELD_PATTERN,
                                      field.span,
                                      "You matched a field with a wildcard pattern. Consider using `..` instead");
                        } else {
                            span_help_and_lint(cx,
                                               UNNEEDED_FIELD_PATTERN,
                                               field.span,
                                               "You matched a field with a wildcard pattern. Consider using `..` \
                                                instead",
                                               &format!("Try with `{} {{ {}, .. }}`",
                                                        type_name,
                                                        normal[..].join(", ")));
                        }
                    }
                }
            }
        }
    }

    fn check_fn(&mut self, cx: &EarlyContext, _: FnKind, decl: &FnDecl, _: Span, _: NodeId) {
        let mut registered_names: HashMap<String, Span> = HashMap::new();

        for arg in &decl.inputs {
            if let PatKind::Ident(_, sp_ident, None) = arg.pat.node {
                let arg_name = sp_ident.node.to_string();

                if arg_name.starts_with('_') {
                    if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
                        span_lint(cx,
                                  DUPLICATE_UNDERSCORE_ARGUMENT,
                                  *correspondence,
                                  &format!("`{}` already exists, having another argument having almost the same \
                                            name makes code comprehension and documentation more difficult",
                                           arg_name[1..].to_owned()));;
                    }
                } else {
                    registered_names.insert(arg_name, arg.pat.span);
                }
            }
        }
    }

    fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
        match expr.node {
            ExprKind::Call(ref paren, _) => {
                if let ExprKind::Paren(ref closure) = paren.node {
                    if let ExprKind::Closure(_, ref decl, ref block, _) = closure.node {
                        span_lint_and_then(cx,
                                           REDUNDANT_CLOSURE_CALL,
                                           expr.span,
                                           "Try not to call a closure in the expression where it is declared.",
                                           |db| if decl.inputs.is_empty() {
                                               let hint = snippet(cx, block.span, "..").into_owned();
                                               db.span_suggestion(expr.span, "Try doing something like: ", hint);
                                           });
                    }
                }
            },
            ExprKind::Unary(UnOp::Neg, ref inner) => {
                if let ExprKind::Unary(UnOp::Neg, _) = inner.node {
                    span_lint(cx,
                              DOUBLE_NEG,
                              expr.span,
                              "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op");
                }
            },
            ExprKind::Lit(ref lit) => {
                if_let_chain! {[
                    let LitKind::Int(value, ..) = lit.node,
                    let Some(src) = snippet_opt(cx, lit.span),
                    let Some(firstch) = src.chars().next(),
                    char::to_digit(firstch, 10).is_some()
                ], {
                    let mut prev = '\0';
                    for ch in src.chars() {
                        if ch == 'i' || ch == 'u' {
                            if prev != '_' {
                                span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span,
                                          "integer type suffix should be separated by an underscore");
                            }
                            break;
                        }
                        prev = ch;
                    }
                    if src.starts_with("0x") {
                        let mut seen = (false, false);
                        for ch in src.chars() {
                            match ch {
                                'a' ... 'f' => seen.0 = true,
                                'A' ... 'F' => seen.1 = true,
                                'i' | 'u'   => break,   // start of suffix already
                                _ => ()
                            }
                        }
                        if seen.0 && seen.1 {
                            span_lint(cx, MIXED_CASE_HEX_LITERALS, lit.span,
                                      "inconsistent casing in hexadecimal literal");
                        }
                    } else if src.starts_with("0b") || src.starts_with("0o") {
                        /* nothing to do */
                    } else if value != 0 && src.starts_with('0') {
                        span_lint_and_then(cx,
                                           ZERO_PREFIXED_LITERAL,
                                           lit.span,
                                           "this is a decimal constant",
                                           |db| {
                            db.span_suggestion(
                                lit.span,
                                "if you mean to use a decimal constant, remove the `0` to remove confusion:",
                                src[1..].to_string(),
                            );
                            db.span_suggestion(
                                lit.span,
                                "if you mean to use an octal constant, use `0o`:",
                                format!("0o{}", &src[1..]),
                            );
                        });
                    }
                }}
                if_let_chain! {[
                    let LitKind::Float(..) = lit.node,
                    let Some(src) = snippet_opt(cx, lit.span),
                    let Some(firstch) = src.chars().next(),
                    char::to_digit(firstch, 10).is_some()
                ], {
                    let mut prev = '\0';
                    for ch in src.chars() {
                        if ch == 'f' {
                            if prev != '_' {
                                span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span,
                                          "float type suffix should be separated by an underscore");
                            }
                            break;
                        }
                        prev = ch;
                    }
                }}
            },
            ExprKind::Cast(ref e, ref ty) => {
                check_cast(cx, expr.span, e, ty);
            },
            _ => (),
        }
    }

    fn check_block(&mut self, cx: &EarlyContext, block: &Block) {
        for w in block.stmts.windows(2) {
            if_let_chain! {[
                let StmtKind::Local(ref local) = w[0].node,
                let Option::Some(ref t) = local.init,
                let ExprKind::Closure(_, _, _, _) = t.node,
                let PatKind::Ident(_, sp_ident, _) = local.pat.node,
                let StmtKind::Semi(ref second) = w[1].node,
                let ExprKind::Assign(_, ref call) = second.node,
                let ExprKind::Call(ref closure, _) = call.node,
                let ExprKind::Path(_, ref path) = closure.node
            ], {
                if sp_ident.node == (&path.segments[0]).identifier {
                    span_lint(
                        cx,
                        REDUNDANT_CLOSURE_CALL,
                        second.span,
                        "Closure called just once immediately after it was declared",
                    );
                }
            }}
        }
    }
}

fn check_cast(cx: &EarlyContext, span: Span, e: &Expr, ty: &Ty) {
    if_let_chain! {[
        let TyKind::Ptr(MutTy { mutbl, .. }) = ty.node,
        let ExprKind::Lit(ref lit) = e.node,
        let LitKind::Int(value, ..) = lit.node,
        value == 0
    ], {
        let msg = match mutbl {
            Mutability::Mutable => "`0 as *mut _` detected. Consider using `ptr::null_mut()`",
            Mutability::Immutable => "`0 as *const _` detected. Consider using `ptr::null()`",
        };
        span_lint(cx, ZERO_PTR, span, msg);
    }}
}
<|endoftext|>


<commit_before>use std::sync::Arc;
use core;
use core::futures::sync::mpsc;

use subscription::Session;

/// Raw transport sink for specific client.
pub type TransportSender = mpsc::Sender<String>;
/// Raw transport error.
pub type TransportError = mpsc::SendError<String>;
/// Subscription send result.
pub type SinkResult = core::futures::sink::Send<TransportSender>;

/// Metadata extension for pub-sub method handling.
pub trait PubSubMetadata: core::Metadata {
	/// Returns session object associated with given request/client.
	/// `None` indicates that sessions are not supported on the used transport.
	fn session(&self) -> Option<Arc<Session>>;
}

/// Unique subscription id.
/// NOTE Assigning same id to different requests will cause the previous request to be unsubscribed.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SubscriptionId {
	/// U64 number
	Number(u64),
	/// String
	String(String),
}

impl SubscriptionId {
	/// Parses `core::Value` into unique subscription id.
	pub fn parse_value(val: &core::Value) -> Option<SubscriptionId> {
		match *val {
			core::Value::String(ref val) => Some(SubscriptionId::String(val.clone())),
			core::Value::Number(ref val) => val.as_u64().map(SubscriptionId::Number),
			_ => None,
		}
	}
}

impl From<SubscriptionId> for core::Value {
	fn from(sub: SubscriptionId) -> Self {
		match sub {
			SubscriptionId::Number(val) => core::Value::Number(val.into()),
			SubscriptionId::String(val) => core::Value::String(val),
		}
	}
}

#[cfg(test)]
mod tests {
	use core::Value;
	use super::SubscriptionId;

	#[test]
	fn should_convert_between_value_and_subscription_id() {
		// given
		let val1 = Value::Number(5.into());
		let val2 = Value::String("asdf".into());
		let val3 = Value::Null;

		// when
		let res1 = SubscriptionId::parse_value(&val1);
		let res2 = SubscriptionId::parse_value(&val2);
		let res3 = SubscriptionId::parse_value(&val3);

		// then
		assert_eq!(res1, Some(SubscriptionId::Number(5)));
		assert_eq!(res2, Some(SubscriptionId::String("asdf".into())));
		assert_eq!(res3, None);

		// and back
		assert_eq!(Value::from(res1.unwrap()), val1);
		assert_eq!(Value::from(res2.unwrap()), val2);
	}
}
<commit_msg>Add From<_> implementations. (#213)<commit_after>use std::sync::Arc;
use core;
use core::futures::sync::mpsc;

use subscription::Session;

/// Raw transport sink for specific client.
pub type TransportSender = mpsc::Sender<String>;
/// Raw transport error.
pub type TransportError = mpsc::SendError<String>;
/// Subscription send result.
pub type SinkResult = core::futures::sink::Send<TransportSender>;

/// Metadata extension for pub-sub method handling.
pub trait PubSubMetadata: core::Metadata {
	/// Returns session object associated with given request/client.
	/// `None` indicates that sessions are not supported on the used transport.
	fn session(&self) -> Option<Arc<Session>>;
}

/// Unique subscription id.
/// NOTE Assigning same id to different requests will cause the previous request to be unsubscribed.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SubscriptionId {
	/// U64 number
	Number(u64),
	/// String
	String(String),
}

impl SubscriptionId {
	/// Parses `core::Value` into unique subscription id.
	pub fn parse_value(val: &core::Value) -> Option<SubscriptionId> {
		match *val {
			core::Value::String(ref val) => Some(SubscriptionId::String(val.clone())),
			core::Value::Number(ref val) => val.as_u64().map(SubscriptionId::Number),
			_ => None,
		}
	}
}

impl From<String> for SubscriptionId {
	fn from(other: String) -> Self {
		SubscriptionId::String(other)
	}
}

impl From<u64> for SubscriptionId {
	fn from(other: u64) -> Self {
		SubscriptionId::Number(other)
	}
}

impl From<SubscriptionId> for core::Value {
	fn from(sub: SubscriptionId) -> Self {
		match sub {
			SubscriptionId::Number(val) => core::Value::Number(val.into()),
			SubscriptionId::String(val) => core::Value::String(val),
		}
	}
}

#[cfg(test)]
mod tests {
	use core::Value;
	use super::SubscriptionId;

	#[test]
	fn should_convert_between_value_and_subscription_id() {
		// given
		let val1 = Value::Number(5.into());
		let val2 = Value::String("asdf".into());
		let val3 = Value::Null;

		// when
		let res1 = SubscriptionId::parse_value(&val1);
		let res2 = SubscriptionId::parse_value(&val2);
		let res3 = SubscriptionId::parse_value(&val3);

		// then
		assert_eq!(res1, Some(SubscriptionId::Number(5)));
		assert_eq!(res2, Some(SubscriptionId::String("asdf".into())));
		assert_eq!(res3, None);

		// and back
		assert_eq!(Value::from(res1.unwrap()), val1);
		assert_eq!(Value::from(res2.unwrap()), val2);
	}
}
<|endoftext|>