use crate::ast::{DatePattern, Expr};
use crate::output::DocString;
use crate::runtime::{MissingDeps, Substance};
use crate::types::{BaseUnit, Dimensionality, Number, Numeric};
use crate::Value;
use std::collections::{BTreeMap, BTreeSet};
#[derive(Default, Debug)]
pub struct Registry {
pub base_units: BTreeSet<BaseUnit>,
pub base_unit_long_names: BTreeMap<String, String>,
pub units: BTreeMap<String, Number>,
pub quantities: BTreeMap<Dimensionality, String>,
pub decomposition_units: BTreeMap<Dimensionality, String>,
pub prefixes: Vec<(String, Numeric)>,
pub definitions: BTreeMap<String, Expr>,
pub docs: BTreeMap<String, DocString>,
pub categories: BTreeMap<String, String>,
pub category_names: BTreeMap<String, String>,
pub datepatterns: Vec<Vec<DatePattern>>,
pub substances: BTreeMap<String, Substance>,
pub substance_symbols: BTreeMap<String, String>,
pub missing_deps: BTreeMap<String, MissingDeps>,
}
impl Registry {
pub fn lookup_exact(&self, name: &str) -> Option<Value> {
if let Some(k) = self.base_units.get(name) {
return Some(Value::Number(Number::one_unit(k.to_owned())));
}
if let Some(v) = self.units.get(name).cloned() {
return Some(Value::Number(v));
}
if let Some(deps) = self.missing_deps.get(name) {
return Some(Value::MissingDeps(deps.clone()));
}
None
}
fn lookup_with_prefix(&self, name: &str) -> Option<Value> {
if let Some(v) = self.lookup_exact(name) {
return Some(v);
}
for &(ref pre, ref pre_value) in &self.prefixes {
if !name.starts_with(pre) {
continue;
}
if let Some(v) = self.lookup_exact(&name[pre.len()..]) {
return Some((&v * &Value::Number(Number::new(pre_value.clone()))).unwrap());
}
}
None
}
pub(crate) fn lookup(&self, name: &str) -> Option<Value> {
let res = self.lookup_with_prefix(name);
if res.is_some() {
return res;
}
if let Some(name) = name.strip_suffix('s') {
self.lookup_with_prefix(name)
} else {
None
}
}
fn canonicalize_exact(&self, name: &str) -> Option<String> {
if let Some(v) = self.base_unit_long_names.get(name) {
return Some(v.clone());
}
if let Some(base_unit) = self.base_units.get(name) {
return Some(base_unit.to_string());
}
if let Some(expr) = self.definitions.get(name) {
if let Expr::Unit { ref name } = *expr {
if let Some(canonicalized) = self.canonicalize(&*name) {
return Some(canonicalized);
} else {
return Some(name.clone());
}
} else {
return Some(name.to_owned());
}
}
None
}
fn canonicalize_with_prefix(&self, name: &str) -> Option<String> {
if let Some(v) = self.canonicalize_exact(name) {
return Some(v);
}
for &(ref prefix, ref value) in &self.prefixes {
if let Some(name) = name.strip_prefix(prefix) {
if let Some(canonicalized) = self.canonicalize_exact(name) {
let mut prefix = prefix;
for &(ref other, ref otherval) in &self.prefixes {
if other.len() > prefix.len() && value == otherval {
prefix = other;
}
}
return Some(format!("{}{}", prefix, canonicalized));
}
}
}
None
}
pub fn canonicalize(&self, name: &str) -> Option<String> {
let res = self.canonicalize_with_prefix(name);
if res.is_some() {
return res;
}
if let Some(name) = name.strip_suffix('s') {
self.canonicalize_with_prefix(name)
} else {
None
}
}
}