From adfee954840aa84b1e1a5605f0b5cf727bed324e Mon Sep 17 00:00:00 2001
From: bjorn3 <17426603+bjorn3@users.noreply.github.com>
Date: Thu, 4 Jan 2024 15:08:49 +0100
Subject: [PATCH] Support including other config files from a config file

This will allow significantly reducing the amount of duplication
between all the different configs used by Redox OS.
---
 src/config/general.rs |  8 ++++++++
 src/config/mod.rs     | 47 +++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 53 insertions(+), 2 deletions(-)

diff --git a/src/config/general.rs b/src/config/general.rs
index 1ef4807..7ed6c33 100644
--- a/src/config/general.rs
+++ b/src/config/general.rs
@@ -5,3 +5,11 @@ pub struct GeneralConfig {
     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 712803b..e6ff206 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -1,6 +1,7 @@
 use std::collections::BTreeMap;
 use std::fs;
-use std::path::Path;
+use std::mem;
+use std::path::{Path, PathBuf};
 
 pub mod file;
 pub mod general;
@@ -9,6 +10,8 @@ pub mod user;
 
 #[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>,
@@ -20,7 +23,7 @@ pub struct Config {
 
 impl Config {
     pub fn from_file(path: &Path) -> Result<Self, failure::Error> {
-        let config = match fs::read_to_string(&path) {
+        let mut config: Config = match fs::read_to_string(&path) {
             Ok(config_data) => match toml::from_str(&config_data) {
                 Ok(config) => config,
                 Err(err) => {
@@ -31,6 +34,46 @@ impl Config {
                 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);
+        }
+    }
 }
-- 
GitLab