1use std::sync::Arc;
4
5use sim_kernel::{
6 AbiVersion, Args, CORE_CLASS_CLASS_ID, CORE_FUNCTION_CLASS_ID, Callable, Class, ClassId,
7 ClassRef, Cx, DefaultFactory, Export, Expr, Factory, Lib, LibManifest, LibTarget, Linker,
8 Object, ObjectCompat, ObjectEncode, ObjectEncoding, ReadConstructor, ReadConstructorRef,
9 Result, ShapeRef, Symbol, TableRef, Value, Version,
10};
11
12use crate::{
13 DeviceCaps, DeviceSample,
14 sample::{decode_known_sample, sample_constructor_args},
15};
16
17const DEVICE_SAMPLE_CLASS_ID: ClassId = ClassId(6201);
18
19#[derive(Clone)]
25pub struct DeviceSampleValue {
26 sample: Expr,
27}
28
29impl DeviceSampleValue {
30 pub fn new(sample: Expr) -> Result<Self> {
32 decode_known_sample(&sample)?;
33 Ok(Self { sample })
34 }
35
36 pub fn sample(&self) -> &Expr {
38 &self.sample
39 }
40
41 pub fn device_caps(&self) -> Result<DeviceCaps> {
43 Ok(DeviceCaps::from_expr(&self.sample)?)
44 }
45}
46
47impl Object for DeviceSampleValue {
48 fn display(&self, _cx: &mut Cx) -> Result<String> {
49 Ok("#<stream-device-sample>".to_owned())
50 }
51
52 fn as_any(&self) -> &dyn std::any::Any {
53 self
54 }
55}
56
57impl ObjectCompat for DeviceSampleValue {
58 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
59 class_value_or_stub(cx)
60 }
61
62 fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
63 Ok(Expr::Call {
64 operator: Box::new(Expr::Symbol(device_sample_class_symbol())),
65 args: sample_constructor_args(&self.sample)?,
66 })
67 }
68
69 fn as_table(&self, cx: &mut Cx) -> Result<Value> {
70 let Expr::Map(entries) = self.sample.clone() else {
71 unreachable!("validated device samples are maps");
72 };
73 cx.factory().table(
74 entries
75 .into_iter()
76 .map(|(key, value)| match key {
77 Expr::Symbol(symbol) => Ok((symbol, cx.factory().expr(value)?)),
78 _ => unreachable!("device sample map keys are symbols"),
79 })
80 .collect::<Result<Vec<_>>>()?,
81 )
82 }
83
84 fn as_object_encoder(&self) -> Option<&dyn ObjectEncode> {
85 Some(self)
86 }
87}
88
89impl ObjectEncode for DeviceSampleValue {
90 fn object_encoding(&self, _cx: &mut Cx) -> Result<ObjectEncoding> {
91 Ok(ObjectEncoding::Constructor {
92 class: device_sample_class_symbol(),
93 args: sample_constructor_args(&self.sample)?,
94 })
95 }
96}
97
98impl sim_citizen::Citizen for DeviceSampleValue {
99 fn citizen_symbol() -> Symbol {
100 device_sample_class_symbol()
101 }
102
103 fn citizen_version() -> u32 {
104 0
105 }
106
107 fn citizen_arity() -> usize {
108 1
109 }
110
111 fn citizen_fields() -> &'static [&'static str] {
112 &["sample"]
113 }
114}
115
116pub struct DeviceStreamBaseLib;
118
119impl Lib for DeviceStreamBaseLib {
120 fn manifest(&self) -> LibManifest {
121 LibManifest {
122 id: device_stream_base_manifest_symbol(),
123 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
124 abi: AbiVersion { major: 0, minor: 1 },
125 target: LibTarget::HostRegistered,
126 requires: Vec::new(),
127 capabilities: Vec::new(),
128 exports: device_stream_base_exports(),
129 }
130 }
131
132 fn load(&self, cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
133 register_device_sample_class(linker)?;
134 linker.value(
135 crate::device_caps_sample_kind_symbol(),
136 cx.factory()
137 .expr(Expr::Symbol(crate::device_caps_sample_kind_symbol()))?,
138 )?;
139 Ok(())
140 }
141}
142
143pub fn install_device_stream_base(cx: &mut Cx) -> Result<()> {
145 sim_lib_core::install_once(cx, &DeviceStreamBaseLib).map(|_| ())
146}
147
148pub fn device_stream_base_exports() -> Vec<Export> {
150 vec![
151 Export::Class {
152 symbol: device_sample_class_symbol(),
153 class_id: Some(DEVICE_SAMPLE_CLASS_ID),
154 },
155 Export::Value {
156 symbol: crate::device_caps_sample_kind_symbol(),
157 },
158 ]
159}
160
161pub fn device_stream_base_manifest_symbol() -> Symbol {
163 Symbol::qualified("stream", "device-base")
164}
165
166pub fn device_sample_class_symbol() -> Symbol {
168 Symbol::qualified("stream", "DeviceSample")
169}
170
171fn register_device_sample_class(linker: &mut Linker<'_>) -> Result<()> {
172 let class = DefaultFactory
173 .opaque(Arc::new(DeviceSampleClass))
174 .expect("device sample class should be boxable");
175 let id = linker.class_with_id(device_sample_class_symbol(), DEVICE_SAMPLE_CLASS_ID)?;
176 linker.bind_class_value(id, class)?;
177 Ok(())
178}
179
180fn install_device_sample_citizen(linker: &mut Linker<'_>) -> Result<()> {
181 register_device_sample_class(linker)
182}
183
184fn conformance_device_sample_citizen(cx: &mut Cx) -> Result<()> {
185 let value = cx.factory().opaque(Arc::new(DeviceSampleValue::new(
186 DeviceCaps::demo(0).to_expr(),
187 )?))?;
188 sim_citizen::check_value_fixture(cx, value)
189}
190
191sim_citizen::inventory::submit! {
192 sim_citizen::CitizenInfo {
193 symbol: "stream/DeviceSample",
194 version: 0,
195 crate_name: env!("CARGO_PKG_NAME"),
196 arity: 1,
197 install: install_device_sample_citizen,
198 conformance: conformance_device_sample_citizen,
199 }
200}
201
202#[derive(Clone)]
203struct DeviceSampleClass;
204
205impl Object for DeviceSampleClass {
206 fn display(&self, _cx: &mut Cx) -> Result<String> {
207 Ok("#<class stream/DeviceSample>".to_owned())
208 }
209
210 fn as_any(&self) -> &dyn std::any::Any {
211 self
212 }
213}
214
215impl ObjectCompat for DeviceSampleClass {
216 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
217 let symbol = Symbol::qualified("core", "Class");
218 if let Some(value) = cx.registry().class_by_symbol(&symbol) {
219 return Ok(value.clone());
220 }
221 cx.factory().class_stub(CORE_CLASS_CLASS_ID, symbol)
222 }
223
224 fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
225 Ok(Expr::Symbol(device_sample_class_symbol()))
226 }
227
228 fn as_callable(&self) -> Option<&dyn Callable> {
229 Some(self)
230 }
231
232 fn as_class(&self) -> Option<&dyn Class> {
233 Some(self)
234 }
235}
236
237impl Callable for DeviceSampleClass {
238 fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
239 construct_device_sample_value(cx, args.into_vec())
240 }
241}
242
243impl Class for DeviceSampleClass {
244 fn id(&self) -> ClassId {
245 DEVICE_SAMPLE_CLASS_ID
246 }
247
248 fn symbol(&self) -> Symbol {
249 device_sample_class_symbol()
250 }
251
252 fn constructor_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
253 cx.factory().nil()
254 }
255
256 fn instance_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
257 cx.factory().nil()
258 }
259
260 fn read_constructor(&self, _cx: &mut Cx) -> Result<Option<ReadConstructorRef>> {
261 Ok(Some(
262 DefaultFactory.opaque(Arc::new(DeviceSampleReadConstructor))?,
263 ))
264 }
265
266 fn members(&self, cx: &mut Cx) -> Result<TableRef> {
267 cx.factory().table(Vec::new())
268 }
269}
270
271#[derive(Clone)]
272struct DeviceSampleReadConstructor;
273
274impl Object for DeviceSampleReadConstructor {
275 fn display(&self, _cx: &mut Cx) -> Result<String> {
276 Ok("#<read-constructor stream/DeviceSample>".to_owned())
277 }
278
279 fn as_any(&self) -> &dyn std::any::Any {
280 self
281 }
282}
283
284impl ObjectCompat for DeviceSampleReadConstructor {
285 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
286 let symbol = Symbol::qualified("core", "Function");
287 if let Some(value) = cx.registry().class_by_symbol(&symbol) {
288 return Ok(value.clone());
289 }
290 cx.factory().class_stub(CORE_FUNCTION_CLASS_ID, symbol)
291 }
292
293 fn as_read_constructor(&self) -> Option<&dyn ReadConstructor> {
294 Some(self)
295 }
296}
297
298impl ReadConstructor for DeviceSampleReadConstructor {
299 fn symbol(&self) -> Symbol {
300 device_sample_class_symbol()
301 }
302
303 fn args_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
304 cx.factory().nil()
305 }
306
307 fn construct_read(&self, cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
308 construct_device_sample_value(cx, args)
309 }
310}
311
312fn construct_device_sample_value(cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
313 let [sample] = args.as_slice() else {
314 return Err(sim_kernel::Error::Eval(
315 "stream/DeviceSample expects one constructor argument".to_owned(),
316 ));
317 };
318 let expr = sample.object().as_expr(cx)?;
319 cx.factory().opaque(Arc::new(DeviceSampleValue::new(expr)?))
320}
321
322fn class_value_or_stub(cx: &mut Cx) -> Result<Value> {
323 if let Some(value) = cx.registry().class_by_symbol(&device_sample_class_symbol()) {
324 return Ok(value.clone());
325 }
326 cx.factory()
327 .class_stub(DEVICE_SAMPLE_CLASS_ID, device_sample_class_symbol())
328}