From 8cf08d2b3cd2882a578d3f688454446a9332d9ab Mon Sep 17 00:00:00 2001 From: Tiffany Bennett Date: Fri, 10 May 2024 18:37:38 -0700 Subject: [PATCH] Custom definitions files (#172) Implements #79. The CLI will now load more than the first `definitions.units` file it finds. It will stick them together and load them as one big `definitions.units` file. This lets you easily add custom definitions by adding them to `~/.config/rink/definitions.units`. The format for it is already documented in the manpage. --- cli/src/config.rs | 70 +++++++++++++++++++++++++++--------------- docs/rink-dates.5.adoc | 2 +- docs/rink-defs.5.adoc | 10 ++++-- 3 files changed, 53 insertions(+), 29 deletions(-) diff --git a/cli/src/config.rs b/cli/src/config.rs index 4b267239..f95b2373 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -223,24 +223,34 @@ impl Config { } } -fn read_from_search_path(filename: &str, paths: &[PathBuf]) -> Result { - for path in paths { - let mut buf = PathBuf::from(path); - buf.push(filename); - if let Ok(result) = read_to_string(buf) { - return Ok(result); - } +fn read_from_search_path( + filename: &str, + paths: &[PathBuf], + default: Option<&'static str>, +) -> Result> { + let result: Vec = paths + .iter() + .filter_map(|path| { + let mut buf = PathBuf::from(path); + buf.push(filename); + read_to_string(buf).ok() + }) + .chain(default.map(ToOwned::to_owned)) + .collect(); + + if result.is_empty() { + Err(eyre!( + "Could not find {}, and rink was not built with one bundled. Search path:{}", + filename, + paths + .iter() + .map(|path| format!("\n {}", path.display())) + .collect::>() + .join("") + )) + } else { + Ok(result) } - - Err(eyre!( - "Could not find {} in search path. Paths:{}", - filename, - paths - .iter() - .map(|path| format!("{}", path.display())) - .collect::>() - .join("\n ") - )) } fn load_live_currency(config: &Currency) -> Result { @@ -255,8 +265,10 @@ fn load_live_currency(config: &Currency) -> Result { } fn try_load_currency(config: &Currency, ctx: &mut Context, search_path: &[PathBuf]) -> Result<()> { - let base = read_from_search_path("currency.units", search_path) - .or_else(|err| CURRENCY_FILE.map(ToOwned::to_owned).ok_or(err)).wrap_err("Rink was not built with a bundled currency.units file, and one was not found in the search path.")?; + let base = read_from_search_path("currency.units", search_path, CURRENCY_FILE)? + .into_iter() + .next() + .unwrap(); let mut base_defs = gnu_units::parse_str(&base); let mut live_defs = load_live_currency(config)?; @@ -283,7 +295,8 @@ pub fn read_config() -> Result { /// Creates a context by searching standard directories pub fn load(config: &Config) -> Result { let mut search_path = vec![PathBuf::from("./")]; - if let Some(config_dir) = dirs::config_dir() { + if let Some(mut config_dir) = dirs::config_dir() { + config_dir.push("rink"); search_path.push(config_dir); } if let Some(prefix) = option_env!("RINK_PATH") { @@ -291,16 +304,23 @@ pub fn load(config: &Config) -> Result { } // Read definitions.units - let units = read_from_search_path("definitions.units", &search_path) - .or_else(|err| DEFAULT_FILE.map(ToOwned::to_owned).ok_or(err).wrap_err("Rink was not built with a bundled definitions.units file, and one was not found in the search path."))?; + let units_files = read_from_search_path("definitions.units", &search_path, DEFAULT_FILE)?; + + let unit_defs: Vec<_> = units_files + .iter() + .map(|s| gnu_units::parse_str(s).defs) + .flatten() + .collect(); // Read datepatterns.txt - let dates = read_from_search_path("datepatterns.txt", &search_path) - .or_else(|err| DATES_FILE.map(ToOwned::to_owned).ok_or(err).wrap_err("Rink was not built with a bundled datepatterns.txt file, and one was not found in the search path."))?; + let dates = read_from_search_path("datepatterns.txt", &search_path, DATES_FILE)? + .into_iter() + .next() + .unwrap(); let mut ctx = Context::new(); ctx.save_previous_result = true; - ctx.load(gnu_units::parse_str(&units)) + ctx.load(rink_core::ast::Defs { defs: unit_defs }) .map_err(|err| eyre!(err))?; ctx.load_dates(datetime::parse_datefile(&dates)); diff --git a/docs/rink-dates.5.adoc b/docs/rink-dates.5.adoc index bf719e23..bff167a6 100644 --- a/docs/rink-dates.5.adoc +++ b/docs/rink-dates.5.adoc @@ -111,7 +111,7 @@ Files Rink searches the following locations: -* `./rink/datepatterns.txt` +* `./datepatterns.txt` * `__$XDG_CONFIG_DIR__/rink/datepatterns.txt` * `/usr/share/rink/datepatterns.txt` diff --git a/docs/rink-defs.5.adoc b/docs/rink-defs.5.adoc index a2562748..8871acc3 100644 --- a/docs/rink-defs.5.adoc +++ b/docs/rink-defs.5.adoc @@ -248,16 +248,20 @@ decimal point. Files ----- -Rink searches for the definitions file in these locations: +Rink searches for definitions files in these locations: -* `./rink/definitions.units` +* `./definitions.units` * `$XDG_CONFIG_DIR/rink/definitions.units` * `/usr/share/rink/definitions.units` +It will load all of the files it finds. The files can reference +definitions from each other. This can be used for custom definitions +building on top of the default units database. + When live currency fetching is enabled, Rink also looks for a currency file in these locations: -* `./rink/currency.units` +* `./currency.units` * `$XDG_CONFIG_DIR/rink/currency.units` * `/usr/share/rink/currency.units`