1use std::fmt;
4
5use sim_kernel::{Expr, Symbol};
6use sim_lib_stream_core::{DataPacket, StreamPacket};
7use sim_value::{access, build};
8
9pub type DeviceSampleResult<T> = std::result::Result<T, DeviceSampleError>;
11
12#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct DeviceSampleError {
19 message: String,
20}
21
22impl DeviceSampleError {
23 pub fn new(message: impl Into<String>) -> Self {
25 Self {
26 message: message.into(),
27 }
28 }
29
30 pub fn message(&self) -> &str {
32 &self.message
33 }
34}
35
36impl fmt::Display for DeviceSampleError {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 f.write_str(&self.message)
39 }
40}
41
42impl std::error::Error for DeviceSampleError {}
43
44impl From<DeviceSampleError> for sim_kernel::Error {
45 fn from(error: DeviceSampleError) -> Self {
46 sim_kernel::Error::Eval(format!("device sample error: {error}"))
47 }
48}
49
50pub trait DeviceSample: Sized + Clone {
57 fn sample_kind() -> &'static str;
59
60 fn seq(&self) -> u64;
62
63 fn to_expr(&self) -> Expr;
65
66 fn from_expr(expr: &Expr) -> DeviceSampleResult<Self>;
68}
69
70pub fn roundtrip_ok<S>(sample: &S) -> bool
72where
73 S: DeviceSample + PartialEq,
74{
75 S::from_expr(&sample.to_expr())
76 .map(|decoded| &decoded == sample)
77 .unwrap_or(false)
78}
79
80pub fn sample_packet<S: DeviceSample>(sample: &S) -> StreamPacket {
82 StreamPacket::Data(DataPacket::new(
83 sample_kind_symbol(S::sample_kind()),
84 sample.to_expr(),
85 ))
86}
87
88pub fn device_sample_record_symbol() -> Symbol {
90 Symbol::qualified("stream", "device-sample")
91}
92
93pub fn sample_kind_symbol(kind: &str) -> Symbol {
95 Symbol::qualified("stream/device-sample", kind)
96}
97
98pub fn device_caps_sample_kind_symbol() -> Symbol {
100 sample_kind_symbol(DeviceCaps::sample_kind())
101}
102
103#[derive(Clone, Debug, PartialEq, Eq)]
108pub struct DeviceCaps {
109 seq: u64,
110 device: Symbol,
111 streams: Vec<Symbol>,
112 inputs: Vec<Symbol>,
113 outputs: Vec<Symbol>,
114}
115
116impl DeviceCaps {
117 pub fn new(
119 seq: u64,
120 device: Symbol,
121 streams: Vec<Symbol>,
122 inputs: Vec<Symbol>,
123 outputs: Vec<Symbol>,
124 ) -> Self {
125 Self {
126 seq,
127 device,
128 streams,
129 inputs,
130 outputs,
131 }
132 }
133
134 pub fn demo(seq: u64) -> Self {
136 Self::new(
137 seq,
138 Symbol::qualified("device", "modeled-edge"),
139 vec![
140 Symbol::qualified("device/stream", "battery"),
141 Symbol::qualified("device/stream", "motion"),
142 ],
143 vec![Symbol::qualified("device/input", "button")],
144 vec![
145 Symbol::qualified("device/output", "screen"),
146 Symbol::qualified("device/output", "haptic"),
147 ],
148 )
149 }
150
151 pub fn device(&self) -> &Symbol {
153 &self.device
154 }
155
156 pub fn streams(&self) -> &[Symbol] {
158 &self.streams
159 }
160
161 pub fn inputs(&self) -> &[Symbol] {
163 &self.inputs
164 }
165
166 pub fn outputs(&self) -> &[Symbol] {
168 &self.outputs
169 }
170
171 pub fn to_stream_packet(&self) -> StreamPacket {
173 sample_packet(self)
174 }
175}
176
177impl DeviceSample for DeviceCaps {
178 fn sample_kind() -> &'static str {
179 "device-caps"
180 }
181
182 fn seq(&self) -> u64 {
183 self.seq
184 }
185
186 fn to_expr(&self) -> Expr {
187 build::map(vec![
188 ("kind", Expr::Symbol(device_sample_record_symbol())),
189 ("sample", Expr::Symbol(device_caps_sample_kind_symbol())),
190 ("seq", build::uint(self.seq)),
191 ("device", Expr::Symbol(self.device.clone())),
192 ("streams", symbols_expr(&self.streams)),
193 ("inputs", symbols_expr(&self.inputs)),
194 ("outputs", symbols_expr(&self.outputs)),
195 ])
196 }
197
198 fn from_expr(expr: &Expr) -> DeviceSampleResult<Self> {
199 let entries = entries(expr)?;
200 expect_record_tag(entries)?;
201 expect_sample_kind(entries, &device_caps_sample_kind_symbol())?;
202 Ok(Self::new(
203 seq_field(entries)?,
204 symbol_field(entries, "device")?.clone(),
205 symbol_list_field(entries, "streams")?,
206 symbol_list_field(entries, "inputs")?,
207 symbol_list_field(entries, "outputs")?,
208 ))
209 }
210}
211
212pub(crate) fn decode_known_sample(expr: &Expr) -> DeviceSampleResult<()> {
213 let entries = entries(expr)?;
214 expect_record_tag(entries)?;
215 let kind = symbol_field(entries, "sample")?;
216 if kind == &device_caps_sample_kind_symbol() {
217 DeviceCaps::from_expr(expr)?;
218 return Ok(());
219 }
220 Err(DeviceSampleError::new(format!(
221 "unknown device sample kind {kind}"
222 )))
223}
224
225pub(crate) fn sample_constructor_args(expr: &Expr) -> DeviceSampleResult<Vec<Expr>> {
226 decode_known_sample(expr)?;
227 Ok(vec![expr.clone()])
228}
229
230fn symbols_expr(symbols: &[Symbol]) -> Expr {
231 build::list(symbols.iter().cloned().map(Expr::Symbol).collect())
232}
233
234fn entries(expr: &Expr) -> DeviceSampleResult<&[(Expr, Expr)]> {
235 access::map_entries(expr, "device sample map").map_err(kernel_error)
236}
237
238fn expect_record_tag(entries: &[(Expr, Expr)]) -> DeviceSampleResult<()> {
239 let actual = symbol_field(entries, "kind")?;
240 let expected = device_sample_record_symbol();
241 if actual == &expected {
242 Ok(())
243 } else {
244 Err(DeviceSampleError::new(format!(
245 "device sample kind tag must be {expected}, found {actual}"
246 )))
247 }
248}
249
250fn expect_sample_kind(entries: &[(Expr, Expr)], expected: &Symbol) -> DeviceSampleResult<()> {
251 let actual = symbol_field(entries, "sample")?;
252 if actual == expected {
253 Ok(())
254 } else {
255 Err(DeviceSampleError::new(format!(
256 "device sample record must be {expected}, found {actual}"
257 )))
258 }
259}
260
261fn seq_field(entries: &[(Expr, Expr)]) -> DeviceSampleResult<u64> {
262 let value = field(entries, "seq")?;
263 let Expr::Number(number) = value else {
264 return Err(DeviceSampleError::new(format!(
265 "device sample seq must be a u64 number, found {}",
266 sim_value::kind::expr_kind(value)
267 )));
268 };
269 if !matches!(number.domain.name.as_ref(), "i64" | "u64") {
270 return Err(DeviceSampleError::new(format!(
271 "device sample seq must use an integer domain, found {}",
272 number.domain
273 )));
274 }
275 number
276 .canonical
277 .parse::<u64>()
278 .map_err(|err| DeviceSampleError::new(format!("invalid device sample seq: {err}")))
279}
280
281fn symbol_field<'a>(entries: &'a [(Expr, Expr)], name: &str) -> DeviceSampleResult<&'a Symbol> {
282 access::entry_required_sym(entries, name, "device sample").map_err(kernel_error)
283}
284
285fn field<'a>(entries: &'a [(Expr, Expr)], name: &str) -> DeviceSampleResult<&'a Expr> {
286 access::entry_required(entries, name, "device sample").map_err(kernel_error)
287}
288
289fn symbol_list_field(entries: &[(Expr, Expr)], name: &str) -> DeviceSampleResult<Vec<Symbol>> {
290 let items =
291 access::entry_required_list(entries, name, "device sample").map_err(kernel_error)?;
292 items
293 .iter()
294 .map(|item| match item {
295 Expr::Symbol(symbol) => Ok(symbol.clone()),
296 other => Err(DeviceSampleError::new(format!(
297 "device sample {name} entries must be symbols, found {}",
298 sim_value::kind::expr_kind(other)
299 ))),
300 })
301 .collect()
302}
303
304fn kernel_error(error: sim_kernel::Error) -> DeviceSampleError {
305 DeviceSampleError::new(error.to_string())
306}