CPC/src/extra_units.rs
2025-12-11 20:11:53 -05:00

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();
}