diff --git a/examples/simple.rs b/examples/simple.rs index 1e8f9cf..d4504de 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -25,8 +25,8 @@ impl Default for ConfyConfig { } fn main() -> Result<(), confy::ConfyError> { - let cfg: ConfyConfig = confy::load("confy_simple_app", None)?; - let file = confy::get_configuration_file_path("confy_simple_app", None)?; + let cfg: ConfyConfig = confy::load::("confy_simple_app", None)?; + let file = confy::get_configuration_file_path::("confy_simple_app", None)?; println!("The configuration file path is: {:#?}", file); println!("The configuration is:"); println!("{:#?}", cfg); @@ -41,7 +41,7 @@ fn main() -> Result<(), confy::ConfyError> { name: "Test".to_string(), ..cfg }; - confy::store("confy_simple_app",None, &cfg)?; + confy::store::("confy_simple_app",None, &cfg)?; println!("The updated toml file content is:"); let mut content = String::new(); std::fs::File::open(&file) diff --git a/src/lib.rs b/src/lib.rs index 0717c77..75a0ec3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,19 +79,60 @@ confy. Please enable one of either the `toml_conf` or `yaml_conf` \ features." ); -#[cfg(all(feature = "toml_conf", feature = "yaml_conf"))] -compile_error!( - "Exactly one config language feature must be enabled to compile \ -confy. Please disable one of either the `toml_conf` or `yaml_conf` features. \ -NOTE: `toml_conf` is a default feature, so disabling it might mean switching off \ -default features for confy in your Cargo.toml" -); +pub trait Format { + fn extension() -> &'static str; + + fn deserialize(cfg_string: S) -> Result + where T: Serialize + DeserializeOwned + Default, + S: AsRef; +} #[cfg(feature = "toml_conf")] -const EXTENSION: &str = "toml"; +mod format_toml { + use serde::{de::DeserializeOwned, Serialize}; + use crate::ConfyError; + use crate::Format; + + pub struct Toml; + impl Format for Toml { + fn extension() -> &'static str { + "toml" + } + + fn deserialize(cfg_string: S) -> Result + where T: Serialize + DeserializeOwned + Default, + S: AsRef + { + toml::from_str(cfg_string.as_ref()).map_err(ConfyError::BadTomlData) + } + } +} +#[cfg(feature = "toml_conf")] +pub use crate::format_toml::*; #[cfg(feature = "yaml_conf")] -const EXTENSION: &str = "yml"; +mod format_yaml { + use serde::{de::DeserializeOwned, Serialize}; + use crate::ConfyError; + use crate::Format; + + pub struct Yaml; + impl Format for Yaml { + fn extension() -> &'static str { + "yml" + } + + fn deserialize(cfg_string: S) -> Result + where T: Serialize + DeserializeOwned + Default, + S: AsRef + { + serde_yaml::from_str(cfg_string.as_ref()).map_err(ConfyError::BadYamlData) + } + } +} +#[cfg(feature = "yaml_conf")] +pub use crate::format_yaml::*; + /// The errors the confy crate can encounter. #[derive(Debug)] @@ -181,13 +222,12 @@ impl Error for ConfyError {} /// # Ok(()) /// # } /// ``` -pub fn load<'a, T: Serialize + DeserializeOwned + Default>( - app_name: &str, - config_name: impl Into>, -) -> Result { - let path = get_configuration_file_path(app_name, config_name)?; - - load_path(path) +pub fn load<'a, F, T, C>(app_name: &str, config_name: C) -> Result + where F: Format, + T: Serialize + DeserializeOwned + Default, + C: Into> +{ + get_configuration_file_path::(app_name, config_name).and_then(|p| load_path::(p)) } /// Load an application configuration from a specified path. @@ -200,25 +240,16 @@ pub fn load<'a, T: Serialize + DeserializeOwned + Default>( /// and behavior, see [`load`]'s documentation. /// /// [`load`]: fn.load.html -pub fn load_path( - path: impl AsRef, -) -> Result { +pub fn load_path(path: P) -> Result + where F: Format, + T: Serialize + DeserializeOwned + Default, + P: AsRef +{ match File::open(&path) { Ok(mut cfg) => { - 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.get_string() + .map_err(ConfyError::ReadConfigurationFileError) + .and_then(F::deserialize) } Err(ref e) if e.kind() == NotFound => { if let Some(parent) = path.as_ref().parent() { @@ -260,12 +291,12 @@ pub fn load_path( /// able to write the configuration file or if `confy` /// encounters an operating system or environment it does /// not support. -pub fn store<'a, T: Serialize>( - app_name: &str, - config_name: impl Into>, - cfg: T, -) -> Result<(), ConfyError> { - let path = get_configuration_file_path(app_name, config_name)?; +pub fn store<'a, F, T, C>(app_name: &str, config_name: C, cfg: T) -> Result<(), ConfyError> + where F: Format, + T: Serialize, + C: Into>, +{ + let path = get_configuration_file_path::(app_name, config_name)?; fs::create_dir_all(&path.parent().unwrap()).map_err(ConfyError::DirectoryCreationFailed)?; store_path(path, cfg) @@ -307,16 +338,16 @@ pub fn store_path(path: impl AsRef, cfg: T) -> Result<(), Co /// /// [`load`]: fn.load.html /// [`store`]: fn.store.html -pub fn get_configuration_file_path<'a>( - app_name: &str, - config_name: impl Into>, -) -> Result { +pub fn get_configuration_file_path<'a, F, C>(app_name: &str, config_name: C) -> Result + where F: Format, + C: Into> +{ let config_name = config_name.into().unwrap_or("default-config"); let project = ProjectDirs::from("rs", "", app_name).ok_or(ConfyError::BadConfigDirectoryStr)?; let config_dir_str = get_configuration_directory_str(&project)?; - let path = [config_dir_str, &format!("{}.{}", config_name, EXTENSION)] + let path = [config_dir_str, &format!("{}.{}", config_name, F::extension())] .iter() .collect();