1use sim_kernel::{CapabilityName, Error, Expr, Result, Symbol};
2use sim_lib_server::{
3 FrameEnvelope, FrameKind, ServerFrame, stream_chunk_frame_from_expr, stream_frame_to_expr,
4};
5use sim_lib_stream_core::{
6 DataPacket, StreamEnvelope, StreamFaultKind, StreamFaultSpec, StreamMetadata, StreamPacket,
7 stream_cancel_capability, stream_open_capability, stream_push_capability,
8 stream_read_capability, stream_remote_network_capability, stream_stats_capability,
9};
10use sim_value::kind::expr_kind;
11
12#[derive(Clone, Debug, PartialEq, Eq)]
21pub enum StreamControl {
22 Open {
24 stream_id: Symbol,
26 metadata: StreamMetadata,
28 },
29 Next {
31 stream_id: Symbol,
33 limit: Option<u64>,
35 },
36 Push {
38 stream_id: Symbol,
40 envelope: Box<StreamEnvelope>,
42 },
43 Close {
45 stream_id: Symbol,
47 },
48 Cancel {
50 stream_id: Symbol,
52 },
53 Stats {
55 stream_id: Symbol,
57 },
58 Metadata {
60 stream_id: Symbol,
62 },
63 Fault {
65 stream_id: Symbol,
67 fault: StreamFaultSpec,
69 },
70}
71
72impl StreamControl {
73 pub fn operation(&self) -> Symbol {
75 match self {
76 Self::Open { .. } => stream_control_open_symbol(),
77 Self::Next { .. } => stream_control_next_symbol(),
78 Self::Push { .. } => stream_control_push_symbol(),
79 Self::Close { .. } => stream_control_close_symbol(),
80 Self::Cancel { .. } => stream_control_cancel_symbol(),
81 Self::Stats { .. } => stream_control_stats_symbol(),
82 Self::Metadata { .. } => stream_control_metadata_symbol(),
83 Self::Fault { .. } => stream_control_fault_symbol(),
84 }
85 }
86
87 pub fn required_capability(&self) -> CapabilityName {
89 match self {
90 Self::Open { .. } => stream_open_capability(),
91 Self::Next { .. } => stream_read_capability(),
92 Self::Push { .. } => stream_push_capability(),
93 Self::Close { .. } | Self::Cancel { .. } => stream_cancel_capability(),
94 Self::Stats { .. } | Self::Metadata { .. } | Self::Fault { .. } => {
95 stream_stats_capability()
96 }
97 }
98 }
99
100 pub fn stream_id(&self) -> &Symbol {
102 match self {
103 Self::Open { stream_id, .. }
104 | Self::Next { stream_id, .. }
105 | Self::Push { stream_id, .. }
106 | Self::Close { stream_id }
107 | Self::Cancel { stream_id }
108 | Self::Stats { stream_id }
109 | Self::Metadata { stream_id }
110 | Self::Fault { stream_id, .. } => stream_id,
111 }
112 }
113
114 pub fn to_expr(&self) -> Expr {
119 StreamPacket::data(self.operation(), self.payload_expr()).to_expr()
120 }
121
122 fn payload_expr(&self) -> Expr {
123 let mut entries = vec![
124 key_expr("control", Expr::Symbol(stream_control_tag_symbol())),
125 key_expr("stream-id", Expr::Symbol(self.stream_id().clone())),
126 ];
127 match self {
128 Self::Open { metadata, .. } => {
129 entries.push(key_expr("metadata", metadata.table_expr()));
130 }
131 Self::Next { limit, .. } => {
132 entries.push(key_expr(
133 "limit",
134 limit
135 .map(|limit| Expr::String(limit.to_string()))
136 .unwrap_or(Expr::Nil),
137 ));
138 }
139 Self::Push { envelope, .. } => {
140 entries.push(key_expr("envelope", envelope.to_expr()));
141 }
142 Self::Close { .. }
143 | Self::Cancel { .. }
144 | Self::Stats { .. }
145 | Self::Metadata { .. } => {}
146 Self::Fault { fault, .. } => {
147 entries.push(key_expr("fault", Expr::Symbol(fault.kind.symbol())));
148 entries.push(key_expr("count", Expr::String(fault.count.to_string())));
149 }
150 }
151 Expr::Map(entries)
152 }
153}
154
155impl TryFrom<Expr> for StreamControl {
156 type Error = Error;
157
158 fn try_from(expr: Expr) -> Result<Self> {
159 let StreamPacket::Data(DataPacket { kind, payload }) = StreamPacket::try_from(expr)? else {
160 return Err(Error::TypeMismatch {
161 expected: "stream fabric control data packet",
162 found: "stream packet",
163 });
164 };
165 let entries = map_entries(&payload)?;
166 let tag = symbol_field(entries, "control")?;
167 if *tag != stream_control_tag_symbol() {
168 return Err(Error::Eval(format!(
169 "unknown stream fabric control tag {}",
170 tag.as_qualified_str()
171 )));
172 }
173 let stream_id = symbol_field(entries, "stream-id")?.clone();
174 match kind.as_qualified_str().as_str() {
175 "stream/fabric/open" => {
176 ensure_fields(entries, &["control", "stream-id", "metadata"])?;
177 Ok(Self::Open {
178 stream_id,
179 metadata: StreamMetadata::from_table_expr(field(entries, "metadata")?)?,
180 })
181 }
182 "stream/fabric/next" => {
183 ensure_fields(entries, &["control", "stream-id", "limit"])?;
184 Ok(Self::Next {
185 stream_id,
186 limit: optional_u64(field(entries, "limit")?)?,
187 })
188 }
189 "stream/fabric/push" => {
190 ensure_fields(entries, &["control", "stream-id", "envelope"])?;
191 Ok(Self::Push {
192 stream_id,
193 envelope: Box::new(StreamEnvelope::try_from(
194 field(entries, "envelope")?.clone(),
195 )?),
196 })
197 }
198 "stream/fabric/close" => {
199 ensure_fields(entries, &["control", "stream-id"])?;
200 Ok(Self::Close { stream_id })
201 }
202 "stream/fabric/cancel" => {
203 ensure_fields(entries, &["control", "stream-id"])?;
204 Ok(Self::Cancel { stream_id })
205 }
206 "stream/fabric/stats" => {
207 ensure_fields(entries, &["control", "stream-id"])?;
208 Ok(Self::Stats { stream_id })
209 }
210 "stream/fabric/metadata" => {
211 ensure_fields(entries, &["control", "stream-id"])?;
212 Ok(Self::Metadata { stream_id })
213 }
214 "stream/fabric/fault" => {
215 ensure_fields(entries, &["control", "stream-id", "fault", "count"])?;
216 Ok(Self::Fault {
217 stream_id,
218 fault: StreamFaultSpec::new(
219 StreamFaultKind::from_symbol(symbol_field(entries, "fault")?)?,
220 parse_u64(field(entries, "count")?)? as usize,
221 ),
222 })
223 }
224 other => Err(Error::Eval(format!(
225 "unknown stream fabric control operation {other}"
226 ))),
227 }
228 }
229}
230
231pub fn stream_control_frame_from_control(
237 cx: &mut sim_kernel::Cx,
238 codec: Symbol,
239 control: &StreamControl,
240 envelope: FrameEnvelope,
241) -> Result<ServerFrame> {
242 cx.require(&stream_remote_network_capability())?;
243 cx.require(&control.required_capability())?;
244 stream_chunk_frame_from_expr(cx, codec, &control.to_expr(), envelope)
245}
246
247pub fn stream_control_from_frame(
252 cx: &mut sim_kernel::Cx,
253 frame: &ServerFrame,
254) -> Result<StreamControl> {
255 if frame.kind != FrameKind::StreamChunk {
256 return Err(Error::Eval(format!(
257 "stream fabric control expected stream chunk frame, got {}",
258 frame.kind.as_symbol()
259 )));
260 }
261 let expr = stream_frame_to_expr(cx, frame)?.ok_or_else(|| {
262 Error::Eval("stream fabric control frame did not decode to a payload".to_owned())
263 })?;
264 StreamControl::try_from(expr)
265}
266
267pub fn stream_control_operation_symbols() -> [Symbol; 8] {
269 [
270 stream_control_open_symbol(),
271 stream_control_next_symbol(),
272 stream_control_push_symbol(),
273 stream_control_close_symbol(),
274 stream_control_cancel_symbol(),
275 stream_control_stats_symbol(),
276 stream_control_metadata_symbol(),
277 stream_control_fault_symbol(),
278 ]
279}
280
281pub fn stream_control_required_capability(control: &StreamControl) -> CapabilityName {
285 control.required_capability()
286}
287
288pub fn stream_control_open_symbol() -> Symbol {
290 Symbol::qualified("stream/fabric", "open")
291}
292
293pub fn stream_control_next_symbol() -> Symbol {
295 Symbol::qualified("stream/fabric", "next")
296}
297
298pub fn stream_control_push_symbol() -> Symbol {
300 Symbol::qualified("stream/fabric", "push")
301}
302
303pub fn stream_control_close_symbol() -> Symbol {
305 Symbol::qualified("stream/fabric", "close")
306}
307
308pub fn stream_control_cancel_symbol() -> Symbol {
310 Symbol::qualified("stream/fabric", "cancel")
311}
312
313pub fn stream_control_stats_symbol() -> Symbol {
315 Symbol::qualified("stream/fabric", "stats")
316}
317
318pub fn stream_control_metadata_symbol() -> Symbol {
320 Symbol::qualified("stream/fabric", "metadata")
321}
322
323pub fn stream_control_fault_symbol() -> Symbol {
325 Symbol::qualified("stream/fabric", "fault")
326}
327
328fn stream_control_tag_symbol() -> Symbol {
329 Symbol::qualified("stream/fabric-control", "v1")
330}
331
332fn key_expr(name: &str, value: Expr) -> (Expr, Expr) {
333 (Expr::Symbol(Symbol::new(name)), value)
334}
335
336fn map_entries(expr: &Expr) -> Result<&[(Expr, Expr)]> {
337 match expr {
338 Expr::Map(entries) => Ok(entries),
339 other => Err(Error::TypeMismatch {
340 expected: "stream fabric control map",
341 found: expr_kind(other),
342 }),
343 }
344}
345
346fn optional_u64(expr: &Expr) -> Result<Option<u64>> {
347 match expr {
348 Expr::Nil => Ok(None),
349 Expr::String(value) => value
350 .parse::<u64>()
351 .map(Some)
352 .map_err(|err| Error::Eval(format!("invalid stream fabric control limit: {err}"))),
353 other => Err(Error::TypeMismatch {
354 expected: "optional u64 string",
355 found: expr_kind(other),
356 }),
357 }
358}
359
360fn parse_u64(expr: &Expr) -> Result<u64> {
361 match expr {
362 Expr::String(value) => value
363 .parse::<u64>()
364 .map_err(|err| Error::Eval(format!("invalid stream fabric control count: {err}"))),
365 other => Err(Error::TypeMismatch {
366 expected: "u64 string",
367 found: expr_kind(other),
368 }),
369 }
370}
371
372fn symbol_field<'a>(entries: &'a [(Expr, Expr)], name: &str) -> Result<&'a Symbol> {
373 sim_value::access::entry_required_sym(entries, name, "symbol field")
374}
375
376fn field<'a>(entries: &'a [(Expr, Expr)], name: &str) -> Result<&'a Expr> {
377 entries
378 .iter()
379 .find_map(|(key, value)| match key {
380 Expr::Symbol(symbol) if symbol.namespace.is_none() && symbol.name.as_ref() == name => {
381 Some(value)
382 }
383 _ => None,
384 })
385 .ok_or_else(|| Error::Eval(format!("stream fabric control missing {name} field")))
386}
387
388fn ensure_fields(entries: &[(Expr, Expr)], allowed: &[&str]) -> Result<()> {
389 for (key, _) in entries {
390 let Expr::Symbol(symbol) = key else {
391 return Err(Error::TypeMismatch {
392 expected: "symbol stream fabric control field",
393 found: expr_kind(key),
394 });
395 };
396 if symbol.namespace.is_none() && allowed.contains(&symbol.name.as_ref()) {
397 continue;
398 }
399 return Err(Error::Eval(format!(
400 "unknown stream fabric control field {}",
401 symbol.as_qualified_str()
402 )));
403 }
404 Ok(())
405}