1use std::{
4 fmt::Debug,
5 marker::PhantomData,
6 sync::{
7 Arc,
8 atomic::{AtomicU32, Ordering},
9 },
10};
11
12use sim_kernel::{
13 Args, Callable, Class, ClassId, ClassRef, Cx, DefaultFactory, Expr, Factory, Object,
14 ObjectEncode, ObjectEncoding, ReadConstructor, ReadConstructorRef, Result, ShapeRef, Symbol,
15 TableRef, Value,
16};
17
18use crate::{CitizenInfo, arity_error, parse_symbol};
19
20pub trait Citizen: Clone + Send + Sync + 'static {
47 fn citizen_symbol() -> Symbol;
49 fn citizen_version() -> u32;
51 fn citizen_arity() -> usize;
53 fn citizen_fields() -> &'static [&'static str];
55}
56
57pub trait CitizenRuntime:
64 Citizen + Object + sim_kernel::ObjectCompat + ObjectEncode + PartialEq + Debug
65{
66 fn citizen_info() -> CitizenInfo;
73 fn install(linker: &mut sim_kernel::Linker<'_>) -> Result<()>
78 where
79 Self: Sized,
80 {
81 install_derived::<Self>(linker)
82 }
83 fn conformance(cx: &mut Cx) -> Result<()>;
85 fn construct_from_values(cx: &mut Cx, args: Vec<Value>) -> Result<Self>;
87 fn example() -> Self;
89}
90
91pub fn install_derived<T>(linker: &mut sim_kernel::Linker<'_>) -> Result<()>
97where
98 T: CitizenRuntime,
99{
100 let class = Arc::new(DerivedCitizenClass::<T>::new());
101 let id = linker.class_value(
102 T::citizen_symbol(),
103 DefaultFactory
104 .opaque(class.clone())
105 .expect("citizen class should be boxable"),
106 )?;
107 class.set_id(id);
108 Ok(())
109}
110
111pub fn constructor_expr<T>(cx: &mut Cx, value: &T) -> Result<Expr>
117where
118 T: Citizen + ObjectEncode,
119{
120 match value.object_encoding(cx)? {
121 ObjectEncoding::Constructor { class, args } => Ok(Expr::Extension {
122 tag: Symbol::qualified("citizen", "read-construct"),
123 payload: Box::new(Expr::Vector(
124 std::iter::once(Expr::Symbol(class)).chain(args).collect(),
125 )),
126 }),
127 ObjectEncoding::TaggedData { tag, fields } => Ok(Expr::Extension {
128 tag,
129 payload: Box::new(Expr::Map(
130 fields
131 .into_iter()
132 .map(|(key, value)| (Expr::Symbol(key), value))
133 .collect(),
134 )),
135 }),
136 ObjectEncoding::Opaque { class, stable_id } => Ok(Expr::Extension {
137 tag: class,
138 payload: Box::new(Expr::String(stable_id)),
139 }),
140 }
141}
142
143pub struct DerivedCitizenClass<T> {
144 id: AtomicU32,
145 marker: PhantomData<T>,
146}
147
148impl<T> DerivedCitizenClass<T> {
149 fn new() -> Self {
150 Self {
151 id: AtomicU32::new(0),
152 marker: PhantomData,
153 }
154 }
155
156 fn set_id(&self, id: ClassId) {
157 self.id.store(id.0, Ordering::Relaxed);
158 }
159}
160
161impl<T> Object for DerivedCitizenClass<T>
162where
163 T: CitizenRuntime,
164{
165 fn display(&self, _cx: &mut Cx) -> Result<String> {
166 Ok(format!("#<class {}>", T::citizen_symbol()))
167 }
168
169 fn as_any(&self) -> &dyn std::any::Any {
170 self
171 }
172}
173
174impl<T> sim_kernel::ObjectCompat for DerivedCitizenClass<T>
175where
176 T: CitizenRuntime,
177{
178 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
179 if let Some(value) = cx
180 .registry()
181 .class_by_symbol(&Symbol::qualified("core", "Class"))
182 {
183 return Ok(value.clone());
184 }
185 cx.factory().class_stub(
186 sim_kernel::CORE_CLASS_CLASS_ID,
187 Symbol::qualified("core", "Class"),
188 )
189 }
190
191 fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
192 Ok(Expr::Symbol(T::citizen_symbol()))
193 }
194
195 fn as_callable(&self) -> Option<&dyn Callable> {
196 Some(self)
197 }
198
199 fn as_class(&self) -> Option<&dyn Class> {
200 Some(self)
201 }
202
203 fn as_read_constructor(&self) -> Option<&dyn ReadConstructor> {
204 Some(self)
205 }
206}
207
208impl<T> Callable for DerivedCitizenClass<T>
209where
210 T: CitizenRuntime,
211{
212 fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
213 let value = T::construct_from_values(cx, args.into_vec())?;
214 cx.factory().opaque(Arc::new(value))
215 }
216}
217
218impl<T> Class for DerivedCitizenClass<T>
219where
220 T: CitizenRuntime,
221{
222 fn id(&self) -> ClassId {
223 ClassId(self.id.load(Ordering::Relaxed))
224 }
225
226 fn symbol(&self) -> Symbol {
227 T::citizen_symbol()
228 }
229
230 fn constructor_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
231 any_shape(cx)
232 }
233
234 fn instance_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
235 any_shape(cx)
236 }
237
238 fn read_constructor(&self, cx: &mut Cx) -> Result<Option<ReadConstructorRef>> {
239 Ok(cx.registry().class_by_symbol(&T::citizen_symbol()).cloned())
240 }
241
242 fn members(&self, cx: &mut Cx) -> Result<TableRef> {
243 citizen_metadata_table(
244 cx,
245 T::citizen_version(),
246 T::citizen_arity(),
247 T::citizen_fields(),
248 )
249 }
250}
251
252impl<T> ReadConstructor for DerivedCitizenClass<T>
253where
254 T: CitizenRuntime,
255{
256 fn symbol(&self) -> Symbol {
257 T::citizen_symbol()
258 }
259
260 fn args_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
261 any_shape(cx)
262 }
263
264 fn construct_read(&self, cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
265 if args.len() != T::citizen_arity() + 1 {
266 return Err(arity_error(
267 T::citizen_symbol(),
268 T::citizen_arity() + 1,
269 args.len(),
270 ));
271 }
272 self.call(cx, Args::new(args))
273 }
274}
275
276fn any_shape(cx: &mut Cx) -> Result<ShapeRef> {
277 if let Some(shape) = cx
278 .registry()
279 .shape_by_symbol(&Symbol::qualified("core", "Any"))
280 {
281 Ok(shape.clone())
282 } else {
283 cx.factory().nil()
284 }
285}
286
287fn citizen_metadata_table(
288 cx: &mut Cx,
289 version: u32,
290 arity: usize,
291 fields: &[&str],
292) -> Result<TableRef> {
293 let fields = fields
294 .iter()
295 .map(|field| cx.factory().symbol(Symbol::new((*field).to_owned())))
296 .collect::<Result<Vec<_>>>()?;
297 cx.factory().table(vec![
298 (
299 Symbol::new("version"),
300 cx.factory()
301 .number_literal(parse_symbol("citizen/int"), version.to_string())?,
302 ),
303 (
304 Symbol::new("arity"),
305 cx.factory()
306 .number_literal(parse_symbol("citizen/int"), arity.to_string())?,
307 ),
308 (Symbol::new("fields"), cx.factory().list(fields)?),
309 ])
310}