Skip to main content

sim_lib_stream_combinators/
cookbook.rs

1//! Deterministic cookbook builders for stream-combinator recipes.
2
3use sim_kernel::{Expr, NumberLiteral, Symbol};
4
5use crate::stream_window_data_kind;
6
7/// Build the modeled pipeline-stage descriptor used by the cookbook recipe.
8pub fn pipeline_stages_demo() -> Expr {
9    Expr::Map(vec![
10        (field("kind"), sym("stream-combinators", "pipeline")),
11        (
12            field("stages"),
13            Expr::Vector(vec![
14                sym("stream/stage", "map"),
15                sym("stream/stage", "filter-data-kind"),
16                Expr::Symbol(stream_window_data_kind()),
17                sym("stream/stage", "record"),
18                sym("stream/stage", "replay"),
19            ]),
20        ),
21        (field("window-size"), number(4)),
22        (field("modeled-source"), sym("stream/source", "memory-data")),
23    ])
24}
25
26fn field(name: &str) -> Expr {
27    Expr::Symbol(Symbol::qualified("stream-combinators", name))
28}
29
30fn sym(namespace: &str, name: &str) -> Expr {
31    Expr::Symbol(Symbol::qualified(namespace, name))
32}
33
34fn number(value: impl ToString) -> Expr {
35    Expr::Number(NumberLiteral {
36        domain: Symbol::qualified("numbers", "i64"),
37        canonical: value.to_string(),
38    })
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    #[test]
46    fn pipeline_demo_includes_window_data_kind() {
47        let Expr::Map(entries) = pipeline_stages_demo() else {
48            panic!("pipeline stages demo is a map")
49        };
50        assert!(entries.iter().any(|(_, value)| {
51            matches!(value, Expr::Vector(stages) if stages.iter().any(|stage| matches!(stage, Expr::Symbol(symbol) if *symbol == stream_window_data_kind())))
52        }));
53    }
54}