sim_lib_mutation/
runtime_key.rs1use std::{
2 collections::hash_map::DefaultHasher,
3 hash::{Hash, Hasher},
4};
5
6use sim_kernel::{Cx, Expr, NumberLiteral, Result, Symbol, Value};
7
8#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub enum RuntimeKey {
15 Bool(bool),
17 Integer(i64),
19 FloatBits(u64),
21 Str(String),
23 Symbol(Symbol),
25 ObjectIdentity(u64),
27}
28
29impl RuntimeKey {
30 pub fn from_value(cx: &mut Cx, value: &Value) -> Result<Option<Self>> {
37 match value.object().as_expr(cx)? {
38 Expr::Nil => Ok(None),
39 Expr::Bool(value) => Ok(Some(Self::Bool(value))),
40 Expr::Number(number) => Ok(number_key(&number)),
41 Expr::String(value) => Ok(Some(Self::Str(value))),
42 Expr::Symbol(symbol) => Ok(Some(Self::Symbol(symbol))),
43 _ => Ok(Some(Self::ObjectIdentity(object_identity(value)))),
44 }
45 }
46
47 pub fn as_integer_index(&self) -> Option<i64> {
49 match self {
50 Self::Integer(index) => Some(*index),
51 _ => None,
52 }
53 }
54
55 pub fn as_expr(&self) -> Expr {
57 match self {
58 Self::Bool(value) => Expr::Bool(*value),
59 Self::Integer(value) => Expr::Number(NumberLiteral {
60 domain: Symbol::qualified("runtime-key", "integer"),
61 canonical: value.to_string(),
62 }),
63 Self::FloatBits(bits) => Expr::Number(NumberLiteral {
64 domain: Symbol::qualified("runtime-key", "float-bits"),
65 canonical: bits.to_string(),
66 }),
67 Self::Str(value) => Expr::String(value.clone()),
68 Self::Symbol(symbol) => Expr::Symbol(symbol.clone()),
69 Self::ObjectIdentity(identity) => Expr::Extension {
70 tag: Symbol::qualified("mutation", "object-identity-key"),
71 payload: Box::new(Expr::String(identity.to_string())),
72 },
73 }
74 }
75}
76
77pub trait RuntimeKeyPolicy: Send + Sync {
82 fn key_for(&self, cx: &mut Cx, value: &Value) -> Result<Option<RuntimeKey>>;
84}
85
86#[derive(Clone, Copy, Debug, Default)]
89pub struct PrimitiveRuntimeKeyPolicy;
90
91impl RuntimeKeyPolicy for PrimitiveRuntimeKeyPolicy {
92 fn key_for(&self, cx: &mut Cx, value: &Value) -> Result<Option<RuntimeKey>> {
93 RuntimeKey::from_value(cx, value)
94 }
95}
96
97fn number_key(number: &NumberLiteral) -> Option<RuntimeKey> {
98 if let Ok(value) = number.canonical.parse::<i64>() {
99 return Some(RuntimeKey::Integer(value));
100 }
101 let value = number.canonical.parse::<f64>().ok()?;
102 (!value.is_nan()).then_some(RuntimeKey::FloatBits(value.to_bits()))
103}
104
105fn object_identity(value: &Value) -> u64 {
106 let mut hasher = DefaultHasher::new();
107 value.hash(&mut hasher);
108 hasher.finish()
109}