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.
This commit is contained in:
Tiffany Bennett 2024-05-10 18:37:38 -07:00 committed by GitHub
parent 07dc170897
commit 8cf08d2b3c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 53 additions and 29 deletions

View file

@ -223,24 +223,34 @@ impl Config {
}
}
fn read_from_search_path(filename: &str, paths: &[PathBuf]) -> Result<String> {
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<Vec<String>> {
let result: Vec<String> = 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();
Err(eyre!(
"Could not find {} in search path. Paths:{}",
filename,
paths
.iter()
.map(|path| format!("{}", path.display()))
.collect::<Vec<String>>()
.join("\n ")
))
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::<Vec<String>>()
.join("")
))
} else {
Ok(result)
}
}
fn load_live_currency(config: &Currency) -> Result<ast::Defs> {
@ -255,8 +265,10 @@ fn load_live_currency(config: &Currency) -> Result<ast::Defs> {
}
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<Config> {
/// Creates a context by searching standard directories
pub fn load(config: &Config) -> Result<Context> {
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<Context> {
}
// 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));

View file

@ -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`

View file

@ -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`