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::{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 construct_from_values(cx: &mut Cx, args: Vec<Value>) -> Result<Self>;
68 fn example() -> Self;
70}
71
72pub fn install_derived<T>(linker: &mut sim_kernel::Linker<'_>) -> Result<()>
78where
79 T: CitizenRuntime,
80{
81 let class = Arc::new(DerivedCitizenClass::<T>::new());
82 let id = linker.class_value(
83 T::citizen_symbol(),
84 DefaultFactory
85 .opaque(class.clone())
86 .expect("citizen class should be boxable"),
87 )?;
88 class.set_id(id);
89 Ok(())
90}
91
92pub fn constructor_expr<T>(cx: &mut Cx, value: &T) -> Result<Expr>
98where
99 T: Citizen + ObjectEncode,
100{
101 match value.object_encoding(cx)? {
102 ObjectEncoding::Constructor { class, args } => Ok(Expr::Extension {
103 tag: Symbol::qualified("citizen", "read-construct"),
104 payload: Box::new(Expr::Vector(
105 std::iter::once(Expr::Symbol(class)).chain(args).collect(),
106 )),
107 }),
108 ObjectEncoding::TaggedData { tag, fields } => Ok(Expr::Extension {
109 tag,
110 payload: Box::new(Expr::Map(
111 fields
112 .into_iter()
113 .map(|(key, value)| (Expr::Symbol(key), value))
114 .collect(),
115 )),
116 }),
117 ObjectEncoding::Opaque { class, stable_id } => Ok(Expr::Extension {
118 tag: class,
119 payload: Box::new(Expr::String(stable_id)),
120 }),
121 }
122}
123
124pub struct DerivedCitizenClass<T> {
125 id: AtomicU32,
126 marker: PhantomData<T>,
127}
128
129impl<T> DerivedCitizenClass<T> {
130 fn new() -> Self {
131 Self {
132 id: AtomicU32::new(0),
133 marker: PhantomData,
134 }
135 }
136
137 fn set_id(&self, id: ClassId) {
138 self.id.store(id.0, Ordering::Relaxed);
139 }
140}
141
142impl<T> Object for DerivedCitizenClass<T>
143where
144 T: CitizenRuntime,
145{
146 fn display(&self, _cx: &mut Cx) -> Result<String> {
147 Ok(format!("#<class {}>", T::citizen_symbol()))
148 }
149
150 fn as_any(&self) -> &dyn std::any::Any {
151 self
152 }
153}
154
155impl<T> sim_kernel::ObjectCompat for DerivedCitizenClass<T>
156where
157 T: CitizenRuntime,
158{
159 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
160 if let Some(value) = cx
161 .registry()
162 .class_by_symbol(&Symbol::qualified("core", "Class"))
163 {
164 return Ok(value.clone());
165 }
166 cx.factory().class_stub(
167 sim_kernel::CORE_CLASS_CLASS_ID,
168 Symbol::qualified("core", "Class"),
169 )
170 }
171
172 fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
173 Ok(Expr::Symbol(T::citizen_symbol()))
174 }
175
176 fn as_callable(&self) -> Option<&dyn Callable> {
177 Some(self)
178 }
179
180 fn as_class(&self) -> Option<&dyn Class> {
181 Some(self)
182 }
183
184 fn as_read_constructor(&self) -> Option<&dyn ReadConstructor> {
185 Some(self)
186 }
187}
188
189impl<T> Callable for DerivedCitizenClass<T>
190where
191 T: CitizenRuntime,
192{
193 fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
194 let value = T::construct_from_values(cx, args.into_vec())?;
195 cx.factory().opaque(Arc::new(value))
196 }
197}
198
199impl<T> Class for DerivedCitizenClass<T>
200where
201 T: CitizenRuntime,
202{
203 fn id(&self) -> ClassId {
204 ClassId(self.id.load(Ordering::Relaxed))
205 }
206
207 fn symbol(&self) -> Symbol {
208 T::citizen_symbol()
209 }
210
211 fn constructor_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
212 cx.factory().nil()
213 }
214
215 fn instance_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
216 cx.factory().nil()
217 }
218
219 fn read_constructor(&self, cx: &mut Cx) -> Result<Option<ReadConstructorRef>> {
220 Ok(cx.registry().class_by_symbol(&T::citizen_symbol()).cloned())
221 }
222
223 fn members(&self, cx: &mut Cx) -> Result<TableRef> {
224 let fields = T::citizen_fields()
225 .iter()
226 .map(|field| cx.factory().symbol(Symbol::new((*field).to_owned())))
227 .collect::<Result<Vec<_>>>()?;
228 cx.factory().table(vec![
229 (
230 Symbol::new("version"),
231 cx.factory().number_literal(
232 parse_symbol("citizen/int"),
233 T::citizen_version().to_string(),
234 )?,
235 ),
236 (Symbol::new("fields"), cx.factory().list(fields)?),
237 ])
238 }
239}
240
241impl<T> ReadConstructor for DerivedCitizenClass<T>
242where
243 T: CitizenRuntime,
244{
245 fn symbol(&self) -> Symbol {
246 T::citizen_symbol()
247 }
248
249 fn args_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
250 cx.factory().nil()
251 }
252
253 fn construct_read(&self, cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
254 if args.len() != T::citizen_arity() + 1 {
255 return Err(arity_error(
256 T::citizen_symbol(),
257 T::citizen_arity() + 1,
258 args.len(),
259 ));
260 }
261 self.call(cx, Args::new(args))
262 }
263}