From fd14e9295a04cb051a70779fd6039d2d25ce9b85 Mon Sep 17 00:00:00 2001
From: licunlong <licunlong1@huawei.com>
Date: Mon, 3 Jul 2023 15:39:47 +0800
Subject: [PATCH] refactor: don't copy unit file to .toml file when loading a
 unit

We used to do this because we thought confique could only infer
the file format from the file extension, so we copied the file
to .toml to tell confique this is a toml file. But actually,
confique allows us to specify the file format if we use Partial.

Let's use confique::Partial to avoid copying files.
---
 coms/service/src/config.rs      | 24 ++++++++++++--------
 core/bin/manager/pre_install.rs | 40 +++++++++++++++++----------------
 core/bin/unit/entry/config.rs   | 25 +++++++++++++--------
 core/bin/unit/util/unit_file.rs | 17 +++-----------
 5 files changed, 69 insertions(+), 57 deletions(-)

diff --git a/coms/service/src/config.rs b/coms/service/src/config.rs
index e698ac2..3b7eb43 100644
--- a/coms/service/src/config.rs
+++ b/coms/service/src/config.rs
@@ -13,7 +13,7 @@
 #![allow(non_snake_case)]
 use super::comm::ServiceUnitComm;
 use super::rentry::{NotifyAccess, SectionService, ServiceCommand, ServiceType};
-use confique::Config;
+use confique::{Config, FileFormat, Partial};
 use std::cell::RefCell;
 use std::collections::{HashMap, VecDeque};
 use std::path::PathBuf;
