From 227746537b79cfcd0ad76f0129cb52703b7f03b9 Mon Sep 17 00:00:00 2001 From: "Francisco J. Sanchez" Date: Thu, 30 Nov 2023 03:34:17 +0100 Subject: [PATCH] make units.toml configuration more intuitive --- CHANGELOG.md | 9 + src/convert/builder.rs | 354 ++++++++++++++++++++------------------ src/convert/mod.rs | 62 ++++--- src/convert/units_file.rs | 83 ++++++--- units.toml | 8 +- units/spanish.toml | 38 ++-- 6 files changed, 312 insertions(+), 242 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c92df29..6a3a0d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ - Reworked error model. - Removed `Ingredient::total_quantity`. - Change `Cookware::group_amounts` return type. +- Several changes in UnitsFile: + - System is no longer set when declaring a unit with an unspecified system as best of a specific system. + - `extend.names`, `extend.aliases` and `extend.symbols` are now combined in `extend.units`. ### Features - New warning for bad single word names. It could be confusing not getting any @@ -34,6 +37,12 @@ - Added `ScaledRecipe::group_cookware`. - Rework `GroupedQuantity` API and add `GroupedValue`. - Ignored ingredients in text mode are now added as text. +- Several features in UnitsFile to make it more intuitive: + - The best unit of a system can now be from any system. It's up to the user if + they want to mix them. + - New `extend.units`, which allows to edit the conversions. + - Improve and actually make usable the fractions configuration. Now with an + `all` and `quantity.` options. ### Fixed - Text steps were ignored in `components` mode. diff --git a/src/convert/builder.rs b/src/convert/builder.rs index bf0b1d3..7c85736 100644 --- a/src/convert/builder.rs +++ b/src/convert/builder.rs @@ -1,17 +1,11 @@ -use std::{ - collections::{HashMap, HashSet}, - sync::Arc, -}; +use std::{collections::HashMap, sync::Arc}; use enum_map::{enum_map, EnumMap}; use thiserror::Error; use super::{ convert_f64, - units_file::{ - self, BestUnits, Extend, FractionsConfigHelper, Precedence, SIPrefix, UnitEntry, Units, - UnitsFile, SI, - }, + units_file::{self, BestUnits, Extend, Precedence, SIPrefix, UnitEntry, Units, UnitsFile, SI}, BestConversions, BestConversionsStore, Converter, Fractions, PhysicalQuantity, System, Unit, UnitIndex, UnknownUnit, }; @@ -23,7 +17,7 @@ use super::{ /// another added before, or be overwritten by others after. #[derive(Debug, Default)] pub struct ConverterBuilder { - all_units: Vec, + all_units: Vec, unit_index: UnitIndex, extend: Vec, si: SI, @@ -32,6 +26,28 @@ pub struct ConverterBuilder { default_system: System, } +#[derive(Debug)] +struct UnitBuilder { + unit: Unit, + is_expanded: bool, + expand_si: bool, + expanded_units: Option>, +} + +impl std::ops::Deref for UnitBuilder { + type Target = Unit; + + fn deref(&self) -> &Self::Target { + &self.unit + } +} + +impl std::ops::DerefMut for UnitBuilder { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.unit + } +} + impl ConverterBuilder { /// New empty builder pub fn new() -> Self { @@ -68,19 +84,22 @@ impl ConverterBuilder { // Add all units to an index let mut add_units = |units: Vec, system| -> Result<(), ConverterBuilderError> { - for unit in units { + for entry in units { let unit = Unit { - names: unit.names, - symbols: unit.symbols, - aliases: unit.aliases, - ratio: unit.ratio, - difference: unit.difference, + names: entry.names, + symbols: entry.symbols, + aliases: entry.aliases, + ratio: entry.ratio, + difference: entry.difference, physical_quantity: group.quantity, - expand_si: unit.expand_si, - expanded_units: None, system, }; - let _id = self.add_unit(unit)?; + let _id = self.add_unit(UnitBuilder { + unit, + is_expanded: false, + expand_si: entry.expand_si, + expanded_units: None, + })?; } Ok(()) }; @@ -156,51 +175,16 @@ impl ConverterBuilder { } } - // apply the extend groups - for extend_group in self.extend { - let mut to_update = HashSet::new(); - - let Extend { - precedence, - names, - symbols, - aliases, - } = extend_group; - - for (k, aliases) in aliases { - let id = self.unit_index.get_unit_id(k.as_str())?; - self.unit_index.add_unit_keys(id, aliases.iter().cloned())?; - join_alias_vec(&mut self.all_units[id].aliases, aliases, precedence); - } - - for (k, names) in names { - let id = self.unit_index.get_unit_id(k.as_str())?; - self.unit_index.add_unit_keys(id, names.iter().cloned())?; - join_alias_vec(&mut self.all_units[id].names, names, precedence); - if self.all_units[id].expand_si { - to_update.insert(id); - } - } - - for (k, symbols) in symbols { - let id = self.unit_index.get_unit_id(k.as_str())?; - self.unit_index.add_unit_keys(id, symbols.iter().cloned())?; - join_alias_vec(&mut self.all_units[id].symbols, symbols, precedence); - if self.all_units[id].expand_si { - to_update.insert(id); - } - } - // update expansions of the modified units at the end of each group - // so updates from prior ones are available to the next. - for id in to_update { - update_expanded_units(&mut self.unit_index, &mut self.all_units, &self.si, id)?; - } - } + apply_extend_groups( + self.extend, + &mut self.all_units, + &mut self.unit_index, + &self.si, + )?; let best = enum_map! { q => { if let Some(best_units) = &self.best_units[q] { - unify_best_units_systems(best_units, &self.unit_index, &mut self.all_units)?; BestConversionsStore::new(best_units, &self.unit_index, &self.all_units)? } else { return Err(ConverterBuilderError::EmptyBest { reason: "no best units given", quantity: q }) @@ -216,38 +200,14 @@ impl ConverterBuilder { index }; - let mut specialization = HashMap::new(); - let mut metric = FractionsConfigHelper::default(); - let mut imperial = FractionsConfigHelper::default(); - for cfg in self.fractions.iter() { - if let Some(cfg) = cfg.metric { - metric = cfg.get(); - } - if let Some(cfg) = cfg.imperial { - imperial = cfg.get(); - } - } - for cfg in self.fractions.iter() { - for (key, cfg) in &cfg.specialization { - let cfg = cfg.get(); - let unit_id = self.unit_index.get_unit_id(key)?; - let unit = &self.all_units[unit_id]; - let cfg = match unit.system { - Some(System::Metric) => cfg.merge(metric), - Some(System::Imperial) => cfg.merge(imperial), - None => cfg, - }; - specialization.insert(unit_id, cfg.define()); - } - } - let fractions = Fractions { - metric: metric.define(), - imperial: imperial.define(), - specialization, - }; + let fractions = build_fractions_config(&self.fractions, &self.unit_index, &self.all_units)?; Ok(Converter { - all_units: self.all_units.into_iter().map(Arc::new).collect(), + all_units: self + .all_units + .into_iter() + .map(|u| Arc::new(u.unit)) + .collect(), unit_index: self.unit_index, quantity_index, best, @@ -257,7 +217,7 @@ impl ConverterBuilder { }) } - fn add_unit(&mut self, unit: Unit) -> Result { + fn add_unit(&mut self, unit: UnitBuilder) -> Result { let id = self.all_units.len(); self.unit_index.add_unit(&unit, id)?; self.all_units.push(unit); @@ -269,7 +229,7 @@ impl BestConversionsStore { fn new( best_units: &BestUnits, unit_index: &UnitIndex, - all_units: &[Unit], + all_units: &[UnitBuilder], ) -> Result { let v = match best_units { BestUnits::Unified(names) => { @@ -288,7 +248,7 @@ impl BestConversions { fn new( units: &[String], unit_index: &UnitIndex, - all_units: &[Unit], + all_units: &[UnitBuilder], ) -> Result { let mut units = units .iter() @@ -318,55 +278,73 @@ impl BestConversions { } } -fn unify_best_units_systems( - best_units: &BestUnits, - unit_index: &UnitIndex, - all_units: &mut [Unit], +fn apply_extend_groups( + extend: Vec, + all_units: &mut [UnitBuilder], + unit_index: &mut UnitIndex, + si: &SI, ) -> Result<(), ConverterBuilderError> { - match best_units { - BestUnits::Unified(_) => {} - BestUnits::BySystem { metric, imperial } => { - unify_units_systems(metric, System::Metric, unit_index, all_units)?; - unify_units_systems(imperial, System::Imperial, unit_index, all_units)?; + for extend_group in extend { + let Extend { precedence, units } = extend_group; + + let mut to_update = Vec::with_capacity(units.len()); + + // First resolve keys with current config + for (k, entry) in units { + let id = unit_index.get_unit_id(k.as_str())?; + if to_update.iter().any(|&(eid, _)| eid == id) { + return Err(ConverterBuilderError::DuplicateExtendUnit { key: k }); + } + if all_units[id].is_expanded + && (entry.ratio.is_some() + || entry.difference.is_some() + || entry.names.is_some() + || entry.symbols.is_some()) + { + return Err(ConverterBuilderError::InvalidExtendExpanded { key: k }); + } + to_update.push((id, entry)); } - } - Ok(()) -} -/// Checks that the best units refs are of the given system. -/// If the unit has no system, set it to the given one. -fn unify_units_systems( - units: &[String], - system: System, - unit_index: &UnitIndex, - all_units: &mut [Unit], -) -> Result<(), ConverterBuilderError> { - for unit in units { - let unit_id = unit_index.get_unit_id(unit)?; - match all_units[unit_id].system { - Some(unit_system) => { - if system != unit_system { - return Err(ConverterBuilderError::IncorrectUnitSystem { - unit: all_units[unit_id].clone().into(), - contained: system, - got: unit_system, - }); - } + // Then apply updates + for (id, entry) in to_update { + // remove all entries from the unit and expansions from the index + unit_index.remove_unit_rec(all_units, &all_units[id]); + let unit = &mut all_units[id]; + + // edit the unit + if let Some(ratio) = entry.ratio { + unit.ratio = ratio; } - None => all_units[unit_id].system = Some(system), + if let Some(difference) = entry.difference { + unit.difference = difference; + } + if let Some(names) = entry.names { + join_alias_vec(&mut unit.names, names, precedence); + } + if let Some(symbols) = entry.symbols { + join_alias_vec(&mut unit.symbols, symbols, precedence); + } + if let Some(aliases) = entry.aliases { + join_alias_vec(&mut unit.aliases, aliases, precedence); + } + + // (re)add the new entries to the index + if all_units[id].expand_si { + update_expanded_units(id, all_units, unit_index, si)?; + } + unit_index.add_unit(&all_units[id], id)?; } } Ok(()) } fn update_expanded_units( + id: usize, + all_units: &mut [UnitBuilder], unit_index: &mut UnitIndex, - all_units: &mut [Unit], si: &SI, - id: usize, ) -> Result<(), ConverterBuilderError> { - // remove all entries from the unit and expansions from the index - unit_index.remove_unit_rec(all_units, &all_units[id]); // update the expanded units let new_units = expand_si(&all_units[id], si)?; for (prefix, expanded_unit) in new_units.into_iter() { @@ -376,26 +354,78 @@ fn update_expanded_units( all_units[expanded_id].aliases = old_unit_aliases; unit_index.add_unit(&all_units[expanded_id], expanded_id)?; } - // (re)add the new entries to the index - unit_index.add_unit(&all_units[id], id)?; Ok(()) } -fn join_alias_vec>>( - target: &mut Vec>, - src: I, - src_precedence: Precedence, -) { +fn build_fractions_config( + fractions: &[units_file::Fractions], + unit_index: &UnitIndex, + all_units: &[UnitBuilder], +) -> Result { + let mut all = None; + + for cfg in fractions.iter() { + all = cfg.all.map(|c| c.get()); + } + + let mut metric = None; + let mut imperial = None; + let mut quantity = HashMap::new(); + + for cfg in fractions.iter() { + metric = cfg.metric.map(|c| c.get()); + imperial = cfg.imperial.map(|c| c.get()); + for (q, cfg) in &cfg.quantity { + quantity.insert(*q, cfg.get()); + } + } + + let mut unit = HashMap::new(); + for cfg in fractions.iter() { + for (key, cfg) in &cfg.unit { + let unit_id = unit_index.get_unit_id(key)?; + let u = &all_units[unit_id]; + + let inherit = [ + quantity.get(&u.physical_quantity), + u.system.and_then(|s| match s { + System::Metric => metric.as_ref(), + System::Imperial => imperial.as_ref(), + }), + all.as_ref(), + ] + .into_iter() + .flatten() + .copied() + .reduce(|acc, e| acc.merge(e)); + + let mut cfg = cfg.get(); + if let Some(inherit) = inherit { + cfg = cfg.merge(inherit) + } + unit.insert(unit_id, cfg.define()); + } + } + Ok(Fractions { + all: all.map(|c| c.define()), + metric: metric.map(|c| c.define()), + imperial: imperial.map(|c| c.define()), + quantity: quantity.into_iter().map(|(q, c)| (q, c.define())).collect(), + unit, + }) +} + +fn join_alias_vec(target: &mut Vec>, mut src: Vec>, src_precedence: Precedence) { match src_precedence { Precedence::Before => { - target.splice(0..0, src); + src.append(target); + *target = src; } Precedence::After => { - target.extend(src); + target.append(&mut src); } Precedence::Override => { - target.clear(); - target.extend(src); + *target = src; } } } @@ -423,7 +453,11 @@ fn join_prefixes( } } -fn expand_si(unit: &Unit, si: &SI) -> Result, ConverterBuilderError> { +fn expand_si( + unit: &UnitBuilder, + si: &SI, +) -> Result, ConverterBuilderError> { + assert!(unit.expand_si); let (Some(prefixes), Some(symbol_prefixes)) = (&si.prefixes, &si.symbol_prefixes) else { return Err(ConverterBuilderError::EmptySIPrefixes); }; @@ -440,6 +474,9 @@ fn expand_si(unit: &Unit, si: &SI) -> Result, ConverterB .flat_map(|p| unit.symbols.iter().map(move |n| format!("{p}{n}").into())) .collect(); + UnitBuilder { + unit: + Unit { names, symbols, @@ -447,10 +484,11 @@ fn expand_si(unit: &Unit, si: &SI) -> Result, ConverterB ratio: unit.ratio * prefix.ratio(), difference: unit.difference, physical_quantity: unit.physical_quantity, - expand_si: false, - expanded_units: None, system: unit.system, - } + }, expand_si: false, + expanded_units: None, + is_expanded: true + } } }; @@ -464,7 +502,7 @@ impl UnitIndex { } } - fn remove_unit_rec(&mut self, all_units: &[Unit], unit: &Unit) { + fn remove_unit_rec(&mut self, all_units: &[UnitBuilder], unit: &UnitBuilder) { if let Some(expanded_units) = &unit.expanded_units { for (_, expanded) in expanded_units { self.remove_unit_rec(all_units, &all_units[*expanded]); @@ -473,21 +511,6 @@ impl UnitIndex { self.remove_unit(unit); } - fn add_unit_keys( - &mut self, - unit_id: usize, - keys: impl IntoIterator>, - ) -> Result<(), ConverterBuilderError> { - for key in keys { - if self.0.insert(Arc::clone(&key), unit_id).is_some() { - return Err(ConverterBuilderError::DuplicateUnit { - name: key.to_string(), - }); - } - } - - Ok(()) - } fn add_unit(&mut self, unit: &Unit, id: usize) -> Result { let mut added = 0; for key in unit.all_keys() { @@ -518,6 +541,12 @@ pub enum ConverterBuilderError { #[error("Duplicate unit: {name}")] DuplicateUnit { name: String }, + #[error("Duplicate unit in extend, another key points to the same unit: {key}")] + DuplicateExtendUnit { key: String }, + + #[error("Can only edit aliases in auto expanded unit: {key}")] + InvalidExtendExpanded { key: String }, + #[error(transparent)] UnknownUnit(#[from] UnknownUnit), @@ -535,11 +564,4 @@ pub enum ConverterBuilderError { #[error("No SI prefixes found when expandind SI on a unit")] EmptySIPrefixes, - - #[error("Best units' unit incorrect system: in {contained} units, unit '{unit}' was {got}")] - IncorrectUnitSystem { - unit: Box, - contained: System, - got: System, - }, } diff --git a/src/convert/mod.rs b/src/convert/mod.rs index 170fbe8..d628d4a 100644 --- a/src/convert/mod.rs +++ b/src/convert/mod.rs @@ -22,8 +22,6 @@ use crate::{ pub use builder::{ConverterBuilder, ConverterBuilderError}; pub use units_file::UnitsFile; -use units_file::SIPrefix; - mod builder; pub mod units_file; @@ -165,17 +163,8 @@ impl Converter { .unit_index .get_unit_id(unit.symbol()) .expect("unit not found"); - self.fractions_config_unit_id(unit.system, unit_id) - } - - fn fractions_config_unit_id(&self, system: Option, unit_id: usize) -> FractionsConfig { - let specialized = self.fractions.specialization.get(&unit_id).copied(); - - specialized.unwrap_or_else(|| match system { - Some(System::Metric) => self.fractions.metric, - Some(System::Imperial) => self.fractions.imperial, - None => FractionsConfig::default(), - }) + self.fractions + .config(unit.system, unit.physical_quantity, unit_id) } /// Determines if the unit should be tried to be converted into a fraction @@ -183,11 +172,7 @@ impl Converter { /// # Panics /// If the unit is not known. pub(crate) fn should_fit_fraction(&self, unit: &Unit) -> bool { - match unit.system { - Some(System::Metric) => self.fractions.metric.enabled, - Some(System::Imperial) => self.fractions.imperial.enabled, - None => FractionsConfig::default().enabled, - } + self.fractions_config(unit).enabled } } @@ -219,9 +204,33 @@ impl PartialEq for Converter { #[derive(Debug, Clone, Default)] struct Fractions { - metric: FractionsConfig, - imperial: FractionsConfig, - specialization: HashMap, + all: Option, + metric: Option, + imperial: Option, + quantity: HashMap, + unit: HashMap, +} + +impl Fractions { + fn config( + &self, + system: Option, + quantity: PhysicalQuantity, + unit_id: usize, + ) -> FractionsConfig { + self.unit + .get(&unit_id) + .or_else(|| self.quantity.get(&quantity)) + .or_else(|| { + system.and_then(|s| match s { + System::Metric => self.metric.as_ref(), + System::Imperial => self.imperial.as_ref(), + }) + }) + .or(self.all.as_ref()) + .copied() + .unwrap_or_default() + } } #[derive(Debug, Clone, Copy)] @@ -279,10 +288,6 @@ pub struct Unit { pub physical_quantity: PhysicalQuantity, /// The unit [System] this unit belongs to, if any pub system: Option, - #[serde(skip)] - expand_si: bool, - #[serde(skip)] - expanded_units: Option>, } impl Unit { @@ -401,6 +406,7 @@ impl BestConversions { Serialize, PartialOrd, Ord, + Hash, strum::Display, strum::EnumString, enum_map::Enum, @@ -542,7 +548,11 @@ impl ScaledQuantity { .iter() .filter_map(|&(_, new_unit_id)| { let new_unit = &converter.all_units[new_unit_id]; - let cfg = converter.fractions_config_unit_id(new_unit.system, new_unit_id); + let cfg = converter.fractions.config( + new_unit.system, + new_unit.physical_quantity, + new_unit_id, + ); if !cfg.enabled { return None; } diff --git a/src/convert/units_file.rs b/src/convert/units_file.rs index abf9738..26293d2 100644 --- a/src/convert/units_file.rs +++ b/src/convert/units_file.rs @@ -28,10 +28,9 @@ pub struct UnitsFile { pub si: Option, /// Automatic conversion to fractions /// - /// Configured for each system, if enabled, a decimal value will be - /// converted to a fraction if possible. + /// If enabled, a decimal value will be converted to a fraction if possible. pub fractions: Option, - /// Extend units from other layers before + /// Extend and/or edit units from other layers before pub extend: Option, /// Declare new units #[serde(default)] @@ -93,37 +92,52 @@ impl SIPrefix { } } +/// Configuration for fractions +/// +/// A unit can have more than one layer, which are applied in the order: +/// - `all` +/// - `metric` / `imperial` +/// - `quantity` +/// - `unit` #[derive(Debug, Clone, Deserialize, Default)] +#[serde(default, deny_unknown_fields)] pub struct Fractions { - pub metric: Option, - pub imperial: Option, - pub specialization: HashMap, + /// The base configuration + pub all: Option, + /// For metric units + pub metric: Option, + /// For imperial units + pub imperial: Option, + /// For each [`PhysicalQuantity`] + pub quantity: HashMap, + /// For specific units. The keys are any unit name, symbol, or alias. + pub unit: HashMap, } #[derive(Debug, Clone, Copy, Deserialize)] #[serde(untagged)] -pub enum FractionsWrapper { +pub enum FractionsConfigWrapper { Toggle(bool), Custom(FractionsConfigHelper), } -impl FractionsWrapper { +impl FractionsConfigWrapper { pub fn get(self) -> FractionsConfigHelper { match self { - FractionsWrapper::Toggle(enabled) => FractionsConfigHelper { + FractionsConfigWrapper::Toggle(enabled) => FractionsConfigHelper { enabled: Some(enabled), ..Default::default() }, - FractionsWrapper::Custom(cfg) => FractionsConfigHelper { - enabled: Some(cfg.enabled.unwrap_or(true)), - ..cfg - }, + FractionsConfigWrapper::Custom(cfg) => cfg, } } } +/// Fractions configuration layer +/// +/// See [`FractionsConfig`] #[derive(Debug, Clone, Copy, Deserialize, Default)] -#[serde(default)] +#[serde(default, deny_unknown_fields)] pub struct FractionsConfigHelper { pub enabled: Option, pub accuracy: Option, @@ -132,16 +146,16 @@ pub struct FractionsConfigHelper { } impl FractionsConfigHelper { - pub fn merge(self, parent: FractionsConfigHelper) -> Self { + pub(crate) fn merge(self, other: FractionsConfigHelper) -> Self { Self { - enabled: self.enabled.or(parent.enabled), - accuracy: self.accuracy.or(parent.accuracy), - max_denominator: self.max_denominator.or(parent.max_denominator), - max_whole: self.max_whole.or(parent.max_whole), + enabled: self.enabled.or(other.enabled), + accuracy: self.accuracy.or(other.accuracy), + max_denominator: self.max_denominator.or(other.max_denominator), + max_whole: self.max_whole.or(other.max_whole), } } - pub fn define(self) -> FractionsConfig { + pub(crate) fn define(self) -> FractionsConfig { let d = FractionsConfig::default(); FractionsConfig { enabled: self.enabled.unwrap_or(d.enabled), @@ -163,12 +177,8 @@ impl FractionsConfigHelper { pub struct Extend { /// Precedence when joining to other layers pub precedence: Precedence, - /// Map for new names - pub names: HashMap>>, - /// Map for new symbols - pub symbols: HashMap>>, - /// Map for new aliases - pub aliases: HashMap>>, + /// Map for units to edit + pub units: HashMap, } /// Precedence when joining a list to other layers @@ -187,6 +197,23 @@ pub enum Precedence { Override, } +/// Editable unit +/// +/// See [`Unit`](super::Unit). If the unit is automatially generated (expanded) from another +/// one, only aliases can be set. +#[derive(Debug, Deserialize, Clone, Default)] +#[serde(default)] +pub struct ExtendUnitEntry { + pub ratio: Option, + pub difference: Option, + #[serde(alias = "name")] + pub names: Option>>, + #[serde(alias = "symbol")] + pub symbols: Option>>, + #[serde(alias = "alias")] + pub aliases: Option>>, +} + /// Configuration of a group of units belonging to a [physical quantity] /// /// [physical quantity]: https://en.wikipedia.org/wiki/Physical_quantity @@ -264,15 +291,17 @@ pub struct UnitEntry { /// Names. For example: `grams` /// /// This will expand with [`SI`] configuration. + #[serde(alias = "name")] pub names: Vec>, /// Symbols. For example: `g` /// /// This will expand with [`SI`] configuration. + #[serde(alias = "symbol")] pub symbols: Vec>, /// Whatever other way you want to call the unit. /// /// This **WILL NOT** expand with [`SI`] configuration. - #[serde(default)] + #[serde(default, alias = "alias")] pub aliases: Vec>, /// Conversion ratio. /// diff --git a/units.toml b/units.toml index c61aa63..8d07bb2 100644 --- a/units.toml +++ b/units.toml @@ -20,12 +20,14 @@ milli = ["m"] metric = false imperial = true -[fractions.specialization] +[fractions.quantity] +time = false +temperature = false + +[fractions.unit] tsp = { max_whole = 5, max_denominator = 2 } tbsp = { max_whole = 4, max_denominator = 3 } lb = { max_denominator = 8 } -F = false -C = false [[quantity]] quantity = "volume" diff --git a/units/spanish.toml b/units/spanish.toml index 22592eb..e072bc7 100644 --- a/units/spanish.toml +++ b/units/spanish.toml @@ -6,23 +6,21 @@ deci = [] centi = [] milli = ["mili"] -[extend.names] -l = ["litro", "litros"] -c = ["taza", "tazas"] -"fl oz" = ["onza líquida", "onzas líquidas"] -gal = ["galón", "galones"] -pint = ["pinta", "pintas"] -quart = ["cuarto", "cuartos"] -m = ["metro", "metros"] -foot = ["pie", "pies"] -inch = ["pulgada", "pulgadas"] -gram = ["gramo", "gramos"] -ounce = ["onza", "onzas"] -pound = ["libra", "libras"] -s = ["segundo", "segundos"] -min = ["minuto", "minutos"] -h = ["hora", "horas"] -d = ["día", "días"] - -[extend.aliases] -kilogram = ["kilo", "kilos"] \ No newline at end of file +[extend.units] +l = { names = ["litro", "litros"] } +c = { names = ["taza", "tazas"] } +"fl oz" = { names = ["onza líquida", "onzas líquidas"] } +gal = { names = ["galón", "galones"] } +pint = { names = ["pinta", "pintas"] } +quart = { names = ["cuarto", "cuartos"] } +m = { names = ["metro", "metros"] } +foot = { names = ["pie", "pies"] } +inch = { names = ["pulgada", "pulgadas"] } +gram = { names = ["gramo", "gramos"] } +kilogram = { aliases = ["kilo", "kilos"] } +ounce = { names = ["onza", "onzas"] } +pound = { names = ["libra", "libras"] } +s = { names = ["segundo", "segundos"] } +min = { names = ["minuto", "minutos"] } +h = { names = ["hora", "horas"] } +d = { names = ["día", "días"] }