1use std::sync::Arc;
11
12use sim_kernel::{
13 Args, CORE_CLASS_CLASS_ID, CORE_FUNCTION_CLASS_ID, Callable, Class, ClassId, ClassRef, Cx,
14 DefaultFactory, Expr, Factory, Linker, Object, ObjectCompat, ObjectEncode, ObjectEncoding,
15 ReadConstructor, ReadConstructorRef, Result, ShapeRef, Symbol, TableRef, Value,
16};
17
18use crate::{
19 BufferPolicy,
20 metadata::{StreamDirection, StreamMedia, StreamMetadata},
21};
22
23const STREAM_METADATA_CLASS_ID: ClassId = ClassId(6200);
24
25#[derive(Clone)]
31pub struct StreamMetadataValue {
32 metadata: StreamMetadata,
33}
34
35impl StreamMetadataValue {
36 pub fn new(metadata: StreamMetadata) -> Self {
38 Self { metadata }
39 }
40
41 pub fn metadata(&self) -> &StreamMetadata {
43 &self.metadata
44 }
45}
46
47impl Object for StreamMetadataValue {
48 fn display(&self, _cx: &mut Cx) -> Result<String> {
49 Ok(format!("#<stream-metadata {}>", self.metadata.id()))
50 }
51
52 fn as_any(&self) -> &dyn std::any::Any {
53 self
54 }
55}
56
57impl ObjectCompat for StreamMetadataValue {
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(stream_metadata_class_symbol())),
65 args: self.metadata.to_constructor_args(),
66 })
67 }
68
69 fn as_table(&self, cx: &mut Cx) -> Result<Value> {
70 let Expr::Map(entries) = self.metadata.table_expr() else {
71 unreachable!("metadata table_expr returns a map");
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!("metadata table 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 StreamMetadataValue {
90 fn object_encoding(&self, _cx: &mut Cx) -> Result<ObjectEncoding> {
91 Ok(ObjectEncoding::Constructor {
92 class: stream_metadata_class_symbol(),
93 args: self.metadata.to_constructor_args(),
94 })
95 }
96}
97
98impl sim_citizen::Citizen for StreamMetadataValue {
99 fn citizen_symbol() -> Symbol {
100 stream_metadata_class_symbol()
101 }
102
103 fn citizen_version() -> u32 {
104 0
105 }
106
107 fn citizen_arity() -> usize {
108 5
109 }
110
111 fn citizen_fields() -> &'static [&'static str] {
112 &["id", "media", "direction", "clock", "buffer"]
113 }
114}
115
116pub fn stream_metadata_class_symbol() -> Symbol {
118 Symbol::qualified("stream", "Metadata")
119}
120
121pub fn install_stream_core_classes(cx: &mut Cx) -> Result<()> {
127 if cx
128 .registry()
129 .class_by_symbol(&stream_metadata_class_symbol())
130 .is_some()
131 {
132 return Ok(());
133 }
134 let class = cx.factory().opaque(Arc::new(StreamMetadataClass))?;
135 cx.registry_mut()
136 .register_class_value(stream_metadata_class_symbol(), class)?;
137 Ok(())
138}
139
140#[derive(Clone)]
141struct StreamMetadataClass;
142
143impl Object for StreamMetadataClass {
144 fn display(&self, _cx: &mut Cx) -> Result<String> {
145 Ok("#<class stream/Metadata>".to_owned())
146 }
147
148 fn as_any(&self) -> &dyn std::any::Any {
149 self
150 }
151}
152
153impl ObjectCompat for StreamMetadataClass {
154 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
155 let symbol = Symbol::qualified("core", "Class");
156 if let Some(value) = cx.registry().class_by_symbol(&symbol) {
157 return Ok(value.clone());
158 }
159 cx.factory().class_stub(CORE_CLASS_CLASS_ID, symbol)
160 }
161
162 fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
163 Ok(Expr::Symbol(stream_metadata_class_symbol()))
164 }
165
166 fn as_callable(&self) -> Option<&dyn Callable> {
167 Some(self)
168 }
169
170 fn as_class(&self) -> Option<&dyn Class> {
171 Some(self)
172 }
173}
174
175impl Callable for StreamMetadataClass {
176 fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
177 construct_stream_metadata_value(cx, args.into_vec())
178 }
179}
180
181impl Class for StreamMetadataClass {
182 fn id(&self) -> ClassId {
183 STREAM_METADATA_CLASS_ID
184 }
185
186 fn symbol(&self) -> Symbol {
187 stream_metadata_class_symbol()
188 }
189
190 fn constructor_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
191 cx.factory().nil()
192 }
193
194 fn instance_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
195 cx.factory().nil()
196 }
197
198 fn read_constructor(&self, _cx: &mut Cx) -> Result<Option<ReadConstructorRef>> {
199 Ok(Some(
200 DefaultFactory.opaque(Arc::new(StreamMetadataReadConstructor))?,
201 ))
202 }
203
204 fn members(&self, cx: &mut Cx) -> Result<TableRef> {
205 cx.factory().table(Vec::new())
206 }
207}
208
209#[derive(Clone)]
210struct StreamMetadataReadConstructor;
211
212impl Object for StreamMetadataReadConstructor {
213 fn display(&self, _cx: &mut Cx) -> Result<String> {
214 Ok("#<read-constructor stream/Metadata>".to_owned())
215 }
216
217 fn as_any(&self) -> &dyn std::any::Any {
218 self
219 }
220}
221
222impl ObjectCompat for StreamMetadataReadConstructor {
223 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
224 let symbol = Symbol::qualified("core", "Function");
225 if let Some(value) = cx.registry().class_by_symbol(&symbol) {
226 return Ok(value.clone());
227 }
228 cx.factory().class_stub(CORE_FUNCTION_CLASS_ID, symbol)
229 }
230
231 fn as_read_constructor(&self) -> Option<&dyn ReadConstructor> {
232 Some(self)
233 }
234}
235
236impl ReadConstructor for StreamMetadataReadConstructor {
237 fn symbol(&self) -> Symbol {
238 stream_metadata_class_symbol()
239 }
240
241 fn args_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
242 cx.factory().nil()
243 }
244
245 fn construct_read(&self, cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
246 construct_stream_metadata_value(cx, args)
247 }
248}
249
250fn construct_stream_metadata_value(cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
251 let mut exprs = Vec::with_capacity(args.len());
252 for value in args {
253 exprs.push(value.object().as_expr(cx)?);
254 }
255 cx.factory().opaque(Arc::new(StreamMetadataValue::new(
256 StreamMetadata::from_constructor_args(exprs)?,
257 )))
258}
259
260fn register_stream_metadata_class(linker: &mut Linker<'_>) -> Result<()> {
261 let class = DefaultFactory
262 .opaque(Arc::new(StreamMetadataClass))
263 .expect("stream metadata class should be boxable");
264 linker.class_value(stream_metadata_class_symbol(), class)?;
265 Ok(())
266}
267
268fn install_stream_metadata_citizen(linker: &mut Linker<'_>) -> Result<()> {
269 register_stream_metadata_class(linker)
270}
271
272fn conformance_stream_metadata_citizen(cx: &mut Cx) -> Result<()> {
273 let metadata = StreamMetadata::new(
274 Symbol::qualified("stream-citizen", "metadata"),
275 StreamMedia::Data,
276 StreamDirection::Source,
277 Symbol::qualified("stream-clock", "logical"),
278 BufferPolicy::bounded(8)?,
279 );
280 let value = cx
281 .factory()
282 .opaque(Arc::new(StreamMetadataValue::new(metadata)))?;
283 sim_citizen::check_value_fixture(cx, value)
284}
285
286sim_citizen::inventory::submit! {
287 sim_citizen::CitizenInfo {
288 symbol: "stream/Metadata",
289 version: 0,
290 crate_name: env!("CARGO_PKG_NAME"),
291 arity: 5,
292 install: install_stream_metadata_citizen,
293 conformance: conformance_stream_metadata_citizen,
294 }
295}
296
297fn class_value_or_stub(cx: &mut Cx) -> Result<Value> {
298 if let Some(value) = cx
299 .registry()
300 .class_by_symbol(&stream_metadata_class_symbol())
301 {
302 return Ok(value.clone());
303 }
304 cx.factory()
305 .class_stub(STREAM_METADATA_CLASS_ID, stream_metadata_class_symbol())
306}