@@ -64,15 +64,21 @@ impl ServiceConfig {
     }
 
     pub(super) fn load(&self, paths: Vec<PathBuf>, update: bool) -> Result<()> {
-        let mut builder = ServiceConfigData::builder().env();
-
-        log::debug!("service load path: {:?}", paths);
-        // fragment
-        for v in paths {
-            builder = builder.file(v);
+        type ConfigPartial = <ServiceConfigData as Config>::Partial;
+        let mut partial: ConfigPartial = Partial::from_env().context(ConfiqueSnafu)?;
+        /* The first config wins, so add default values at last. */
+        log::debug!("Loading service config from: {:?}", paths);
+        for path in paths {
+            partial = match confique::File::with_format(&path, FileFormat::Toml).load() {
+                Err(e) => {
+                    log::error!("Failed to load {path:?}: {e}, skipping");
+                    continue;
+                }
+                Ok(v) => partial.with_fallback(v),
+            }
         }
-
-        *self.data.borrow_mut() = match builder.load() {
+        partial = partial.with_fallback(ConfigPartial::default_values());
+        *self.data.borrow_mut() = match ServiceConfigData::from_partial(partial) {
             Err(e) => {
                 /* The error message is pretty readable, just print it out. */
                 log::error!("{e}");
diff --git a/core/bin/manager/pre_install.rs b/core/bin/manager/pre_install.rs
index 88150c0..08c795a 100644
--- a/core/bin/manager/pre_install.rs
+++ b/core/bin/manager/pre_install.rs
@@ -15,7 +15,7 @@ use crate::unit::{unit_name_to_type, UeConfigInstall, UnitType};
 use basic::fs_util;
 use basic::path_lookup::LookupPaths;
 use bitflags::bitflags;
-use confique::Config;
+use confique::{Config, FileFormat, Partial};
 use nix::unistd::UnlinkatFlags;
 use std::{
     cell::RefCell,
@@ -553,14 +553,14 @@ impl Install {
         }
 
         let canon_path = path.canonicalize()?;
-        let tmp = format!("{}.toml", canon_path.as_path().display());
-        if let Err(e) = std::fs::copy(canon_path, &tmp).context(IoSnafu) {
-            log::warn!("copy file content to toml file error: {}", e);
-            return Err(e);
-        }
-
-        let mut builder = UeConfigData::builder().env();
-        builder = builder.file(&tmp);
+        type ConfigPartial = <UeConfigData as Config>::Partial;
+        let mut partial: ConfigPartial = Partial::from_env().context(ConfiqueSnafu)?;
+        /* The first config wins, so add default values at last. */
+        partial = partial.with_fallback(
+            confique::File::with_format(canon_path, FileFormat::Toml)
+                .load()
+                .context(ConfiqueSnafu)?,
+        );
 
         let dropin_dir_name = format!("{}.d", unit_install.name());
 
@@ -570,7 +570,7 @@ impl Install {
             let dropin_dir = base_dir.join(&dropin_dir_name);
 
             if !dropin_dir.exists() {
-                log::debug!("dropin path is not exist, ignore it: {:?}", &dropin_dir);
+                log::debug!("Dropin path {dropin_dir:?} does not exist, ignoring");
                 continue;
             }
 
@@ -578,24 +578,26 @@ impl Install {
             for entry in dirs {
                 let dir_entry = entry?;
                 let fragment = dir_entry.path();
-                if fragment.is_file() {
-                    let file_name = String::from(fragment.file_name().unwrap().to_str().unwrap());
-                    if file_name.starts_with('.') || !file_name.ends_with(".toml") {
+                if !fragment.is_file() {
+                    log::debug!("Fragment file {fragment:?} is not a file, ignoring");
+                    continue;
+                }
+                partial = match confique::File::with_format(&fragment, FileFormat::Toml).load() {
+                    Err(e) => {
+                        log::error!("Failed to load {fragment:?}: {e}, skipping.");
                         continue;
                     }
-
-                    builder = builder.file(fragment);
-                }
+                    Ok(v) => partial.with_fallback(v),
+                };
             }
         }
-
-        let configer = builder.load().context(ConfiqueSnafu)?;
+        partial = partial.with_fallback(ConfigPartial::default_values());
+        let configer = UeConfigData::from_partial(partial).context(ConfiqueSnafu)?;
         unit_install.fill_struct(&configer);
 
         for also in &configer.Install.Also {
             self.unit_install_discover(also, ctx.clone())?;
         }
-        // fs::remove_file(&tmp);
 
         Ok(())
     }
diff --git a/core/bin/unit/entry/config.rs b/core/bin/unit/entry/config.rs
index 705512d..91313c4 100644
--- a/core/bin/unit/entry/config.rs
+++ b/core/bin/unit/entry/config.rs
@@ -14,7 +14,7 @@
 use super::base::UeBase;
 use crate::unit::rentry::{UeConfigInstall, UeConfigUnit};
 use crate::unit::util::UnitFile;
-use confique::Config;
+use confique::{Config, FileFormat, Partial};
 use serde::{Deserialize, Deserializer, Serialize};
 use std::cell::RefCell;
 use std::rc::Rc;
@@ -132,21 +132,28 @@ impl UeConfig {
     }
 
     pub(super) fn load_fragment_and_dropin(&self, files: &UnitFile, name: &String) -> Result<()> {
-        let mut builder = UeConfigData::builder().env();
-
+        type ConfigPartial = <UeConfigData as Config>::Partial;
+        let mut partial: ConfigPartial = Partial::from_env().context(ConfiqueSnafu)?;
+        /* The first config wins, so add default values at last. */
         let unit_conf_frag = files.get_unit_id_fragment_pathbuf(name);
         if unit_conf_frag.is_empty() {
             return Err(format!("{name} doesn't have corresponding config file").into());
         }
         // fragment
-        for v in unit_conf_frag {
-            if !v.exists() {
-                return Err(format!("Config file {:?} of {name} doesn't exist", v).into());
+        for path in unit_conf_frag {
+            if !path.exists() {
+                return Err(format!("Config file {:?} of {name} doesn't exist", path).into());
             }
-            builder = builder.file(&v);
+            partial = match confique::File::with_format(&path, FileFormat::Toml).load() {
+                Err(e) => {
+                    log::error!("Failed to load {path:?}: {e}, skipping");
+                    continue;
+                }
+                Ok(v) => partial.with_fallback(v),
+            };
         }
-
-        let mut configer = builder.load().context(ConfiqueSnafu)?;
+        partial = partial.with_fallback(ConfigPartial::default_values());
+        let mut configer = UeConfigData::from_partial(partial).context(ConfiqueSnafu)?;
 
         // dropin
         for v in files.get_unit_wants_symlink_units(name) {
diff --git a/core/bin/unit/util/unit_file.rs b/core/bin/unit/util/unit_file.rs
index 0a0fa9f..eb9404b 100644
--- a/core/bin/unit/util/unit_file.rs
+++ b/core/bin/unit/util/unit_file.rs
@@ -118,12 +118,7 @@ impl UnitFileData {
                 if file_name.starts_with('.') || file_name.ends_with(".toml") {
                     continue;
                 }
-                let path = format!("{}.toml", fragment.to_string_lossy());
-
-                if let Err(e) = std::fs::copy(fragment, &path) {
-                    log::warn!("copy file content to toml file error: {}", e);
-                }
-                res.push(Path::new(&path).to_path_buf());
+                res.push(fragment);
             }
         }
         /* {/etc/sysmater, /usr/lib/sysmaster}/foo.service */
@@ -135,14 +130,8 @@ impl UnitFileData {
         if config_path.is_symlink() {
             return None;
         }
-        /* Add .toml to the original path name */
-        let path_toml = format!("{}.toml", config_path.to_string_lossy());
-        let to = Path::new(&path_toml);
-        if let Err(e) = std::fs::copy(config_path, to) {
-            log::warn!("copy file content to toml file error: {}", e);
-            return None;
-        }
-        res.push(to.to_path_buf());
+
+        res.push(config_path);
         Some(res)
     }
 
-- 
2.33.0

