126 lines
2.5 KiB
Rust
126 lines
2.5 KiB
Rust
use crate::units::{Unit, UnitType};
|
|
use fastnum::D128;
|
|
use once_cell::sync::Lazy;
|
|
use std::collections::HashMap;
|
|
use std::sync::RwLock;
|
|
|
|
#[derive(Clone, Copy)]
|
|
pub(crate) struct ExtraUnitDef {
|
|
pub(crate) category: UnitType,
|
|
pub(crate) weight: D128,
|
|
pub(crate) singular: &'static str,
|
|
pub(crate) plural: &'static str,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct Registry {
|
|
by_name: HashMap<&'static str, u16>,
|
|
units: Vec<ExtraUnitDef>,
|
|
}
|
|
|
|
static REGISTRY: Lazy<RwLock<Registry>> = Lazy::new(|| RwLock::new(Registry::default()));
|
|
|
|
fn leak_str(s: String) -> &'static str {
|
|
Box::leak(s.into_boxed_str())
|
|
}
|
|
|
|
pub(crate) fn lookup_unit(word: &str) -> Option<Unit> {
|
|
let reg = REGISTRY.read().ok()?;
|
|
let id = *reg.by_name.get(word)?;
|
|
Some(Unit::Extra(id))
|
|
}
|
|
|
|
pub(crate) fn upsert_unit(
|
|
name_lowercase: &str,
|
|
singular: &str,
|
|
plural: &str,
|
|
category: UnitType,
|
|
weight: D128,
|
|
) -> Unit {
|
|
let mut reg = REGISTRY
|
|
.write()
|
|
.expect("extra unit registry lock poisoned");
|
|
|
|
if let Some(existing_id) = reg.by_name.get(name_lowercase).copied() {
|
|
let idx = existing_id as usize;
|
|
reg.units[idx] = ExtraUnitDef {
|
|
category,
|
|
weight,
|
|
singular: reg.units[idx].singular,
|
|
plural: reg.units[idx].plural,
|
|
};
|
|
return Unit::Extra(existing_id);
|
|
}
|
|
|
|
let id: u16 = reg
|
|
.units
|
|
.len()
|
|
.try_into()
|
|
.expect("too many extra units registered");
|
|
|
|
let key = leak_str(name_lowercase.to_string());
|
|
let singular = leak_str(singular.to_string());
|
|
let plural = leak_str(plural.to_string());
|
|
|
|
reg.by_name.insert(key, id);
|
|
reg.units.push(ExtraUnitDef {
|
|
category,
|
|
weight,
|
|
singular,
|
|
plural,
|
|
});
|
|
|
|
Unit::Extra(id)
|
|
}
|
|
|
|
pub(crate) fn get_category(id: u16) -> UnitType {
|
|
let reg = REGISTRY
|
|
.read()
|
|
.expect("extra unit registry lock poisoned");
|
|
reg.units
|
|
.get(id as usize)
|
|
.map(|u| u.category)
|
|
.unwrap_or(UnitType::NoType)
|
|
}
|
|
|
|
pub(crate) fn get_weight(id: u16) -> D128 {
|
|
let reg = REGISTRY
|
|
.read()
|
|
.expect("extra unit registry lock poisoned");
|
|
reg.units
|
|
.get(id as usize)
|
|
.map(|u| u.weight)
|
|
.unwrap_or(D128::NAN)
|
|
}
|
|
|
|
pub(crate) fn get_singular(id: u16) -> &'static str {
|
|
let reg = REGISTRY
|
|
.read()
|
|
.expect("extra unit registry lock poisoned");
|
|
reg.units
|
|
.get(id as usize)
|
|
.map(|u| u.singular)
|
|
.unwrap_or("")
|
|
}
|
|
|
|
pub(crate) fn get_plural(id: u16) -> &'static str {
|
|
let reg = REGISTRY
|
|
.read()
|
|
.expect("extra unit registry lock poisoned");
|
|
reg.units
|
|
.get(id as usize)
|
|
.map(|u| u.plural)
|
|
.unwrap_or("")
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub(crate) fn clear_for_tests() {
|
|
let mut reg = REGISTRY
|
|
.write()
|
|
.expect("extra unit registry lock poisoned");
|
|
reg.by_name.clear();
|
|
reg.units.clear();
|
|
}
|
|
|
|
|