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
118macro_rules! signed_int_field {
119 ($($ty:ty),* $(,)?) => {
120 $(
121 impl CitizenField for $ty {
122 fn encode_field(&self) -> Expr {
123 int_expr(self.to_string())
124 }
125
126 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
127 let value = decode_integer_text(expr, field)?;
128 <$ty>::try_from(value).map_err(|_| {
129 field_error(field, format!("integer {value} is out of range"))
130 })
131 }
132 }
133 )*
134 };
135}
136
137macro_rules! unsigned_int_field {
138 ($($ty:ty),* $(,)?) => {
139 $(
140 impl CitizenField for $ty {
141 fn encode_field(&self) -> Expr {
142 int_expr(self.to_string())
143 }
144
145 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
146 let value = decode_integer_text(expr, field)?;
147 <$ty>::try_from(value).map_err(|_| {
148 field_error(field, format!("integer {value} is out of range"))
149 })
150 }
151 }
152 )*
153 };
154}
155
156signed_int_field!(i8, i16, i32, i64, i128, isize);
157unsigned_int_field!(u8, u16, u32, u64, usize);
158
159impl CitizenField for bool {
160 fn encode_field(&self) -> Expr {
161 Expr::Bool(*self)
162 }
163
164 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
165 match expr {
166 Expr::Bool(value) => Ok(*value),
167 other => Err(field_error(
168 field,
169 format!("expected bool, found {}", expr_kind(other)),
170 )),
171 }
172 }
173}
174
175impl CitizenField for String {
176 fn encode_field(&self) -> Expr {
177 Expr::String(self.clone())
178 }
179
180 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
181 match expr {
182 Expr::String(value) => Ok(value.clone()),
183 other => Err(field_error(
184 field,
185 format!("expected string, found {}", expr_kind(other)),
186 )),
187 }
188 }
189}
190
191impl CitizenField for Symbol {
192 fn encode_field(&self) -> Expr {
193 Expr::Symbol(self.clone())
194 }
195
196 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
197 match expr {
198 Expr::Symbol(value) => Ok(value.clone()),
199 other => Err(field_error(
200 field,
201 format!("expected symbol, found {}", expr_kind(other)),
202 )),
203 }
204 }
205}
206
207impl CitizenField for Expr {
208 fn encode_field(&self) -> Expr {
209 self.clone()
210 }
211
212 fn decode_field_expr(expr: &Expr, _field: &'static str) -> Result<Self> {
213 Ok(expr.clone())
214 }
215}
216
217impl CitizenField for CapabilityName {
218 fn encode_field(&self) -> Expr {
219 Expr::String(self.as_str().to_owned())
220 }
221
222 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
223 match expr {
224 Expr::String(value) => Ok(CapabilityName::new(value.clone())),
225 other => Err(field_error(
226 field,
227 format!(
228 "expected string capability name, found {}",
229 expr_kind(other)
230 ),
231 )),
232 }
233 }
234}
235
236impl CitizenField for f64 {
237 fn encode_field(&self) -> Expr {
238 Expr::Number(NumberLiteral {
239 domain: Symbol::qualified("numbers", "f64"),
240 canonical: canonical_f64(*self),
241 })
242 }
243
244 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
245 match expr {
246 Expr::Number(number) => number
247 .canonical
248 .parse::<f64>()
249 .map_err(|err| field_error(field, format!("invalid f64: {err}"))),
250 other => Err(field_error(
251 field,
252 format!("expected number, found {}", expr_kind(other)),
253 )),
254 }
255 }
256}
257
258impl<A, B> CitizenField for (A, B)
259where
260 A: CitizenField,
261 B: CitizenField,
262{
263 fn encode_field(&self) -> Expr {
264 Expr::List(vec![self.0.encode_field(), self.1.encode_field()])
265 }
266
267 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
268 let Expr::List(items) = expr else {
269 return Err(field_error(
270 field,
271 format!("expected pair list, found {}", expr_kind(expr)),
272 ));
273 };
274 let [first, second] = items.as_slice() else {
275 return Err(field_error(
276 field,
277 format!("expected pair list with 2 item(s), found {}", items.len()),
278 ));
279 };
280 Ok((
281 A::decode_field_expr(first, field)?,
282 B::decode_field_expr(second, field)?,
283 ))
284 }
285}
286
287impl<T> CitizenField for Vec<T>
288where
289 T: CitizenField,
290{
291 fn encode_field(&self) -> Expr {
292 Expr::List(self.iter().map(CitizenField::encode_field).collect())
293 }
294
295 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
296 match expr {
297 Expr::List(items) => items
298 .iter()
299 .map(|item| T::decode_field_expr(item, field))
300 .collect(),
301 other => Err(field_error(
302 field,
303 format!("expected list, found {}", expr_kind(other)),
304 )),
305 }
306 }
307}
308
309impl<T> CitizenField for Option<T>
310where
311 T: CitizenField,
312{
313 fn encode_field(&self) -> Expr {
314 self.as_ref()
315 .map(CitizenField::encode_field)
316 .unwrap_or(Expr::Nil)
317 }
318
319 fn decode_field_expr(expr: &Expr, field: &'static str) -> Result<Self> {
320 match expr {
321 Expr::Nil => Ok(None),
322 other => T::decode_field_expr(other, field).map(Some),
323 }
324 }
325}
326
327fn int_expr(canonical: String) -> Expr {
328 Expr::Number(NumberLiteral {
329 domain: Symbol::qualified("citizen", "int"),
330 canonical,
331 })
332}
333
334fn decode_integer_text(expr: &Expr, field: &'static str) -> Result<i128> {
335 match expr {
336 Expr::Number(number) => number
337 .canonical
338 .parse::<i128>()
339 .map_err(|err| field_error(field, format!("invalid integer: {err}"))),
340 other => Err(field_error(
341 field,
342 format!("expected integer number, found {}", expr_kind(other)),
343 )),
344 }
345}
346
347fn canonical_f64(value: f64) -> String {
348 if value.is_nan() {
349 "NaN".to_owned()
350 } else if value == f64::INFINITY {
351 "inf".to_owned()
352 } else if value == f64::NEG_INFINITY {
353 "-inf".to_owned()
354 } else {
355 value.to_string()
356 }
357}