shikane 1.0.1

dynamic output configuration tool focusing on accuracy and determinism
Documentation
use std::collections::VecDeque;
use std::path::PathBuf;
use std::time::Duration;

#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use serde::{Deserialize, Serialize};
use snafu::{prelude::*, Location};
use xdg::BaseDirectories;

use crate::daemon::ShikaneArgs;
use crate::error;
use crate::profile::Profile;

#[derive(Clone, Debug)]
pub struct Settings {
    pub profiles: VecDeque<Profile>,
    pub skip_tests: bool,
    pub oneshot: bool,
    pub timeout: Duration,
    pub config_path: PathBuf,
}

#[derive(Default, Debug, Serialize, Deserialize)]
pub struct SettingsToml {
    pub timeout: Option<u64>,
    #[serde(default, rename = "profile")]
    pub profiles: VecDeque<Profile>,
}

impl Settings {
    pub fn from_args(args: ShikaneArgs) -> Self {
        let (config, path) = match parse_settings_toml(args.config) {
            Ok(config) => config,
            Err(err) => {
                error!("{}", error::report(err.as_ref()));
                std::process::exit(1);
            }
        };

        let timeout = config.timeout.unwrap_or(args.timeout);

        Self {
            profiles: config.profiles,
            skip_tests: args.skip_tests,
            oneshot: args.oneshot,
            timeout: Duration::from_millis(timeout),
            config_path: path,
        }
    }

    pub fn reload_config(&mut self, config: Option<PathBuf>) -> Result<(), Box<dyn snafu::Error>> {
        let config = config.unwrap_or(self.config_path.clone());
        debug!("reloading config from {:?}", std::fs::canonicalize(&config));
        let (config, path) = parse_settings_toml(Some(config))?;
        self.profiles = config.profiles;
        self.config_path = path;
        Ok(())
    }
}

fn parse_settings_toml(
    config_path: Option<PathBuf>,
) -> Result<(SettingsToml, PathBuf), Box<dyn snafu::Error>> {
    let config_path = match config_path {
        None => {
            let xdg_dirs = BaseDirectories::with_prefix("shikane").context(BaseDirectoriesCtx)?;
            xdg_dirs
                .place_config_file("config.toml")
                .context(ConfigPathCtx)?
        }
        Some(path) => path,
    };
    let s = std::fs::read_to_string(config_path.clone()).context(ReadConfigFileCtx)?;
    let mut config: SettingsToml = toml::from_str(&s).context(TomlDeserializeCtx)?;
    config
        .profiles
        .iter_mut()
        .enumerate()
        .for_each(|(idx, p)| p.index = idx);
    Ok((config, config_path))
}

#[derive(Debug, Snafu)]
#[snafu(context(suffix(Ctx)))]
pub enum SettingsError {
    #[snafu(display("[{location}] Problem with XDG directories"))]
    BaseDirectories {
        source: xdg::BaseDirectoriesError,
        location: Location,
    },
    #[snafu(display("[{location}] Cannot read config file"))]
    ReadConfigFile {
        source: std::io::Error,
        location: Location,
    },
    #[snafu(display("[{location}] Cannot place config file in XDG config directory"))]
    ConfigPath {
        source: std::io::Error,
        location: Location,
    },
    #[snafu(display("[{location}] Cannot deserialize settings from TOML"))]
    TomlDeserialize {
        source: toml::de::Error,
        location: Location,
    },
}