pub use vyre_spec::AlgebraicLaw;
use rustc_hash::FxHashMap;
use std::sync::LazyLock;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct AlgebraicLawRegistration {
pub op_id: &'static str,
pub law: AlgebraicLaw,
}
impl AlgebraicLawRegistration {
#[must_use]
pub const fn new(op_id: &'static str, law: AlgebraicLaw) -> Self {
Self { op_id, law }
}
}
inventory::collect!(AlgebraicLawRegistration);
static LAWS_BY_OP: LazyLock<FxHashMap<&'static str, Vec<&'static AlgebraicLaw>>> =
LazyLock::new(|| {
let mut map: FxHashMap<&'static str, Vec<&'static AlgebraicLaw>> = FxHashMap::default();
for r in inventory::iter::<AlgebraicLawRegistration>() {
map.entry(r.op_id).or_default().push(&r.law);
}
map
});
#[must_use]
pub fn laws_for_op(op_id: &str) -> &'static [&'static AlgebraicLaw] {
LAWS_BY_OP.get(op_id).map(Vec::as_slice).unwrap_or(&[])
}
#[must_use]
pub fn has_law<F>(op_id: &str, predicate: F) -> bool
where
F: Fn(&AlgebraicLaw) -> bool,
{
laws_for_op(op_id).iter().any(|law| predicate(law))
}
#[must_use]
pub fn is_commutative(op_id: &str) -> bool {
has_law(op_id, |l| matches!(l, AlgebraicLaw::Commutative))
}
#[must_use]
pub fn is_associative(op_id: &str) -> bool {
has_law(op_id, |l| matches!(l, AlgebraicLaw::Associative))
}
#[cfg(test)]
mod tests {
use super::*;
inventory::submit! {
AlgebraicLawRegistration::new("test::commutative_op", AlgebraicLaw::Commutative)
}
inventory::submit! {
AlgebraicLawRegistration::new("test::associative_op", AlgebraicLaw::Associative)
}
#[test]
fn registered_commutative_law_is_queryable() {
assert!(is_commutative("test::commutative_op"));
assert!(!is_commutative("test::associative_op"));
}
#[test]
fn registered_associative_law_is_queryable() {
assert!(is_associative("test::associative_op"));
assert!(!is_associative("test::commutative_op"));
}
#[test]
fn laws_for_unregistered_op_is_empty() {
assert!(laws_for_op("test::does_not_exist").is_empty());
}
#[test]
fn laws_are_stable_across_calls() {
let a = laws_for_op("test::commutative_op").len();
let b = laws_for_op("test::commutative_op").len();
assert_eq!(a, b);
assert_eq!(a, 1);
}
}