sim_lib_interference_runtime/
citizen.rs1use sim_citizen::{CitizenField, CitizenRegistry, CitizenRuntime};
4use sim_kernel::{
5 AbiVersion, Cx, Error, Export, Expr, Lib, LibManifest, LibTarget, Linker, Result, Symbol,
6 Value, Version,
7};
8
9use crate::{
10 EmitterDescriptor, MediumDescriptor, PhasorFieldDescriptor, PlaneDescriptor, ProblemDescriptor,
11 ProjectionCertificateDescriptor, ProjectionRequestDescriptor, SamplingCertificateDescriptor,
12 ScalarProjectionDescriptor, StudyDescriptor, StudyEvidenceDescriptor, WorkEstimateDescriptor,
13};
14
15pub(crate) trait RecordCitizenSpec:
16 Clone + core::fmt::Debug + PartialEq + Send + Sync + 'static
17{
18 const FIELDS: &'static [&'static str];
19
20 fn encode_fields(&self, cx: &mut Cx) -> Result<Vec<Expr>>;
21 fn decode_fields(cx: &mut Cx, fields: Vec<Value>) -> Result<Self>;
22 fn example() -> Self;
23 fn validate(&self) -> Result<()>;
24}
25
26macro_rules! impl_record_citizen {
27 ($ty:ty, $symbol:literal, $arity:literal) => {
28 impl ::sim_citizen::Citizen for $ty {
29 fn citizen_symbol() -> ::sim_kernel::Symbol {
30 ::sim_citizen::parse_symbol($symbol)
31 }
32
33 fn citizen_version() -> u32 {
34 1
35 }
36
37 fn citizen_arity() -> usize {
38 <Self as $crate::citizen::RecordCitizenSpec>::FIELDS.len()
39 }
40
41 fn citizen_fields() -> &'static [&'static str] {
42 <Self as $crate::citizen::RecordCitizenSpec>::FIELDS
43 }
44 }
45
46 impl ::sim_kernel::Object for $ty {
47 fn display(&self, cx: &mut ::sim_kernel::Cx) -> ::sim_kernel::Result<String> {
48 let fields = <Self as $crate::citizen::RecordCitizenSpec>::encode_fields(self, cx)?;
49 Ok(format!("#<citizen {}:{}>", $symbol, fields.len()))
50 }
51
52 fn as_any(&self) -> &dyn ::std::any::Any {
53 self
54 }
55 }
56
57 impl ::sim_kernel::ObjectCompat for $ty {
58 fn class(
59 &self,
60 cx: &mut ::sim_kernel::Cx,
61 ) -> ::sim_kernel::Result<::sim_kernel::ClassRef> {
62 let symbol = <Self as ::sim_citizen::Citizen>::citizen_symbol();
63 if let Some(value) = cx.registry().class_by_symbol(&symbol) {
64 return Ok(value.clone());
65 }
66 ::sim_kernel::Factory::class_stub(cx.factory(), ::sim_kernel::ClassId(0), symbol)
67 }
68
69 fn as_expr(
70 &self,
71 cx: &mut ::sim_kernel::Cx,
72 ) -> ::sim_kernel::Result<::sim_kernel::Expr> {
73 ::sim_citizen::constructor_expr(cx, self)
74 }
75
76 fn as_object_encoder(&self) -> Option<&dyn ::sim_kernel::ObjectEncode> {
77 Some(self)
78 }
79 }
80
81 impl ::sim_kernel::ObjectEncode for $ty {
82 fn object_encoding(
83 &self,
84 cx: &mut ::sim_kernel::Cx,
85 ) -> ::sim_kernel::Result<::sim_kernel::ObjectEncoding> {
86 <Self as $crate::citizen::RecordCitizenSpec>::validate(self)?;
87 let mut args = vec![::sim_kernel::Expr::Symbol(::sim_kernel::Symbol::new("v1"))];
88 args.extend(<Self as $crate::citizen::RecordCitizenSpec>::encode_fields(
89 self, cx,
90 )?);
91 Ok(::sim_kernel::ObjectEncoding::Constructor {
92 class: <Self as ::sim_citizen::Citizen>::citizen_symbol(),
93 args,
94 })
95 }
96 }
97
98 impl ::sim_citizen::CitizenRuntime for $ty {
99 fn citizen_info() -> ::sim_citizen::CitizenInfo {
100 ::sim_citizen::CitizenInfo {
101 symbol: $symbol,
102 version: 1,
103 crate_name: env!("CARGO_PKG_NAME"),
104 arity: <Self as ::sim_citizen::Citizen>::citizen_arity(),
105 install: <Self as ::sim_citizen::CitizenRuntime>::install,
106 conformance: <Self as ::sim_citizen::CitizenRuntime>::conformance,
107 }
108 }
109
110 fn conformance(cx: &mut ::sim_kernel::Cx) -> ::sim_kernel::Result<()> {
111 ::sim_citizen::check_fixture(
112 cx,
113 <Self as $crate::citizen::RecordCitizenSpec>::example(),
114 )
115 }
116
117 fn construct_from_values(
118 cx: &mut ::sim_kernel::Cx,
119 args: Vec<::sim_kernel::Value>,
120 ) -> ::sim_kernel::Result<Self> {
121 let expected = <Self as ::sim_citizen::Citizen>::citizen_arity() + 1;
122 if args.len() != expected {
123 return Err(::sim_citizen::arity_error(
124 <Self as ::sim_citizen::Citizen>::citizen_symbol(),
125 expected,
126 args.len(),
127 ));
128 }
129 let mut args = args.into_iter();
130 let version = args.next().expect("arity checked");
131 ::sim_citizen::decode_version(
132 cx,
133 version,
134 1,
135 <Self as ::sim_citizen::Citizen>::citizen_symbol(),
136 )?;
137 let value = <Self as $crate::citizen::RecordCitizenSpec>::decode_fields(
138 cx,
139 args.collect(),
140 )?;
141 <Self as $crate::citizen::RecordCitizenSpec>::validate(&value)?;
142 Ok(value)
143 }
144
145 fn example() -> Self {
146 <Self as $crate::citizen::RecordCitizenSpec>::example()
147 }
148 }
149
150 const _: () = {
151 ::sim_citizen::inventory::submit! {
152 ::sim_citizen::CitizenInfo {
153 symbol: $symbol,
154 version: 1,
155 crate_name: env!("CARGO_PKG_NAME"),
156 arity: $arity,
157 install: <$ty as ::sim_citizen::CitizenRuntime>::install,
158 conformance: <$ty as ::sim_citizen::CitizenRuntime>::conformance,
159 }
160 }
161 };
162 };
163}
164
165pub(crate) fn invalid(record: &str, message: impl Into<String>) -> Error {
166 Error::Eval(format!("interference {record}: {}", message.into()))
167}
168
169pub(crate) fn next_field<T>(
170 cx: &mut Cx,
171 fields: &mut impl Iterator<Item = Value>,
172 name: &'static str,
173) -> Result<T>
174where
175 T: CitizenField,
176{
177 T::decode_field_value(
178 cx,
179 fields
180 .next()
181 .ok_or_else(|| invalid("record", format!("missing field {name}")))?,
182 name,
183 )
184}
185
186pub(crate) fn encode_field<T: CitizenField>(value: &T) -> Expr {
187 value.encode_field()
188}
189
190pub(crate) fn encode_record<T>(cx: &mut Cx, value: &T) -> Result<Expr>
191where
192 T: CitizenRuntime,
193{
194 sim_citizen::constructor_expr(cx, value)
195}
196
197pub(crate) fn decode_record<T>(cx: &mut Cx, value: Value, field: &'static str) -> Result<T>
198where
199 T: CitizenRuntime,
200{
201 let expr = sim_citizen::value_to_expr(cx, value, field)?;
202 decode_record_expr(cx, &expr, field)
203}
204
205pub(crate) fn decode_record_expr<T>(cx: &mut Cx, expr: &Expr, field: &'static str) -> Result<T>
206where
207 T: CitizenRuntime,
208{
209 let (class, args) = read_construct_parts(expr, field)?;
210 if class != T::citizen_symbol() {
211 return Err(invalid(
212 field,
213 format!("expected nested {}, found {class}", T::citizen_symbol()),
214 ));
215 }
216 let values = args
217 .iter()
218 .map(|arg| sim_citizen::value_from_expr(cx, arg))
219 .collect::<Result<Vec<_>>>()?;
220 T::construct_from_values(cx, values)
221}
222
223pub(crate) fn encode_records<T>(cx: &mut Cx, values: &[T]) -> Result<Expr>
224where
225 T: CitizenRuntime,
226{
227 values
228 .iter()
229 .map(|value| encode_record(cx, value))
230 .collect::<Result<Vec<_>>>()
231 .map(Expr::List)
232}
233
234pub(crate) fn decode_records<T>(cx: &mut Cx, value: Value, field: &'static str) -> Result<Vec<T>>
235where
236 T: CitizenRuntime,
237{
238 let expr = sim_citizen::value_to_expr(cx, value, field)?;
239 let Expr::List(items) = expr else {
240 return Err(invalid(field, "expected a list of nested citizens"));
241 };
242 items
243 .iter()
244 .map(|item| decode_record_expr(cx, item, field))
245 .collect()
246}
247
248pub(crate) fn read_construct_parts<'a>(
249 expr: &'a Expr,
250 field: &'static str,
251) -> Result<(Symbol, &'a [Expr])> {
252 let Expr::Extension { tag, payload } = expr else {
253 return Err(invalid(field, "expected a read-construct extension"));
254 };
255 if *tag != Symbol::qualified("citizen", "read-construct") {
256 return Err(invalid(
257 field,
258 format!("expected citizen/read-construct, found {tag}"),
259 ));
260 }
261 let Expr::Vector(items) = payload.as_ref() else {
262 return Err(invalid(field, "read-construct payload must be a vector"));
263 };
264 let Some((Expr::Symbol(class), args)) = items.split_first() else {
265 return Err(invalid(
266 field,
267 "read-construct payload must start with a class symbol",
268 ));
269 };
270 Ok((class.clone(), args))
271}
272
273pub fn interference_citizen_registry() -> Result<CitizenRegistry> {
275 let mut registry = CitizenRegistry::new();
276 registry
277 .register::<MediumDescriptor>()?
278 .register::<EmitterDescriptor>()?
279 .register::<ProblemDescriptor>()?
280 .register::<PlaneDescriptor>()?
281 .register::<SamplingCertificateDescriptor>()?
282 .register::<WorkEstimateDescriptor>()?
283 .register::<PhasorFieldDescriptor>()?
284 .register::<StudyEvidenceDescriptor>()?
285 .register::<StudyDescriptor>()?
286 .register::<ProjectionCertificateDescriptor>()?
287 .register::<ProjectionRequestDescriptor>()?
288 .register::<ScalarProjectionDescriptor>()?;
289 Ok(registry)
290}
291
292pub struct InterferenceRecordsLib;
294
295impl Lib for InterferenceRecordsLib {
296 fn manifest(&self) -> LibManifest {
297 let mut exports = RECORD_SYMBOLS
298 .iter()
299 .map(|symbol| Export::Class {
300 symbol: sim_citizen::parse_symbol(symbol),
301 class_id: None,
302 })
303 .collect::<Vec<_>>();
304 exports.extend(
305 crate::shapes::interference_shape_symbols()
306 .into_iter()
307 .map(|symbol| Export::Shape {
308 symbol,
309 shape_id: None,
310 }),
311 );
312 LibManifest {
313 id: records_lib_symbol(),
314 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
315 abi: AbiVersion { major: 0, minor: 1 },
316 target: LibTarget::HostRegistered,
317 requires: Vec::new(),
318 capabilities: Vec::new(),
319 exports,
320 }
321 }
322
323 fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
324 interference_citizen_registry()?.install_all(linker)?;
325 crate::shapes::register_interference_shapes(linker)
326 }
327}
328
329pub fn install_interference_records(cx: &mut Cx) -> Result<()> {
331 if cx.registry().lib(&records_lib_symbol()).is_none() {
332 cx.load_lib(&InterferenceRecordsLib)?;
333 }
334 Ok(())
335}
336
337fn records_lib_symbol() -> Symbol {
338 Symbol::qualified("sim", "interference-records")
339}
340
341const RECORD_SYMBOLS: [&str; 12] = [
342 "interference/Medium",
343 "interference/Emitter",
344 "interference/Problem",
345 "interference/Plane",
346 "interference/SamplingCertificate",
347 "interference/WorkEstimate",
348 "interference/PhasorField",
349 "interference/StudyEvidence",
350 "interference/Study",
351 "interference/ProjectionCertificate",
352 "interference/ProjectionRequest",
353 "interference/Projection",
354];