use crate::markup::Markup;
use crate::prefix_parser::AcceptsPrefix;
use crate::registry::{BaseRepresentation, BaseRepresentationFactor, Registry, RegistryError};
use crate::typed_ast::Type;
use crate::unit::{CanonicalName, Unit};
use compact_str::CompactString;
use thiserror::Error;
#[derive(Clone, Error, Debug, PartialEq, Eq)]
pub enum UnitRegistryError {
#[error("{0}")]
RegistryError(RegistryError),
}
pub type Result<T> = std::result::Result<T, UnitRegistryError>;
#[derive(Debug, Clone)]
pub struct UnitMetadata {
pub type_: Type,
pub readable_type: Markup,
pub aliases: Vec<(CompactString, AcceptsPrefix)>,
pub name: Option<CompactString>,
pub canonical_name: CanonicalName,
pub url: Option<CompactString>,
pub description: Option<CompactString>,
pub binary_prefixes: bool,
pub metric_prefixes: bool,
pub is_abbreviation: bool,
pub code_source_id: usize,
}
#[derive(Clone)]
pub struct UnitRegistry {
pub inner: Registry<UnitMetadata>,
}
impl UnitRegistry {
pub fn new() -> Self {
Self {
inner: Registry::<UnitMetadata>::default(),
}
}
pub fn add_base_unit(&mut self, name: &str, metadata: UnitMetadata) -> Result<()> {
self.inner
.add_base_entry(name, metadata)
.map_err(UnitRegistryError::RegistryError)
}
pub fn add_derived_unit(
&mut self,
name: &str,
base_representation: &Unit,
metadata: UnitMetadata,
) -> Result<()> {
let base_representation_factors = base_representation
.iter()
.map(|factor| BaseRepresentationFactor(factor.unit_id.name.clone(), factor.exponent));
let base_representation = BaseRepresentation::from_factors(base_representation_factors);
self.inner
.add_derived_entry(name, base_representation, metadata)
.map_err(UnitRegistryError::RegistryError)?;
Ok(())
}
pub fn get_matching_unit_names(&self, unit: &Unit) -> impl Iterator<Item = CompactString> + '_ {
let (base_unit, _) = unit.to_base_unit_representation();
let base_repr = BaseRepresentation::from_factors(
base_unit
.iter()
.map(|f| BaseRepresentationFactor(f.unit_id.name.clone(), f.exponent)),
);
let base_unit_match = (base_repr.iter().count() == 1)
.then(|| base_repr.iter().next())
.flatten()
.filter(|factor| factor.1 == num_rational::Ratio::from_integer(1))
.filter(|factor| self.inner.is_base_unit(&factor.0))
.map(|factor| factor.0.clone());
let derived_units = self
.inner
.get_derived_entry_names_for_filtered(&base_repr, |metadata| !metadata.is_abbreviation);
base_unit_match.into_iter().chain(derived_units)
}
}