Skip to content
cfg.rs 5.27 KiB
Newer Older
Luker's avatar
Luker committed
/*
Luker's avatar
Luker committed
 * Copyright (c) 2021-2022, Luca Fulchir <luker@fenrirproject.org>
Luker's avatar
Luker committed
 *
 * This file is part of dfim.
 *
 * dfim is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * dfim is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with dfim.  If not, see <https://www.gnu.org/licenses/>.
 */

use crate::config;
use crate::config::device;
pub use crate::config::errors;
use crate::config::trgt;
Luker's avatar
Luker committed
use ::std::collections::HashMap;
use ::std::ptr::NonNull;
use ::std::sync::Arc;
use ::std::sync::Mutex;

pub struct Cfg {
    pub main: config::Main,
    pub devices: Vec<device::Device>,
    pub targets: Vec<trgt::Target>,
Luker's avatar
Luker committed
    /// only safe if Config is read only everywhere
    pub target_map: HashMap<String, NonNull<trgt::Target>>,
Luker's avatar
Luker committed
    /// only safe in send/sync since the hashmap is read only
    pub lock_dev: HashMap<String, Mutex<NonNull<device::Device>>>,
}

// Config can be made Send/Sync because it's read only
// do NOT modify Config after the initial check()
unsafe impl Send for Cfg {}
unsafe impl Sync for Cfg {}

impl Cfg {
    pub fn new(
        logger: &::slog::Logger,
        mut raw_config: config::FullConfig,
    ) -> Result<Arc<Self>, errors::ConfigError> {
        use config::CfgVerify;
        raw_config.standardize();
        raw_config.check_consistency(logger)?;
Luker's avatar
Luker committed

        let mut ret = Cfg {
            main: raw_config.main,
            devices: raw_config.devices,
            targets: raw_config.targets,
            target_map: HashMap::new(),
            lock_dev: HashMap::new(),
        };
        use trgt::TargetCommon;
Luker's avatar
Luker committed
        // build the hashmap of targets
        ret.target_map = HashMap::with_capacity(ret.targets.len());
        for t in &mut ret.targets {
            if let Some(_) = ret.target_map.get(t.id()) {
                ::slog::error!(
                    logger,
                    "Same id reused multiple times: \"{}\"",
                    t.id()
                );
                return Err(errors::ConfigError::Consistency(
                    "reuse of the same id: \"".to_owned() + t.id() + "\"",
                ));
            }
            ret.target_map
                .insert(t.id().to_string(), NonNull::new(&mut *t).unwrap());
        }
        // build the hashmap of devices
        ret.lock_dev = HashMap::with_capacity(ret.devices.len());
        for d in &mut ret.devices {
            ret.lock_dev.insert(
                d.id.to_string(),
                Mutex::new(NonNull::new(&mut *d).unwrap()),
            );
        }
        // check that the used did not specify non-existing ids in devices
        for d in &ret.devices {
            if let trgt::TargetId::Id(t) = &d.target {
Luker's avatar
Luker committed
                match ret.target_map.get(t) {
                    Some(_) => {}
                    None => {
                        ::slog::error!(
                            logger,
                            "Used a non existing id as target: \"{}\"",
                            t
                        );
                        return Err(errors::ConfigError::Consistency(
                            "Non existing id referenced as target: \""
                                .to_owned()
                                + t
                                + "\"",
                        ));
                    }
                }
            }
        }
        // check that the used did not specify non-existing ids in other targets
        for from in &ret.targets {
            for to in &from.targets() {
                match ret.target_map.get(to) {
                    Some(_) => {}
                    None => {
                        ::slog::error!(
                            logger,
                            "Used a non existing id as target: \"{}\"",
                            to
                        );
                        return Err(errors::ConfigError::Consistency(
                            "Non existing id referenced as target: \""
                                .to_owned()
                                + to
                                + "\"",
                        ));
                    }
                }
            }
        }
Luker's avatar
Luker committed
        // just WARN the user that some targets/devices might be unused
Luker's avatar
Luker committed
        'unused_target: for t in &ret.targets {
            for d in &ret.devices {
                if let trgt::TargetId::Id(d_target_id) = &d.target {
Luker's avatar
Luker committed
                    if t.id() == d_target_id {
                        continue 'unused_target;
                    }
                }
            }
            for t2 in &ret.targets {
                if t.id() == t2.id() {
                    continue;
                }
                for dest in t2.targets() {
Luker's avatar
Luker committed
                    if t.id() == dest {
Luker's avatar
Luker committed
                        continue 'unused_target;
                    }
                }
            }
            ::slog::warn!(logger, "Unused target: \"{}\"", t.id());
        }
Luker's avatar
Luker committed
        Ok(Arc::new(ret))
    }
}