From e5f875903eb45f3781f17005d1b1af269c934718 Mon Sep 17 00:00:00 2001 From: asuper0 <41113804+asuper0@users.noreply.github.com> Date: Tue, 18 Jun 2024 09:27:29 +0800 Subject: [PATCH] Adding fn load_or_else (#98) Co-authored-by: chenren --- src/lib.rs | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 8c8afd7..b690772 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -234,6 +234,61 @@ pub fn load_path( } } +/// Load an application configuration from a specified path. +/// +/// A new configuration file is created with `op`'s result if none +/// exists or file content is incorrect. +/// +/// This is an alternate version of [`load`] that allows the specification of +/// an arbitrary path instead of a system one. For more information on errors +/// and behavior, see [`load`]'s documentation. +/// +/// [`load`]: fn.load.html +pub fn load_or_else(path: impl AsRef, op: F) -> Result +where + T: DeserializeOwned + Serialize, + F: FnOnce() -> T, +{ + let path_ref = path.as_ref(); + let load_value = || { + let cfg = op(); + if let Some(parent) = path.as_ref().parent() { + fs::create_dir_all(parent).map_err(ConfyError::DirectoryCreationFailed)?; + } + store_path(path_ref, &cfg)?; + Ok(cfg) + }; + + match File::open(path_ref) { + Ok(mut cfg) => { + let mut load_from_file = || { + let cfg_string = cfg + .get_string() + .map_err(ConfyError::ReadConfigurationFileError)?; + + #[cfg(feature = "toml_conf")] + { + let cfg_data = toml::from_str(&cfg_string); + cfg_data.map_err(ConfyError::BadTomlData) + } + #[cfg(feature = "yaml_conf")] + { + let cfg_data = serde_yaml::from_str(&cfg_string); + cfg_data.map_err(ConfyError::BadYamlData) + } + #[cfg(feature = "ron_conf")] + { + let cfg_data = ron::from_str(&cfg_string); + cfg_data.map_err(ConfyError::BadRonData) + } + }; + load_from_file().or_else(|_| load_value()) + } + Err(ref e) if e.kind() == NotFound => load_value(), + Err(e) => Err(ConfyError::GeneralLoadError(e)), + } +} + /// Save changes made to a configuration object /// /// This function will update a configuration, @@ -423,6 +478,36 @@ mod tests { }) } + /// [`load_or_else`] loads [`ExampleConfig`]. + #[test] + fn load_or_else_works() { + with_config_path(|path| { + let the_value = || ExampleConfig { + name: "a".to_string(), + count: 5, + }; + + let config: ExampleConfig = load_or_else(path, the_value).expect("load_or_else failed"); + assert_eq!(config, the_value()); + }); + + with_config_path(|path| { + fs::create_dir_all(path.parent().unwrap()).unwrap(); + let mut file = File::create(path).expect("creating file failed"); + file.write("some normal text".as_bytes()) + .expect("write to file failed"); + drop(file); + + let the_value = || ExampleConfig { + name: "a".to_string(), + count: 5, + }; + + let config: ExampleConfig = load_or_else(path, the_value).expect("load_or_else failed"); + assert_eq!(config, the_value()); + }) + } + /// [`store_path`] stores [`ExampleConfig`]. #[test] fn test_store_path() {