1use std::convert::TryFrom;
4
5use sim_kernel::{CapabilityName, Cx, Error, Expr, NumberLiteral, Result, Symbol, Value};
6use sim_value::kind::expr_kind;
7
8pub trait CitizenField: Sized {
27 fn encode_field(&self) -> Expr;
29 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self>;
31
32 fn decode_field_value(cx: &mut Cx, value: Value, field: &'static str) -> Result<Self> {
37 let expr = value_to_expr(cx, value, field)?;
38 Self::decode_field_expr(&expr, field)
39 }
40}
41
42pub fn value_to_expr(cx: &mut Cx, value: Value, field: &'static str) -> Result<Expr> {
44 value
45 .object()
46 .as_expr(cx)
47 .map_err(|err| field_error(field, format!("cannot convert value to Expr: {err}")))
48}
49
50pub fn value_from_expr(cx: &mut Cx, expr: &Expr) -> Result<Value> {
56 match expr {
57 Expr::Nil => cx.factory().nil(),
58 Expr::Bool(value) => cx.factory().bool(*value),
59 Expr::Number(value) => cx
60 .factory()
61 .number_literal(value.domain.clone(), value.canonical.clone()),
62 Expr::Symbol(value) => cx.factory().symbol(value.clone()),
63 Expr::String(value) => cx.factory().string(value.clone()),
64 Expr::Bytes(value) => cx.factory().bytes(value.clone()),
65 Expr::List(items) => {
66 let values = items
67 .iter()
68 .map(|item| value_from_expr(cx, item))
69 .collect::<Result<Vec<_>>>()?;
70 cx.factory().list(values)
71 }
72 Expr::Map(entries) => {
73 let entries = entries
74 .iter()
75 .map(|(key, value)| {
76 let Expr::Symbol(key) = key else {
77 return Err(field_error(
78 "map",
79 format!("expected symbol key, found {}", expr_kind(key)),
80 ));
81 };
82 Ok((key.clone(), value_from_expr(cx, value)?))
83 })
84 .collect::<Result<Vec<_>>>()?;
85 cx.factory().table(entries)
86 }
87 other => cx.factory().expr(other.clone()),
88 }
89}
90
91pub fn decode_version(cx: &mut Cx, value: Value, expected: u32, class: Symbol) -> Result<()> {
96 let expected_symbol = Symbol::new(format!("v{expected}"));
97 match value_to_expr(cx, value, "version")? {
98 Expr::Symbol(actual) if actual == expected_symbol => Ok(()),
99 other => Err(Error::Eval(format!(
100 "citizen {class} expects version {expected_symbol}, found {}",
101 expr_kind(&other)
102 ))),
103 }
104}
105
106pub fn arity_error(class: Symbol, expected: usize, actual: usize) -> Error {
108 Error::Eval(format!(
109 "citizen {class} expects {expected} read-constructor argument(s), found {actual}"
110 ))
111}
112
113pub fn field_error(field: &'static str, message: impl Into<String>) -> Error {
115 Error::Eval(format!("citizen field {field}: {}", message.into()))
116}
117
118fn expect_number_domain(
119 number: &NumberLiteral,
120 expected: Symbol,
121 field: &'static str,
122) -> Result<()> {
123 if number.domain == expected {
124 Ok(())
125 } else {
126 Err(field_error(
127 field,
128 format!("expected number domain {expected}, found {}", number.domain),
129 ))
130 }
131}
132
133macro_rules! signed_int_field {
134 ($($ty:ty),* $(,)?) => {
135 $(
136 impl CitizenField for $ty {
137 fn encode_field(&self) -> Expr {
138 int_expr(self.to_string())
139 }
140
141 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
142 let value = decode_integer_text(expr, field)?;
143 <$ty>::try_from(value).map_err(|_| {
144 field_error(field, format!("integer {value} is out of range"))
145 })
146 }
147 }
148 )*
149 };
150}
151
152macro_rules! unsigned_int_field {
153 ($($ty:ty),* $(,)?) => {
154 $(
155 impl CitizenField for $ty {
156 fn encode_field(&self) -> Expr {
157 int_expr(self.to_string())
158 }
159
160 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
161 let value = decode_integer_text(expr, field)?;
162 <$ty>::try_from(value).map_err(|_| {
163 field_error(field, format!("integer {value} is out of range"))
164 })
165 }
166 }
167 )*
168 };
169}
170
171signed_int_field!(i8, i16, i32, i64, i128, isize);
172unsigned_int_field!(u8, u16, u32, u64, usize);
173
174impl CitizenField for bool {
175 fn encode_field(&self) -> Expr {
176 Expr::Bool(*self)
177 }
178
179 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
180 match expr {
181 Expr::Bool(value) => Ok(*value),
182 other => Err(field_error(
183 field,
184 format!("expected bool, found {}", expr_kind(other)),
185 )),
186 }
187 }
188}
189
190impl CitizenField for String {
191 fn encode_field(&self) -> Expr {
192 Expr::String(self.clone())
193 }
194
195 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
196 match expr {
197 Expr::String(value) => Ok(value.clone()),
198 other => Err(field_error(
199 field,
200 format!("expected string, found {}", expr_kind(other)),
201 )),
202 }
203 }
204}
205
206impl CitizenField for Symbol {
207 fn encode_field(&self) -> Expr {
208 Expr::Symbol(self.clone())
209 }
210
211 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
212 match expr {
213 Expr::Symbol(value) => Ok(value.clone()),
214 other => Err(field_error(
215 field,
216 format!("expected symbol, found {}", expr_kind(other)),
217 )),
218 }
219 }
220}
221
222impl CitizenField for Expr {
223 fn encode_field(&self) -> Expr {
224 self.clone()
225 }
226
227 fn decode_field_expr(expr: &Expr, _field: &'static str) -> Result<Self> {
228 Ok(expr.clone())
229 }
230}
231
232impl CitizenField for CapabilityName {
233 fn encode_field(&self) -> Expr {
234 Expr::String(self.as_str().to_owned())
235 }
236
237 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
238 match expr {
239 Expr::String(value) => Ok(CapabilityName::new(value.clone())),
240 other => Err(field_error(
241 field,
242 format!(
243 "expected string capability name, found {}",
244 expr_kind(other)
245 ),
246 )),
247 }
248 }
249}
250
251impl CitizenField for f64 {
252 fn encode_field(&self) -> Expr {
253 Expr::Number(NumberLiteral {
254 domain: Symbol::qualified("numbers", "f64"),
255 canonical: canonical_f64(*self),
256 })
257 }
258
259 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
260 match expr {
261 Expr::Number(number) => {
262 expect_number_domain(number, Symbol::qualified("numbers", "f64"), field)?;
263 number
264 .canonical
265 .parse::<f64>()
266 .map_err(|err| field_error(field, format!("invalid f64: {err}")))
267 }
268 other => Err(field_error(
269 field,
270 format!("expected number, found {}", expr_kind(other)),
271 )),
272 }
273 }
274}
275
276impl<A, B> CitizenField for (A, B)
277where
278 A: CitizenField,
279 B: CitizenField,
280{
281 fn encode_field(&self) -> Expr {
282 Expr::List(vec![self.0.encode_field(), self.1.encode_field()])
283 }
284
285 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
286 let Expr::List(items) = expr else {
287 return Err(field_error(
288 field,
289 format!("expected pair list, found {}", expr_kind(expr)),
290 ));
291 };
292 let [first, second] = items.as_slice() else {
293 return Err(field_error(
294 field,
295 format!("expected pair list with 2 item(s), found {}", items.len()),
296 ));
297 };
298 Ok((
299 A::decode_field_expr(first, field)?,
300 B::decode_field_expr(second, field)?,
301 ))
302 }
303}
304
305impl<T> CitizenField for Vec<T>
306where
307 T: CitizenField,
308{
309 fn encode_field(&self) -> Expr {
310 Expr::List(self.iter().map(CitizenField::encode_field).collect())
311 }
312
313 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
314 match expr {
315 Expr::List(items) => items
316 .iter()
317 .map(|item| T::decode_field_expr(item, field))
318 .collect(),
319 other => Err(field_error(
320 field,
321 format!("expected list, found {}", expr_kind(other)),
322 )),
323 }
324 }
325}
326
327impl<T> CitizenField for Option<T>
328where
329 T: CitizenField,
330{
331 fn encode_field(&self) -> Expr {
332 self.as_ref()
333 .map(CitizenField::encode_field)
334 .unwrap_or(Expr::Nil)
335 }
336
337 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
338 match expr {
339 Expr::Nil => Ok(None),
340 other => T::decode_field_expr(other, field).map(Some),
341 }
342 }
343}
344
345fn int_expr(canonical: String) -> Expr {
346 Expr::Number(NumberLiteral {
347 domain: Symbol::qualified("citizen", "int"),
348 canonical,
349 })
350}
351
352fn decode_integer_text(expr: &Expr, field: &'static str) -> Result<i128> {
353 match expr {
354 Expr::Number(number) => {
355 expect_number_domain(number, Symbol::qualified("citizen", "int"), field)?;
356 number
357 .canonical
358 .parse::<i128>()
359 .map_err(|err| field_error(field, format!("invalid integer: {err}")))
360 }
361 other => Err(field_error(
362 field,
363 format!("expected integer number, found {}", expr_kind(other)),
364 )),
365 }
366}
367
368fn canonical_f64(value: f64) -> String {
369 if value.is_nan() {
370 "NaN".to_owned()
371 } else if value == f64::INFINITY {
372 "inf".to_owned()
373 } else if value == f64::NEG_INFINITY {
374 "-inf".to_owned()
375 } else {
376 value.to_string()
377 }
378}