diff --git a/src/bin/installer.rs b/src/bin/installer.rs index eb43526e0fb42b8ad27dc7ccf03a6588df759593..bd87130d49727c5ae8cc99b85205295e74cf9552 100644 --- a/src/bin/installer.rs +++ b/src/bin/installer.rs @@ -3,13 +3,13 @@ extern crate redox_installer; extern crate serde; extern crate toml; -use std::io::{Read, Write}; +use std::io::Write; use std::path::Path; use std::{env, fs, io, process}; use arg_parser::ArgParser; -use redox_installer::PackageConfig; +use redox_installer::{Config, PackageConfig}; fn main() { let stderr = io::stderr(); @@ -27,24 +27,11 @@ fn main() { // If not set on the command line or the filesystem config, then build packages from source. let repo_binary = parser.found("repo-binary"); - let mut config_data = String::new(); let mut config = if let Some(path) = parser.get_opt("config") { - match fs::File::open(&path) { - Ok(mut config_file) => match config_file.read_to_string(&mut config_data) { - Ok(_) => match toml::from_str(&config_data) { - Ok(config) => config, - Err(err) => { - writeln!(stderr, "installer: {}: failed to decode: {}", path, err).unwrap(); - process::exit(1); - } - }, - Err(err) => { - writeln!(stderr, "installer: {}: failed to read: {}", path, err).unwrap(); - process::exit(1); - } - }, + match Config::from_file(Path::new(&path)) { + Ok(config) => config, Err(err) => { - writeln!(stderr, "installer: {}: failed to open: {}", path, err).unwrap(); + writeln!(stderr, "installer: {err}").unwrap(); process::exit(1); } } @@ -55,7 +42,7 @@ fn main() { // Add filesystem.toml to config config.files.push(redox_installer::FileConfig { path: "filesystem.toml".to_string(), - data: config_data, + data: toml::to_string_pretty(&config).unwrap(), ..Default::default() }); diff --git a/src/bin/installer_tui.rs b/src/bin/installer_tui.rs index 74067a542eafa416363029903c227b3bc0b746f3..fb58264e75a88c4d6125fc4bbb4a3f864ba20dc2 100644 --- a/src/bin/installer_tui.rs +++ b/src/bin/installer_tui.rs @@ -309,24 +309,7 @@ fn main() { efi_partition_size: None, }; let res = with_whole_disk(&disk_path, &disk_option, |mount_path| -> Result<(), failure::Error> { - let mut config: Config = { - let path = root_path.join("filesystem.toml"); - match fs::read_to_string(&path) { - Ok(config_data) => { - match toml::from_str(&config_data) { - Ok(config) => { - config - }, - Err(err) => { - return Err(format_err!("{}: failed to decode: {}", path.display(), err)); - } - } - }, - Err(err) => { - return Err(format_err!("{}: failed to read: {}", path.display(), err)); - } - } - }; + let mut config: Config = Config::from_file(&root_path.join("filesystem.toml"))?; // Copy filesystem.toml, which is not packaged let mut files = vec![ diff --git a/src/config/file.rs b/src/config/file.rs index 21565c36590d585804e1145668cd15f93b7c216b..7672f908d506e0c240ca82ae10f4ab43d0c28c07 100644 --- a/src/config/file.rs +++ b/src/config/file.rs @@ -29,7 +29,7 @@ fn chown<P: AsRef<Path>>(path: P, uid: uid_t, gid: gid_t, recursive: bool) -> Re Ok(()) } -#[derive(Clone, Debug, Default, Deserialize)] +#[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct FileConfig { pub path: String, pub data: String, diff --git a/src/config/general.rs b/src/config/general.rs index 59099bbcdba51403679014e17eeab8d78fdff83a..7ed6c33f5efa1367aa09ab4e0bad295306c458df 100644 --- a/src/config/general.rs +++ b/src/config/general.rs @@ -1,7 +1,15 @@ -#[derive(Clone, Debug, Default, Deserialize)] +#[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct GeneralConfig { pub prompt: bool, // Allow config to specify cookbook recipe or binary package as default pub repo_binary: Option<bool>, pub efi_partition_size: Option<u32>, //MiB } + +impl GeneralConfig { + pub(super) fn merge(&mut self, other: GeneralConfig) { + self.prompt = other.prompt; + self.repo_binary = other.repo_binary.or(self.repo_binary); + self.efi_partition_size = other.efi_partition_size.or(self.efi_partition_size); + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index 6172d4a86def8b185620a2a4100ba726982c16c2..e6ff206cb23f8184718157149486853f10d82957 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,12 +1,17 @@ use std::collections::BTreeMap; +use std::fs; +use std::mem; +use std::path::{Path, PathBuf}; -pub mod general; pub mod file; +pub mod general; pub mod package; pub mod user; -#[derive(Clone, Debug, Default, Deserialize)] +#[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct Config { + #[serde(default)] + pub include: Vec<PathBuf>, pub general: general::GeneralConfig, #[serde(default)] pub packages: BTreeMap<String, package::PackageConfig>, @@ -15,3 +20,60 @@ pub struct Config { #[serde(default)] pub users: BTreeMap<String, user::UserConfig>, } + +impl Config { + pub fn from_file(path: &Path) -> Result<Self, failure::Error> { + let mut config: Config = match fs::read_to_string(&path) { + Ok(config_data) => match toml::from_str(&config_data) { + Ok(config) => config, + Err(err) => { + return Err(format_err!("{}: failed to decode: {}", path.display(), err)); + } + }, + Err(err) => { + return Err(format_err!("{}: 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))) + .collect::<Result<Vec<Config>, failure::Error>>()?; + 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) + } + + 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, + } = 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); + } + } +} diff --git a/src/config/package.rs b/src/config/package.rs index f2df773caabb1ffd476b2be2840796e3c11e87e9..5f37196fe5d927d4fa9749e5fc04896b0639cf72 100644 --- a/src/config/package.rs +++ b/src/config/package.rs @@ -1,4 +1,4 @@ -#[derive(Clone, Debug, Deserialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] #[serde(untagged)] pub enum PackageConfig { Empty, diff --git a/src/config/user.rs b/src/config/user.rs index 4886f4f968cf89934749ab83285704ee486ba85b..3b823420481d0a835e0878bc51c06db8eebb73ca 100644 --- a/src/config/user.rs +++ b/src/config/user.rs @@ -1,4 +1,4 @@ -#[derive(Clone, Debug, Default, Deserialize)] +#[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct UserConfig { pub password: Option<String>, pub uid: Option<u32>, diff --git a/test.toml b/test.toml index 02376f01545c115f1500840df9c557401256ea8b..39c0082dcf598173f1ea3e4902896e2ebfddece5 100644 --- a/test.toml +++ b/test.toml @@ -137,11 +137,6 @@ path = "/usr/bin" data = "../bin" symlink = true -[[files]] -path = "/usr/games" -data = "../games" -symlink = true - [[files]] path = "/usr/include" data = "../include"