Skip to main content

extend_store_nested/
extend_store_nested.rs

1//! Complex nested structures — the kind an Operation would produce.
2//!
3//! Operations expose one or more Stores as output. This example shows patterns
4//! you'd use when writing an Operation that returns structured results: query
5//! rows as an array of maps, grouped data as a map of arrays, and deeply nested
6//! configs. All construction uses closure-based APIs and chaining with `?`.
7
8use panopticon_core::extend::{Store, Value};
9
10fn write_query_results(store: &mut Store) -> Result<(), Box<dyn std::error::Error>> {
11    store.with_array("rows", |rows| {
12        // Row 1 — all fields inserted via chaining, no .into() needed.
13        let mut row = rows.push_map()?;
14        row.insert("user", "alice@contoso.com")?
15            .insert("ip", "10.0.0.1")?
16            .insert("status", "Success")?
17            .insert("attempts", 1i64)?;
18
19        // Row 2
20        let mut row = rows.push_map()?;
21        row.insert("user", "bob@contoso.com")?
22            .insert("ip", "192.168.1.50")?
23            .insert("status", "Failure")?
24            .insert("attempts", 3i64)?;
25
26        Ok(())
27    })?;
28
29    Ok(())
30}
31
32fn write_grouped_data(store: &mut Store) -> Result<(), Box<dyn std::error::Error>> {
33    store.with_map("by_severity", |groups| {
34        groups.with_array("high", |arr| {
35            arr.push("INC-001")?.push("INC-004")?;
36            Ok(())
37        })?;
38
39        groups.with_array("medium", |arr| {
40            arr.push("INC-002")?;
41            Ok(())
42        })?;
43
44        groups.with_array("low", |arr| {
45            arr.push("INC-003")?.push("INC-005")?.push("INC-006")?;
46            Ok(())
47        })?;
48
49        Ok(())
50    })?;
51
52    Ok(())
53}
54
55fn write_nested_config(store: &mut Store) -> Result<(), Box<dyn std::error::Error>> {
56    store.with_map("pipeline_config", |cfg| {
57        cfg.insert("name", "signin_analysis")?;
58
59        cfg.with_map("source", |src| {
60            src.insert("type", "kql")?
61                .insert("workspace", "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")?;
62
63            src.with_map("options", |opts| {
64                opts.insert("timeout_secs", 30i64)?
65                    .insert("max_rows", 10_000i64)?;
66                Ok(())
67            })?;
68
69            Ok(())
70        })?;
71
72        cfg.with_array("outputs", |outputs| {
73            let mut o = outputs.push_map()?;
74            o.insert("format", "json")?
75                .insert("path", "/tmp/results.json")?;
76
77            let mut o = outputs.push_map()?;
78            o.insert("format", "csv")?
79                .insert("path", "/tmp/results.csv")?;
80
81            Ok(())
82        })?;
83
84        Ok(())
85    })?;
86
87    Ok(())
88}
89
90fn main() -> Result<(), Box<dyn std::error::Error>> {
91    let mut store = Store::new();
92
93    // An Operation would call these to populate its output Store.
94    write_query_results(&mut store)?;
95    write_grouped_data(&mut store)?;
96    write_nested_config(&mut store)?;
97
98    // --- Reading results back via chained accessors ---
99
100    // Query rows: array -> index -> key -> value
101    let bob_status = store
102        .get("rows")?
103        .get_index(1)?
104        .get_key("status")?
105        .get_value()?;
106    println!("rows[1].status = {bob_status:?}");
107    assert_eq!(bob_status, &Value::Text("Failure".into()));
108
109    let alice_attempts = store
110        .get("rows")?
111        .get_index(0)?
112        .get_key("attempts")?
113        .get_value()?;
114    println!("rows[0].attempts = {alice_attempts:?}");
115    assert_eq!(alice_attempts, &Value::Integer(1));
116
117    // Grouped data: map -> key -> index -> value
118    let high_count = store.get("by_severity")?.get_key("high")?.as_array()?.len();
119    println!("by_severity.high: {high_count} incidents");
120    assert_eq!(high_count, 2);
121
122    let first_low = store
123        .get("by_severity")?
124        .get_key("low")?
125        .get_index(0)?
126        .get_value()?;
127    println!("by_severity.low[0] = {first_low:?}");
128
129    // Nested config: map -> key -> key -> key -> value
130    let timeout = store
131        .get("pipeline_config")?
132        .get_key("source")?
133        .get_key("options")?
134        .get_key("timeout_secs")?
135        .get_value()?;
136    println!("pipeline_config.source.options.timeout_secs = {timeout:?}");
137    assert_eq!(timeout, &Value::Integer(30));
138
139    // Config outputs array: map -> key -> index -> key -> value
140    let second_format = store
141        .get("pipeline_config")?
142        .get_key("outputs")?
143        .get_index(1)?
144        .get_key("format")?
145        .get_value()?;
146    println!("pipeline_config.outputs[1].format = {second_format:?}");
147    assert_eq!(second_format, &Value::Text("csv".into()));
148
149    println!("store_nested: ok");
150    Ok(())
151}