redox_installer 0.2.42

A Redox filesystem builder
Documentation
use std::collections::BTreeMap;
use std::fmt::Display;
use std::fs;
use std::mem;
use std::path::{Path, PathBuf};

use anyhow::bail;
use anyhow::Context;
use anyhow::Result;

use crate::PackageConfig;

pub mod file;
#[cfg(feature = "installer")]
pub mod file_impl;
pub mod general;
pub mod package;
pub mod user;

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Config {
    #[serde(default)]
    pub include: Vec<PathBuf>,
    #[serde(default)]
    pub general: general::GeneralConfig,
    #[serde(default)]
    pub packages: BTreeMap<String, package::PackageConfig>,
    #[serde(default)]
    pub files: Vec<file::FileConfig>,
    #[serde(default)]
    pub users: BTreeMap<String, user::UserConfig>,
    #[serde(default)]
    pub groups: BTreeMap<String, user::GroupConfig>,
}

impl Config {
    /// Load installer config from a TOML path
    pub fn from_file(path: &Path) -> Result<Self> {
        let mut config: Config = match fs::read_to_string(&path) {
            Ok(config_data) => match toml::from_str(&config_data) {
                Ok(config) => config,
                Err(err) => {
                    bail!("failed to decode '{}': {}", path.display(), err);
                }
            },
            Err(err) => {
                bail!("failed to read '{}': {}", path.display(), err);
            }
        };

        let config_dir = path.parent().unwrap();

        let mut configs = mem::take(&mut config.include)
            .into_iter()
            .map(|path| {
                Config::from_file(&config_dir.join(&path))
                    .with_context(|| format!("Importing from {}", path.display()))
            })
            .collect::<Result<Vec<Config>>>()?;
        configs.push(config); // Put ourself last to ensure that it overwrites anything else.

        config = configs.remove(0);

        for other_config in configs {
            config.merge(other_config);
        }

        Ok(config)
    }

    /// Load hardcoded install config to fetch bootloaders
    pub fn bootloader_config() -> Self {
        let mut bootloader_config = Config::default();
        // TODO: This is unused
        bootloader_config.files.push(file::FileConfig {
            path: "/etc/pkg.d/50_redox".to_string(),
            data: "https://static.redox-os.org/pkg".to_string(),
            ..Default::default()
        });
        bootloader_config
            .packages
            .insert("bootloader".to_string(), PackageConfig::default());
        bootloader_config
    }

    pub fn merge(&mut self, other: Config) {
        assert!(self.include.is_empty());
        assert!(other.include.is_empty());

        let Config {
            include: _,
            general: other_general,
            packages: other_packages,
            files: other_files,
            users: other_users,
            groups: other_groups,
        } = other;

        self.general.merge(other_general);

        for (package, package_config) in other_packages {
            self.packages.insert(package, package_config);
        }

        self.files.extend(other_files);

        for (user, user_config) in other_users {
            self.users.insert(user, user_config);
        }

        for (group, group_config) in other_groups {
            self.groups.insert(group, group_config);
        }
    }
}

impl Display for Config {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f, "files:")?;
        for file in &self.files {
            writeln!(f, "- {}", file)?;
        }
        writeln!(f, "users:")?;
        for (name, user) in &self.users {
            writeln!(f, "- {}:{}", name, user)?;
        }
        write!(f, "packages: ")?;
        for name in self.packages.keys() {
            write!(f, " {}", name)?;
        }
        writeln!(f, "")?;
        Ok(())
    }
}