sim_lib_standard_core/
fidelity.rs1use sim_kernel::{ContentId, Coordinate, Expr, HandleId, Ref, Symbol};
4pub(crate) use sim_value::kind::expr_kind;
5
6#[derive(Clone, Debug, PartialEq, Eq)]
9pub struct FidelityBadge {
10 pub subject: Ref,
12 pub badge: Symbol,
14 pub level: u8,
16 pub evidence: Ref,
18}
19
20impl FidelityBadge {
21 pub fn new(subject: Ref, badge: Symbol, level: u8, evidence: Ref) -> Self {
23 Self {
24 subject,
25 badge,
26 level,
27 evidence,
28 }
29 }
30
31 pub fn to_constructor_args(&self) -> Vec<Expr> {
33 vec![
34 ref_expr(&self.subject),
35 Expr::Symbol(self.badge.clone()),
36 Expr::String(self.level.to_string()),
37 ref_expr(&self.evidence),
38 ]
39 }
40
41 pub fn from_constructor_args(args: Vec<Expr>) -> sim_kernel::Result<Self> {
43 let [subject, badge, level, evidence] = args.as_slice() else {
44 return Err(sim_kernel::Error::Eval(
45 "standard/FidelityBadge expects subject, badge, level, evidence".to_owned(),
46 ));
47 };
48 Ok(Self {
49 subject: ref_from_expr(subject)?,
50 badge: symbol_from_expr(badge, "badge")?,
51 level: level_from_expr(level)?,
52 evidence: ref_from_expr(evidence)?,
53 })
54 }
55}
56
57pub fn fidelity_badge_class_symbol() -> Symbol {
59 Symbol::qualified("standard", "FidelityBadge")
60}
61
62pub(crate) fn ref_expr(reference: &Ref) -> Expr {
63 match reference {
64 Ref::Symbol(symbol) => Expr::Symbol(symbol.clone()),
65 Ref::Content(content) => Expr::List(vec![
66 Expr::Symbol(ref_content_symbol()),
67 Expr::Symbol(content.algorithm.clone()),
68 Expr::Bytes(content.bytes.to_vec()),
69 ]),
70 Ref::Handle(handle) => Expr::List(vec![
71 Expr::Symbol(ref_handle_symbol()),
72 Expr::Bytes(handle.0.to_be_bytes().to_vec()),
73 ]),
74 Ref::Coord(coordinate) => Expr::List(vec![
75 Expr::Symbol(ref_coord_symbol()),
76 Expr::Symbol(coordinate.space.clone()),
77 ref_expr(&Ref::Content(coordinate.ordinal.clone())),
78 ]),
79 }
80}
81
82pub(crate) fn ref_from_expr(expr: &Expr) -> sim_kernel::Result<Ref> {
83 match expr {
84 Expr::Symbol(symbol) => Ok(Ref::Symbol(symbol.clone())),
85 Expr::List(items) => ref_from_list(items),
86 _ => Err(sim_kernel::Error::TypeMismatch {
87 expected: "symbol ref",
88 found: expr_kind(expr),
89 }),
90 }
91}
92
93pub(crate) fn symbol_from_expr(expr: &Expr, expected: &'static str) -> sim_kernel::Result<Symbol> {
94 match expr {
95 Expr::Symbol(symbol) => Ok(symbol.clone()),
96 _ => Err(sim_kernel::Error::TypeMismatch {
97 expected,
98 found: expr_kind(expr),
99 }),
100 }
101}
102
103fn level_from_expr(expr: &Expr) -> sim_kernel::Result<u8> {
104 let Expr::String(level) = expr else {
105 return Err(sim_kernel::Error::TypeMismatch {
106 expected: "level string",
107 found: expr_kind(expr),
108 });
109 };
110 level
111 .parse::<u8>()
112 .map_err(|err| sim_kernel::Error::Eval(format!("invalid fidelity level {level}: {err}")))
113}
114
115fn ref_from_list(items: &[Expr]) -> sim_kernel::Result<Ref> {
116 let Some(Expr::Symbol(head)) = items.first() else {
117 return Err(sim_kernel::Error::TypeMismatch {
118 expected: "ref constructor head",
119 found: items.first().map(expr_kind).unwrap_or("empty list"),
120 });
121 };
122 if head == &ref_content_symbol() {
123 return content_ref_from_items(items);
124 }
125 if head == &ref_handle_symbol() {
126 return handle_ref_from_items(items);
127 }
128 if head == &ref_coord_symbol() {
129 return coord_ref_from_items(items);
130 }
131 Err(sim_kernel::Error::Eval(format!(
132 "unknown standard ref constructor {head}"
133 )))
134}
135
136fn content_ref_from_items(items: &[Expr]) -> sim_kernel::Result<Ref> {
137 let [_, Expr::Symbol(algorithm), Expr::Bytes(bytes)] = items else {
138 return Err(sim_kernel::Error::Eval(
139 "standard/ref-content expects algorithm and 32 bytes".to_owned(),
140 ));
141 };
142 let bytes = bytes_32(bytes)?;
143 Ok(Ref::Content(ContentId::from_bytes(
144 algorithm.clone(),
145 bytes,
146 )))
147}
148
149fn handle_ref_from_items(items: &[Expr]) -> sim_kernel::Result<Ref> {
150 let [_, Expr::Bytes(bytes)] = items else {
151 return Err(sim_kernel::Error::Eval(
152 "standard/ref-handle expects 16 bytes".to_owned(),
153 ));
154 };
155 let bytes: [u8; 16] = bytes.as_slice().try_into().map_err(|_| {
156 sim_kernel::Error::Eval(format!(
157 "handle ref expects 16 bytes, found {}",
158 bytes.len()
159 ))
160 })?;
161 Ok(Ref::Handle(HandleId(u128::from_be_bytes(bytes))))
162}
163
164fn coord_ref_from_items(items: &[Expr]) -> sim_kernel::Result<Ref> {
165 let [_, Expr::Symbol(space), ordinal] = items else {
166 return Err(sim_kernel::Error::Eval(
167 "standard/ref-coord expects space and content ordinal".to_owned(),
168 ));
169 };
170 let Ref::Content(ordinal) = ref_from_expr(ordinal)? else {
171 return Err(sim_kernel::Error::TypeMismatch {
172 expected: "content ordinal ref",
173 found: expr_kind(ordinal),
174 });
175 };
176 Ok(Ref::Coord(Coordinate {
177 space: space.clone(),
178 ordinal,
179 }))
180}
181
182fn bytes_32(bytes: &[u8]) -> sim_kernel::Result<[u8; 32]> {
183 bytes.try_into().map_err(|_| {
184 sim_kernel::Error::Eval(format!(
185 "content ref expects 32 bytes, found {}",
186 bytes.len()
187 ))
188 })
189}
190
191fn ref_content_symbol() -> Symbol {
192 Symbol::qualified("standard", "ref-content")
193}
194
195fn ref_handle_symbol() -> Symbol {
196 Symbol::qualified("standard", "ref-handle")
197}
198
199fn ref_coord_symbol() -> Symbol {
200 Symbol::qualified("standard", "ref-coord")
201}