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