MIAnex/src/config.rs

181 lines
5.5 KiB
Rust

//! Handle config
pub use ini::Ini;
use std::collections::HashMap;
use std::path::PathBuf;
use crate::error::{Config as ConfigError, ConfigFormat as ConfigFormatError};
pub static RELATIVE_CONFIG_FILE: &str = "mia.ini";
/// List all sections of the config
pub struct Config {
pub general: General,
pub schema: Schema,
}
/// List all options of the 'General' section
pub struct General {
pub import_branch_prefix: String,
pub import_branch_schema: String,
pub main_branch: String,
}
/// List all options of the 'Schema' section
pub struct Schema {
pub default: String,
pub custom: HashMap<String, String>,
}
impl Config {
/// Initialize `Config` by reading it from a valid Ini config file at `config_path`.
pub fn from_file(config_path: &PathBuf) -> Result<Self, ConfigError> {
let ini_conf = Ini::load_from_file(&config_path).map_err(|source| match source {
ini::Error::Io(e) => ConfigError::ReadFile { source: e },
ini::Error::Parse(e) => ConfigError::from(e),
})?;
Config::from_ini(ini_conf)
}
/// Initialize `Config` from valid ini string `dump`.
pub fn from_string(dump: &str) -> Result<Self, ConfigError> {
let ini_conf = Ini::load_from_str(dump)?;
Config::from_ini(ini_conf)
}
/// Initialize `Config` from a `Ini` config.
fn from_ini(ini_conf: Ini) -> Result<Self, ConfigError> {
let mut schemas = HashMap::from_iter(Self::read_section(&ini_conf, "Schema")?);
let schema_default = schemas.remove("Default").ok_or(ConfigError::InvalidFormat(
ConfigFormatError::Custom {
message: format!("Field 'Default' is missing in section 'Schema'"),
},
))?;
Ok(Self {
general: General {
import_branch_prefix: Self::read_property(
&ini_conf,
"General",
"ImportBranchPrefix",
)?,
import_branch_schema: Self::read_property(
&ini_conf,
"General",
"ImportBranchSchema",
)?,
main_branch: Self::read_property(&ini_conf, "General", "MainBranch")?,
},
schema: Schema {
default: schema_default,
custom: schemas,
},
})
}
/// Dumps the default config to `file`.
/// `file` must not exists.
pub fn dump_default(file: &PathBuf) -> Result<(), ConfigError> {
assert!(file.is_absolute());
// TODO: Add flag to overwrite config in case it exists
if file.exists() {
return Err(ConfigError::WriteFile {
source: std::io::Error::new(
std::io::ErrorKind::Other,
format!(": File {} already exists", file.display()),
),
});
}
let def_config = Config::default();
let mut conf = Ini::new();
conf.with_section(Some("General"))
.set(
"ImportBranchPrefix",
def_config.general.import_branch_prefix,
)
.set(
"ImportBranchSchema",
def_config.general.import_branch_schema,
)
.set("MainBranch", def_config.general.main_branch);
conf.with_section(Some("Schema"))
.set("Default", def_config.schema.default)
.set(
"Event",
def_config
.schema
.custom
.get("Event")
.expect("Default trait is supposed to add an 'Event' schema"),
);
conf.write_to_file(file)
.map_err(|source| ConfigError::WriteFile { source })?;
Ok(())
}
/// Return value of key within section
fn read_property(file: &Ini, section: &str, key: &str) -> Result<String, ConfigFormatError> {
Ok(file
.section(Some(section))
.ok_or(ConfigFormatError::Custom {
message: format!("Section '{}' is missing", section),
})?
.get(key)
.ok_or(ConfigFormatError::Custom {
message: format!("Field '{}' is missing in section '{}'", key, section),
})?
.to_string())
}
/// Return (key, value) pair for given section
fn read_section(file: &Ini, section: &str) -> Result<Vec<(String, String)>, ConfigFormatError> {
Ok(file
.section(Some(section))
.ok_or(ConfigFormatError::Custom {
message: format!("Section '{}' is missing", section),
})?
.iter()
.map(|(s, k)| (s.to_string(), k.to_string()))
.collect())
}
}
impl Default for Config {
fn default() -> Self {
Self {
general: General::default(),
schema: Schema::default(),
}
}
}
impl Default for General {
fn default() -> Self {
Self {
import_branch_prefix: String::from("import/"),
import_branch_schema: String::from("*Sy*Sm*i*c"),
main_branch: String::from("main"),
}
}
}
impl Default for Schema {
fn default() -> Self {
let mut schema = HashMap::new();
schema.insert(
String::from("Event"),
String::from("%Y/*Sy*Sm*Sd-*Ey*Em*Ed*i/%y%m%d/%y%m%d_%H%M%S*c.*x"),
);
Self {
default: String::from("%Y/%y%m%d*i/%y%m%d_%H%M%S*c.*x"),
custom: schema,
}
}
}