pub enum Param {
Literal(Value),
Ref(String),
Template(Vec<Param>),
Array(Vec<Param>),
Map(HashMap<String, Param>),
}Expand description
A parameter value supplied to an operation input at draft time.
Operations declare their inputs in OperationMetadata; callers bind
each declared input to a Param through the params! macro when
adding a step with Pipeline::step. A Param is either a literal
(resolved immediately from the embedded Value) or a reference that
looks up state from the runtime Store when the step runs.
Most code constructs Params through the params! macro rather than
the variants directly — the macro picks the right variant from the Rust
literal or reference syntax it is given.
use panopticon_core::prelude::*;
let mut pipe = Pipeline::default();
pipe.var("target", "world")?;
pipe.step::<SetVar>(
"greet",
params!(
"name" => "greeting",
"value" => Param::template(vec![
Param::literal("hello, "),
Param::reference("target"),
]),
),
)?;Variants§
Literal(Value)
A scalar value supplied directly, with no store lookup.
Ref(String)
A reference to a store entry by dotted path (e.g. "var_name" or
"step_name.output_name"). Resolved against the runtime store
when the step executes.
Template(Vec<Param>)
A string template whose parts (literals or references) are resolved
and concatenated into a single Text value at runtime. Every part
must resolve to a scalar — nested arrays or maps produce
OperationError::InvalidTemplatePart.
Array(Vec<Param>)
An array of parameters. Each element is resolved independently and
the result is wrapped in a StoreEntry::Array.
Map(HashMap<String, Param>)
A map of parameters keyed by name. Each value is resolved
independently and the result is wrapped in a StoreEntry::Map.
Implementations§
Source§impl Param
impl Param
Sourcepub fn literal(value: impl Into<Value>) -> Self
pub fn literal(value: impl Into<Value>) -> Self
Constructs a Param::Literal from anything convertible into a
Value.
Examples found in repository?
11fn main() -> Result<(), Box<dyn std::error::Error>> {
12 // ── Basic guard: Compare produces true, body executes ──
13 println!("=== Guard (true) ===");
14 {
15 let mut pipe = Pipeline::default();
16 pipe.var("status_code", 200i64)?;
17
18 pipe.step::<Compare>(
19 "is_ok",
20 params!(
21 "left" => Param::reference("status_code"),
22 "op" => "eq",
23 "right" => 200i64,
24 "name" => "should_run",
25 ),
26 )?;
27
28 pipe.guard("check", GuardSource::boolean("is_ok.should_run"), |body| {
29 body.step::<SetVar>(
30 "produce",
31 params!("name" => "result", "value" => "guard body executed"),
32 )?;
33 Ok(())
34 })?;
35
36 pipe.hook(Logger::new().writer(std::io::stdout()));
37 pipe.hook(Timeout::new(Duration::from_secs(5)));
38
39 let complete = pipe.compile()?.run().wait()?;
40 complete.debug();
41 }
42
43 // ── Guard skipped: Compare produces false ──
44 println!("\n=== Guard (false) ===");
45 {
46 let mut pipe = Pipeline::default();
47 pipe.var("status_code", 404i64)?;
48
49 pipe.step::<Compare>(
50 "is_ok",
51 params!(
52 "left" => Param::reference("status_code"),
53 "op" => "eq",
54 "right" => 200i64,
55 "name" => "should_run",
56 ),
57 )?;
58
59 pipe.guard("check", GuardSource::boolean("is_ok.should_run"), |body| {
60 body.step::<SetVar>(
61 "produce",
62 params!("name" => "result", "value" => "should not appear"),
63 )?;
64 Ok(())
65 })?;
66
67 pipe.hook(Logger::new().writer(std::io::stdout()));
68
69 let complete = pipe.compile()?.run().wait()?;
70 complete.debug();
71 }
72
73 // ── Guard output used by downstream step ──
74 println!("\n=== Guard output used downstream ===");
75 {
76 let mut pipe = Pipeline::default();
77 pipe.var("count", 5i64)?;
78
79 pipe.step::<Compare>(
80 "has_results",
81 params!(
82 "left" => Param::reference("count"),
83 "op" => "gt",
84 "right" => 0i64,
85 "name" => "flag",
86 ),
87 )?;
88
89 pipe.guard("gate", GuardSource::boolean("has_results.flag"), |body| {
90 body.step::<SetVar>(
91 "create",
92 params!("name" => "message", "value" => "from inside guard"),
93 )?;
94 Ok(())
95 })?;
96
97 pipe.step::<SetVar>(
98 "consume",
99 params!(
100 "name" => "final_output",
101 "value" => Param::template(vec![
102 Param::literal("received: "),
103 Param::reference("message"),
104 ]),
105 ),
106 )?;
107
108 pipe.hook(Logger::new().writer(std::io::stdout()));
109
110 let complete = pipe.compile()?.run().wait()?;
111 complete.debug();
112 }
113
114 // ── Error: unresolved guard reference ──
115 println!("\n=== Guard unresolved reference ===");
116 {
117 let mut pipe = Pipeline::default();
118
119 pipe.guard("check", GuardSource::boolean("nonexistent"), |_body| Ok(()))?;
120
121 match pipe.compile() {
122 Err(e) => println!(" Caught: {}", e),
123 Ok(_) => println!(" ERROR: should have failed!"),
124 }
125 }
126
127 Ok(())
128}More examples
12fn main() -> Result<(), Box<dyn std::error::Error>> {
13 // ── Basic iter_array: iterate over items and capture each ──
14 println!("=== Basic iter_array ===");
15 {
16 let mut pipe = Pipeline::default();
17 pipe.array("numbers")?.push(10)?.push(20)?.push(30)?;
18
19 pipe.iter_array(
20 "process",
21 IterSource::array("numbers"),
22 |_index, item, body| {
23 body.step::<SetVar>(
24 "capture",
25 params!(
26 "name" => "doubled",
27 "value" => Param::reference(item),
28 ),
29 )?;
30 Ok(())
31 },
32 )?;
33
34 pipe.hook(Logger::new().writer(std::io::stdout()));
35 pipe.hook(Timeout::new(Duration::from_secs(5)));
36
37 let complete = pipe.compile()?.run().wait()?;
38 complete.debug();
39 }
40
41 // ── iter_array with parent variable references ──
42 println!("\n=== iter_array referencing parent vars ===");
43 {
44 let mut pipe = Pipeline::default();
45 pipe.var("prefix", "item_")?;
46 pipe.array("items")?
47 .push("alpha")?
48 .push("beta")?
49 .push("gamma")?;
50
51 pipe.iter_array("loop", IterSource::array("items"), |_index, item, body| {
52 body.step::<SetVar>(
53 "combine",
54 params!(
55 "name" => "label",
56 "value" => Param::template(vec![
57 Param::reference("prefix"),
58 Param::reference(item),
59 ]),
60 ),
61 )?;
62 Ok(())
63 })?;
64
65 let complete = pipe.compile()?.run().wait()?;
66 complete.debug();
67 }
68
69 // ── iter_array with multiple body steps ──
70 println!("\n=== iter_array with multi-step body ===");
71 {
72 let mut pipe = Pipeline::default();
73 pipe.var("suffix", "_processed")?;
74 pipe.array("words")?.push("hello")?.push("world")?;
75
76 pipe.iter_array("each", IterSource::array("words"), |_index, item, body| {
77 body.step::<SetVar>(
78 "tag",
79 params!(
80 "name" => "tagged",
81 "value" => Param::template(vec![
82 Param::reference(item),
83 Param::reference("suffix"),
84 ]),
85 ),
86 )?;
87 body.step::<SetVar>(
88 "wrap",
89 params!(
90 "name" => "wrapped",
91 "value" => Param::template(vec![
92 Param::literal("["),
93 Param::reference("tagged"),
94 Param::literal("]"),
95 ]),
96 ),
97 )?;
98 Ok(())
99 })?;
100
101 let complete = pipe.compile()?.run().wait()?;
102 complete.debug();
103 }
104
105 // ── Error: iter_array with unresolved source ──
106 println!("\n=== iter_array unresolved source ===");
107 {
108 let mut pipe = Pipeline::default();
109
110 pipe.iter_array("loop", IterSource::array("nonexistent"), |_, _, _| Ok(()))?;
111
112 match pipe.compile() {
113 Err(e) => println!(" Caught: {}", e),
114 Ok(_) => println!(" ERROR: should have failed!"),
115 }
116 }
117
118 // ── Error: iter_array body references unknown variable ──
119 println!("\n=== iter_array unresolved body reference ===");
120 {
121 let mut pipe = Pipeline::default();
122 pipe.array("items")?.push(1)?;
123
124 pipe.iter_array("loop", IterSource::array("items"), |_, _, body| {
125 body.step::<SetVar>(
126 "bad",
127 params!(
128 "name" => "out",
129 "value" => Param::reference("ghost"),
130 ),
131 )?;
132 Ok(())
133 })?;
134
135 match pipe.compile() {
136 Err(e) => println!(" Caught: {}", e),
137 Ok(_) => println!(" ERROR: should have failed!"),
138 }
139 }
140
141 Ok(())
142}11fn main() -> Result<(), Box<dyn std::error::Error>> {
12 // ── Basic iter_map: iterate over key-value pairs ──
13 println!("=== Basic iter_map ===");
14 {
15 let mut pipe = Pipeline::default();
16 pipe.map("config")?
17 .insert("host", "localhost")?
18 .insert("port", "8080")?
19 .insert("protocol", "https")?;
20
21 pipe.iter_map(
22 "read_config",
23 IterSource::map("config"),
24 |key, value, body| {
25 body.step::<SetVar>(
26 "capture_key",
27 params!(
28 "name" => "current_key",
29 "value" => Param::reference(key),
30 ),
31 )?;
32 body.step::<SetVar>(
33 "capture_value",
34 params!(
35 "name" => "current_value",
36 "value" => Param::reference(value),
37 ),
38 )?;
39 Ok(())
40 },
41 )?;
42
43 pipe.hook(Logger::new().writer(std::io::stdout()));
44 pipe.hook(Timeout::new(Duration::from_secs(5)));
45
46 let complete = pipe.compile()?.run().wait()?;
47 complete.debug();
48 }
49
50 // ── iter_map with parent variable references ──
51 println!("\n=== iter_map referencing parent vars ===");
52 {
53 let mut pipe = Pipeline::default();
54 pipe.var("env", "production")?;
55 pipe.map("settings")?
56 .insert("db_host", "db.example.com")?
57 .insert("cache_host", "cache.example.com")?;
58
59 pipe.iter_map(
60 "apply_env",
61 IterSource::map("settings"),
62 |key, value, body| {
63 body.step::<SetVar>(
64 "label",
65 params!(
66 "name" => "entry",
67 "value" => Param::template(vec![
68 Param::reference("env"),
69 Param::literal("."),
70 Param::reference(key),
71 Param::literal("="),
72 Param::reference(value),
73 ]),
74 ),
75 )?;
76 Ok(())
77 },
78 )?;
79
80 let complete = pipe.compile()?.run().wait()?;
81 complete.debug();
82 }
83
84 // ── iter_map followed by regular steps ──
85 println!("\n=== iter_map then regular steps ===");
86 {
87 let mut pipe = Pipeline::default();
88 pipe.var("status", "ready")?;
89 pipe.map("headers")?
90 .insert("content-type", "application/json")?
91 .insert("accept", "text/html")?;
92
93 pipe.iter_map(
94 "scan_headers",
95 IterSource::map("headers"),
96 |_key, value, body| {
97 body.step::<SetVar>(
98 "save",
99 params!(
100 "name" => "last_header",
101 "value" => Param::reference(value),
102 ),
103 )?;
104 Ok(())
105 },
106 )?;
107
108 // Regular step after the iteration
109 pipe.step::<SetVar>(
110 "finalize",
111 params!(
112 "name" => "done",
113 "value" => Param::reference("status"),
114 ),
115 )?;
116
117 pipe.returns(
118 "result",
119 params!(
120 "status" => Param::reference("done"),
121 ),
122 )?;
123
124 let complete = pipe.compile()?.run().wait()?;
125 complete.debug();
126 }
127
128 // ── Error: iter_map with unresolved source ──
129 println!("\n=== iter_map unresolved source ===");
130 {
131 let mut pipe = Pipeline::default();
132
133 pipe.iter_map("loop", IterSource::map("missing"), |_, _, _| Ok(()))?;
134
135 match pipe.compile() {
136 Err(e) => println!(" Caught: {}", e),
137 Ok(_) => println!(" ERROR: should have failed!"),
138 }
139 }
140
141 // ── Error: iter_map body references unknown variable ──
142 println!("\n=== iter_map unresolved body reference ===");
143 {
144 let mut pipe = Pipeline::default();
145 pipe.map("data")?.insert("k", "v")?;
146
147 pipe.iter_map("loop", IterSource::map("data"), |_, _, body| {
148 body.step::<SetVar>(
149 "bad",
150 params!(
151 "name" => "out",
152 "value" => Param::reference("phantom"),
153 ),
154 )?;
155 Ok(())
156 })?;
157
158 match pipe.compile() {
159 Err(e) => println!(" Caught: {}", e),
160 Ok(_) => println!(" ERROR: should have failed!"),
161 }
162 }
163
164 Ok(())
165}13fn main() -> Result<(), Box<dyn std::error::Error>> {
14 // ── Happy path: derived outputs resolved at compile time ──
15 println!("=== Valid pipeline ===");
16 {
17 let mut pipe = Pipeline::default();
18
19 pipe.var("number", 1)?;
20 pipe.var("string", "example")?;
21 pipe.array("array")?.push(1)?.push(2)?.push(3)?;
22
23 // Derived output name from a variable ref: "string" → "example"
24 pipe.step::<SetVar>(
25 "set_var_1",
26 params!(
27 "name" => Param::reference("string"),
28 "value" => Param::reference("number"),
29 ),
30 )?;
31
32 // Derived output name from a literal: "greeting"
33 pipe.step::<SetVar>(
34 "set_var_2",
35 params!(
36 "name" => "greeting",
37 "value" => "hello world",
38 ),
39 )?;
40
41 // Chained: use derived output "greeting" from set_var_2 as a variable reference
42 pipe.step::<SetVar>(
43 "set_var_3",
44 params!(
45 "name" => "farewell",
46 "value" => Param::reference("greeting"),
47 ),
48 )?;
49
50 // Template referencing a derived output
51 pipe.step::<SetVar>(
52 "set_var_4",
53 params!(
54 "name" => "message",
55 "value" => Param::template(vec![
56 Param::reference("greeting"),
57 Param::literal(" and goodbye"),
58 ]),
59 ),
60 )?;
61
62 pipe.returns(
63 "summary",
64 params!(
65 "original_number" => Param::reference("number"),
66 "original_string" => Param::reference("string"),
67 "the_array" => Param::reference("array"),
68 "greeting" => Param::reference("greeting"),
69 "derived_from_ref" => Param::reference("example"),
70 "farewell" => Param::reference("farewell"),
71 "message" => Param::reference("message"),
72 ),
73 )?;
74
75 // Logger: writes each event to stdout
76 pipe.hook(Logger::new().writer(std::io::stdout()));
77
78 // Profiler: tracks per-step wall-clock duration
79 let profiler = Profiler::new().writer(std::io::stdout());
80 let timings = profiler.timings();
81 pipe.hook(profiler);
82
83 // EventLog: collects structured event records for post-mortem inspection
84 let event_log = EventLog::new();
85 let log = event_log.log();
86 pipe.hook(event_log);
87
88 // Timeout: aborts if pipeline exceeds 5 seconds
89 pipe.hook(Timeout::new(Duration::from_secs(5)));
90
91 // StoreValidator: assert "greeting" exists as Text after set_var_2
92 pipe.hook(
93 StoreValidator::new()
94 .after_step("set_var_2")
95 .expect("greeting", Type::Text),
96 );
97
98 let complete = pipe.compile()?.run().wait()?;
99 complete.debug();
100
101 // Read profiler timings programmatically
102 println!("\nProgrammatic timings:");
103 for (name, dur) in timings.lock().unwrap().iter() {
104 println!(" {} took {:.3}ms", name, dur.as_secs_f64() * 1000.0);
105 }
106
107 // Read event log
108 println!("\nEvent log ({} events):", log.lock().unwrap().len());
109 for record in log.lock().unwrap().iter() {
110 println!(
111 " [{:.3}ms] {:?}{}",
112 record.elapsed.as_secs_f64() * 1000.0,
113 record.kind,
114 record
115 .step_name
116 .as_deref()
117 .map(|s| format!(" ({})", s))
118 .unwrap_or_default(),
119 );
120 }
121 }
122
123 // ── Error: step references a variable that doesn't exist ──
124 println!("\n=== Unresolved step reference ===");
125 {
126 let mut pipe = Pipeline::default();
127 pipe.var("number", 1)?;
128
129 pipe.step::<SetVar>(
130 "bad_step",
131 params!(
132 "name" => "output",
133 "value" => Param::reference("does_not_exist"),
134 ),
135 )?;
136
137 match pipe.compile() {
138 Err(e) => println!(" Caught: {}", e),
139 Ok(_) => println!(" ERROR: should have failed!"),
140 }
141 }
142
143 // ── Error: return references something that was never produced ──
144 println!("\n=== Unresolved return reference ===");
145 {
146 let mut pipe = Pipeline::default();
147 pipe.var("number", 1)?;
148
149 pipe.step::<SetVar>(
150 "ok_step",
151 params!(
152 "name" => "output",
153 "value" => Param::reference("number"),
154 ),
155 )?;
156
157 pipe.returns(
158 "result",
159 params!(
160 "good" => Param::reference("output"),
161 "bad" => Param::reference("never_created"),
162 ),
163 )?;
164
165 match pipe.compile() {
166 Err(e) => println!(" Caught: {}", e),
167 Ok(_) => println!(" ERROR: should have failed!"),
168 }
169 }
170
171 // ── Error: step references a later step's output (ordering) ──
172 println!("\n=== Forward reference (wrong order) ===");
173 {
174 let mut pipe = Pipeline::default();
175 pipe.var("base", "start")?;
176
177 // This step tries to reference "later_output" which doesn't exist yet
178 pipe.step::<SetVar>(
179 "step_1",
180 params!(
181 "name" => "early",
182 "value" => Param::reference("later_output"),
183 ),
184 )?;
185
186 pipe.step::<SetVar>(
187 "step_2",
188 params!(
189 "name" => "later_output",
190 "value" => Param::reference("base"),
191 ),
192 )?;
193
194 match pipe.compile() {
195 Err(e) => println!(" Caught: {}", e),
196 Ok(_) => println!(" ERROR: should have failed!"),
197 }
198 }
199
200 // ── Error: duplicate variable name ──
201 println!("\n=== Duplicate variable ===");
202 {
203 let mut pipe = Pipeline::default();
204 pipe.var("x", 1)?;
205 match pipe.var("x", 2) {
206 Err(e) => println!(" Caught: {}", e),
207 Ok(_) => println!(" ERROR: should have failed!"),
208 }
209 }
210
211 // ── Error: duplicate step name ──
212 println!("\n=== Duplicate step ===");
213 {
214 let mut pipe = Pipeline::default();
215 pipe.var("v", "val")?;
216
217 pipe.step::<SetVar>("same_name", params!("name" => "a", "value" => "b"))?;
218 match pipe.step::<SetVar>("same_name", params!("name" => "c", "value" => "d")) {
219 Err(e) => println!(" Caught: {}", e),
220 Ok(_) => println!(" ERROR: should have failed!"),
221 }
222 }
223
224 // ── StepFilter: deny a step from executing ──
225 println!("\n=== StepFilter deny ===");
226 {
227 let mut pipe = Pipeline::default();
228 pipe.var("x", 1)?;
229
230 pipe.step::<SetVar>("allowed_step", params!("name" => "a", "value" => "ok"))?;
231 pipe.step::<SetVar>(
232 "blocked_step",
233 params!("name" => "b", "value" => Param::reference("a")),
234 )?;
235
236 pipe.returns("result", params!("a" => Param::reference("a")))?;
237
238 pipe.hook(StepFilter::deny(["blocked_step"]));
239
240 match pipe.compile()?.run().wait() {
241 Err(e) => println!(" Caught: {}", e),
242 Ok(_) => println!(" ERROR: should have been denied!"),
243 }
244 }
245
246 Ok(())
247}Sourcepub fn reference(path: impl Into<String>) -> Self
pub fn reference(path: impl Into<String>) -> Self
Constructs a Param::Ref pointing at a dotted store path.
Examples found in repository?
11fn main() -> Result<(), Box<dyn std::error::Error>> {
12 // ── Basic guard: Compare produces true, body executes ──
13 println!("=== Guard (true) ===");
14 {
15 let mut pipe = Pipeline::default();
16 pipe.var("status_code", 200i64)?;
17
18 pipe.step::<Compare>(
19 "is_ok",
20 params!(
21 "left" => Param::reference("status_code"),
22 "op" => "eq",
23 "right" => 200i64,
24 "name" => "should_run",
25 ),
26 )?;
27
28 pipe.guard("check", GuardSource::boolean("is_ok.should_run"), |body| {
29 body.step::<SetVar>(
30 "produce",
31 params!("name" => "result", "value" => "guard body executed"),
32 )?;
33 Ok(())
34 })?;
35
36 pipe.hook(Logger::new().writer(std::io::stdout()));
37 pipe.hook(Timeout::new(Duration::from_secs(5)));
38
39 let complete = pipe.compile()?.run().wait()?;
40 complete.debug();
41 }
42
43 // ── Guard skipped: Compare produces false ──
44 println!("\n=== Guard (false) ===");
45 {
46 let mut pipe = Pipeline::default();
47 pipe.var("status_code", 404i64)?;
48
49 pipe.step::<Compare>(
50 "is_ok",
51 params!(
52 "left" => Param::reference("status_code"),
53 "op" => "eq",
54 "right" => 200i64,
55 "name" => "should_run",
56 ),
57 )?;
58
59 pipe.guard("check", GuardSource::boolean("is_ok.should_run"), |body| {
60 body.step::<SetVar>(
61 "produce",
62 params!("name" => "result", "value" => "should not appear"),
63 )?;
64 Ok(())
65 })?;
66
67 pipe.hook(Logger::new().writer(std::io::stdout()));
68
69 let complete = pipe.compile()?.run().wait()?;
70 complete.debug();
71 }
72
73 // ── Guard output used by downstream step ──
74 println!("\n=== Guard output used downstream ===");
75 {
76 let mut pipe = Pipeline::default();
77 pipe.var("count", 5i64)?;
78
79 pipe.step::<Compare>(
80 "has_results",
81 params!(
82 "left" => Param::reference("count"),
83 "op" => "gt",
84 "right" => 0i64,
85 "name" => "flag",
86 ),
87 )?;
88
89 pipe.guard("gate", GuardSource::boolean("has_results.flag"), |body| {
90 body.step::<SetVar>(
91 "create",
92 params!("name" => "message", "value" => "from inside guard"),
93 )?;
94 Ok(())
95 })?;
96
97 pipe.step::<SetVar>(
98 "consume",
99 params!(
100 "name" => "final_output",
101 "value" => Param::template(vec![
102 Param::literal("received: "),
103 Param::reference("message"),
104 ]),
105 ),
106 )?;
107
108 pipe.hook(Logger::new().writer(std::io::stdout()));
109
110 let complete = pipe.compile()?.run().wait()?;
111 complete.debug();
112 }
113
114 // ── Error: unresolved guard reference ──
115 println!("\n=== Guard unresolved reference ===");
116 {
117 let mut pipe = Pipeline::default();
118
119 pipe.guard("check", GuardSource::boolean("nonexistent"), |_body| Ok(()))?;
120
121 match pipe.compile() {
122 Err(e) => println!(" Caught: {}", e),
123 Ok(_) => println!(" ERROR: should have failed!"),
124 }
125 }
126
127 Ok(())
128}More examples
12fn main() -> Result<(), Box<dyn std::error::Error>> {
13 // ── Basic iter_array: iterate over items and capture each ──
14 println!("=== Basic iter_array ===");
15 {
16 let mut pipe = Pipeline::default();
17 pipe.array("numbers")?.push(10)?.push(20)?.push(30)?;
18
19 pipe.iter_array(
20 "process",
21 IterSource::array("numbers"),
22 |_index, item, body| {
23 body.step::<SetVar>(
24 "capture",
25 params!(
26 "name" => "doubled",
27 "value" => Param::reference(item),
28 ),
29 )?;
30 Ok(())
31 },
32 )?;
33
34 pipe.hook(Logger::new().writer(std::io::stdout()));
35 pipe.hook(Timeout::new(Duration::from_secs(5)));
36
37 let complete = pipe.compile()?.run().wait()?;
38 complete.debug();
39 }
40
41 // ── iter_array with parent variable references ──
42 println!("\n=== iter_array referencing parent vars ===");
43 {
44 let mut pipe = Pipeline::default();
45 pipe.var("prefix", "item_")?;
46 pipe.array("items")?
47 .push("alpha")?
48 .push("beta")?
49 .push("gamma")?;
50
51 pipe.iter_array("loop", IterSource::array("items"), |_index, item, body| {
52 body.step::<SetVar>(
53 "combine",
54 params!(
55 "name" => "label",
56 "value" => Param::template(vec![
57 Param::reference("prefix"),
58 Param::reference(item),
59 ]),
60 ),
61 )?;
62 Ok(())
63 })?;
64
65 let complete = pipe.compile()?.run().wait()?;
66 complete.debug();
67 }
68
69 // ── iter_array with multiple body steps ──
70 println!("\n=== iter_array with multi-step body ===");
71 {
72 let mut pipe = Pipeline::default();
73 pipe.var("suffix", "_processed")?;
74 pipe.array("words")?.push("hello")?.push("world")?;
75
76 pipe.iter_array("each", IterSource::array("words"), |_index, item, body| {
77 body.step::<SetVar>(
78 "tag",
79 params!(
80 "name" => "tagged",
81 "value" => Param::template(vec![
82 Param::reference(item),
83 Param::reference("suffix"),
84 ]),
85 ),
86 )?;
87 body.step::<SetVar>(
88 "wrap",
89 params!(
90 "name" => "wrapped",
91 "value" => Param::template(vec![
92 Param::literal("["),
93 Param::reference("tagged"),
94 Param::literal("]"),
95 ]),
96 ),
97 )?;
98 Ok(())
99 })?;
100
101 let complete = pipe.compile()?.run().wait()?;
102 complete.debug();
103 }
104
105 // ── Error: iter_array with unresolved source ──
106 println!("\n=== iter_array unresolved source ===");
107 {
108 let mut pipe = Pipeline::default();
109
110 pipe.iter_array("loop", IterSource::array("nonexistent"), |_, _, _| Ok(()))?;
111
112 match pipe.compile() {
113 Err(e) => println!(" Caught: {}", e),
114 Ok(_) => println!(" ERROR: should have failed!"),
115 }
116 }
117
118 // ── Error: iter_array body references unknown variable ──
119 println!("\n=== iter_array unresolved body reference ===");
120 {
121 let mut pipe = Pipeline::default();
122 pipe.array("items")?.push(1)?;
123
124 pipe.iter_array("loop", IterSource::array("items"), |_, _, body| {
125 body.step::<SetVar>(
126 "bad",
127 params!(
128 "name" => "out",
129 "value" => Param::reference("ghost"),
130 ),
131 )?;
132 Ok(())
133 })?;
134
135 match pipe.compile() {
136 Err(e) => println!(" Caught: {}", e),
137 Ok(_) => println!(" ERROR: should have failed!"),
138 }
139 }
140
141 Ok(())
142}11fn main() -> Result<(), Box<dyn std::error::Error>> {
12 // ── Basic iter_map: iterate over key-value pairs ──
13 println!("=== Basic iter_map ===");
14 {
15 let mut pipe = Pipeline::default();
16 pipe.map("config")?
17 .insert("host", "localhost")?
18 .insert("port", "8080")?
19 .insert("protocol", "https")?;
20
21 pipe.iter_map(
22 "read_config",
23 IterSource::map("config"),
24 |key, value, body| {
25 body.step::<SetVar>(
26 "capture_key",
27 params!(
28 "name" => "current_key",
29 "value" => Param::reference(key),
30 ),
31 )?;
32 body.step::<SetVar>(
33 "capture_value",
34 params!(
35 "name" => "current_value",
36 "value" => Param::reference(value),
37 ),
38 )?;
39 Ok(())
40 },
41 )?;
42
43 pipe.hook(Logger::new().writer(std::io::stdout()));
44 pipe.hook(Timeout::new(Duration::from_secs(5)));
45
46 let complete = pipe.compile()?.run().wait()?;
47 complete.debug();
48 }
49
50 // ── iter_map with parent variable references ──
51 println!("\n=== iter_map referencing parent vars ===");
52 {
53 let mut pipe = Pipeline::default();
54 pipe.var("env", "production")?;
55 pipe.map("settings")?
56 .insert("db_host", "db.example.com")?
57 .insert("cache_host", "cache.example.com")?;
58
59 pipe.iter_map(
60 "apply_env",
61 IterSource::map("settings"),
62 |key, value, body| {
63 body.step::<SetVar>(
64 "label",
65 params!(
66 "name" => "entry",
67 "value" => Param::template(vec![
68 Param::reference("env"),
69 Param::literal("."),
70 Param::reference(key),
71 Param::literal("="),
72 Param::reference(value),
73 ]),
74 ),
75 )?;
76 Ok(())
77 },
78 )?;
79
80 let complete = pipe.compile()?.run().wait()?;
81 complete.debug();
82 }
83
84 // ── iter_map followed by regular steps ──
85 println!("\n=== iter_map then regular steps ===");
86 {
87 let mut pipe = Pipeline::default();
88 pipe.var("status", "ready")?;
89 pipe.map("headers")?
90 .insert("content-type", "application/json")?
91 .insert("accept", "text/html")?;
92
93 pipe.iter_map(
94 "scan_headers",
95 IterSource::map("headers"),
96 |_key, value, body| {
97 body.step::<SetVar>(
98 "save",
99 params!(
100 "name" => "last_header",
101 "value" => Param::reference(value),
102 ),
103 )?;
104 Ok(())
105 },
106 )?;
107
108 // Regular step after the iteration
109 pipe.step::<SetVar>(
110 "finalize",
111 params!(
112 "name" => "done",
113 "value" => Param::reference("status"),
114 ),
115 )?;
116
117 pipe.returns(
118 "result",
119 params!(
120 "status" => Param::reference("done"),
121 ),
122 )?;
123
124 let complete = pipe.compile()?.run().wait()?;
125 complete.debug();
126 }
127
128 // ── Error: iter_map with unresolved source ──
129 println!("\n=== iter_map unresolved source ===");
130 {
131 let mut pipe = Pipeline::default();
132
133 pipe.iter_map("loop", IterSource::map("missing"), |_, _, _| Ok(()))?;
134
135 match pipe.compile() {
136 Err(e) => println!(" Caught: {}", e),
137 Ok(_) => println!(" ERROR: should have failed!"),
138 }
139 }
140
141 // ── Error: iter_map body references unknown variable ──
142 println!("\n=== iter_map unresolved body reference ===");
143 {
144 let mut pipe = Pipeline::default();
145 pipe.map("data")?.insert("k", "v")?;
146
147 pipe.iter_map("loop", IterSource::map("data"), |_, _, body| {
148 body.step::<SetVar>(
149 "bad",
150 params!(
151 "name" => "out",
152 "value" => Param::reference("phantom"),
153 ),
154 )?;
155 Ok(())
156 })?;
157
158 match pipe.compile() {
159 Err(e) => println!(" Caught: {}", e),
160 Ok(_) => println!(" ERROR: should have failed!"),
161 }
162 }
163
164 Ok(())
165}13fn main() -> Result<(), Box<dyn std::error::Error>> {
14 // ── Happy path: derived outputs resolved at compile time ──
15 println!("=== Valid pipeline ===");
16 {
17 let mut pipe = Pipeline::default();
18
19 pipe.var("number", 1)?;
20 pipe.var("string", "example")?;
21 pipe.array("array")?.push(1)?.push(2)?.push(3)?;
22
23 // Derived output name from a variable ref: "string" → "example"
24 pipe.step::<SetVar>(
25 "set_var_1",
26 params!(
27 "name" => Param::reference("string"),
28 "value" => Param::reference("number"),
29 ),
30 )?;
31
32 // Derived output name from a literal: "greeting"
33 pipe.step::<SetVar>(
34 "set_var_2",
35 params!(
36 "name" => "greeting",
37 "value" => "hello world",
38 ),
39 )?;
40
41 // Chained: use derived output "greeting" from set_var_2 as a variable reference
42 pipe.step::<SetVar>(
43 "set_var_3",
44 params!(
45 "name" => "farewell",
46 "value" => Param::reference("greeting"),
47 ),
48 )?;
49
50 // Template referencing a derived output
51 pipe.step::<SetVar>(
52 "set_var_4",
53 params!(
54 "name" => "message",
55 "value" => Param::template(vec![
56 Param::reference("greeting"),
57 Param::literal(" and goodbye"),
58 ]),
59 ),
60 )?;
61
62 pipe.returns(
63 "summary",
64 params!(
65 "original_number" => Param::reference("number"),
66 "original_string" => Param::reference("string"),
67 "the_array" => Param::reference("array"),
68 "greeting" => Param::reference("greeting"),
69 "derived_from_ref" => Param::reference("example"),
70 "farewell" => Param::reference("farewell"),
71 "message" => Param::reference("message"),
72 ),
73 )?;
74
75 // Logger: writes each event to stdout
76 pipe.hook(Logger::new().writer(std::io::stdout()));
77
78 // Profiler: tracks per-step wall-clock duration
79 let profiler = Profiler::new().writer(std::io::stdout());
80 let timings = profiler.timings();
81 pipe.hook(profiler);
82
83 // EventLog: collects structured event records for post-mortem inspection
84 let event_log = EventLog::new();
85 let log = event_log.log();
86 pipe.hook(event_log);
87
88 // Timeout: aborts if pipeline exceeds 5 seconds
89 pipe.hook(Timeout::new(Duration::from_secs(5)));
90
91 // StoreValidator: assert "greeting" exists as Text after set_var_2
92 pipe.hook(
93 StoreValidator::new()
94 .after_step("set_var_2")
95 .expect("greeting", Type::Text),
96 );
97
98 let complete = pipe.compile()?.run().wait()?;
99 complete.debug();
100
101 // Read profiler timings programmatically
102 println!("\nProgrammatic timings:");
103 for (name, dur) in timings.lock().unwrap().iter() {
104 println!(" {} took {:.3}ms", name, dur.as_secs_f64() * 1000.0);
105 }
106
107 // Read event log
108 println!("\nEvent log ({} events):", log.lock().unwrap().len());
109 for record in log.lock().unwrap().iter() {
110 println!(
111 " [{:.3}ms] {:?}{}",
112 record.elapsed.as_secs_f64() * 1000.0,
113 record.kind,
114 record
115 .step_name
116 .as_deref()
117 .map(|s| format!(" ({})", s))
118 .unwrap_or_default(),
119 );
120 }
121 }
122
123 // ── Error: step references a variable that doesn't exist ──
124 println!("\n=== Unresolved step reference ===");
125 {
126 let mut pipe = Pipeline::default();
127 pipe.var("number", 1)?;
128
129 pipe.step::<SetVar>(
130 "bad_step",
131 params!(
132 "name" => "output",
133 "value" => Param::reference("does_not_exist"),
134 ),
135 )?;
136
137 match pipe.compile() {
138 Err(e) => println!(" Caught: {}", e),
139 Ok(_) => println!(" ERROR: should have failed!"),
140 }
141 }
142
143 // ── Error: return references something that was never produced ──
144 println!("\n=== Unresolved return reference ===");
145 {
146 let mut pipe = Pipeline::default();
147 pipe.var("number", 1)?;
148
149 pipe.step::<SetVar>(
150 "ok_step",
151 params!(
152 "name" => "output",
153 "value" => Param::reference("number"),
154 ),
155 )?;
156
157 pipe.returns(
158 "result",
159 params!(
160 "good" => Param::reference("output"),
161 "bad" => Param::reference("never_created"),
162 ),
163 )?;
164
165 match pipe.compile() {
166 Err(e) => println!(" Caught: {}", e),
167 Ok(_) => println!(" ERROR: should have failed!"),
168 }
169 }
170
171 // ── Error: step references a later step's output (ordering) ──
172 println!("\n=== Forward reference (wrong order) ===");
173 {
174 let mut pipe = Pipeline::default();
175 pipe.var("base", "start")?;
176
177 // This step tries to reference "later_output" which doesn't exist yet
178 pipe.step::<SetVar>(
179 "step_1",
180 params!(
181 "name" => "early",
182 "value" => Param::reference("later_output"),
183 ),
184 )?;
185
186 pipe.step::<SetVar>(
187 "step_2",
188 params!(
189 "name" => "later_output",
190 "value" => Param::reference("base"),
191 ),
192 )?;
193
194 match pipe.compile() {
195 Err(e) => println!(" Caught: {}", e),
196 Ok(_) => println!(" ERROR: should have failed!"),
197 }
198 }
199
200 // ── Error: duplicate variable name ──
201 println!("\n=== Duplicate variable ===");
202 {
203 let mut pipe = Pipeline::default();
204 pipe.var("x", 1)?;
205 match pipe.var("x", 2) {
206 Err(e) => println!(" Caught: {}", e),
207 Ok(_) => println!(" ERROR: should have failed!"),
208 }
209 }
210
211 // ── Error: duplicate step name ──
212 println!("\n=== Duplicate step ===");
213 {
214 let mut pipe = Pipeline::default();
215 pipe.var("v", "val")?;
216
217 pipe.step::<SetVar>("same_name", params!("name" => "a", "value" => "b"))?;
218 match pipe.step::<SetVar>("same_name", params!("name" => "c", "value" => "d")) {
219 Err(e) => println!(" Caught: {}", e),
220 Ok(_) => println!(" ERROR: should have failed!"),
221 }
222 }
223
224 // ── StepFilter: deny a step from executing ──
225 println!("\n=== StepFilter deny ===");
226 {
227 let mut pipe = Pipeline::default();
228 pipe.var("x", 1)?;
229
230 pipe.step::<SetVar>("allowed_step", params!("name" => "a", "value" => "ok"))?;
231 pipe.step::<SetVar>(
232 "blocked_step",
233 params!("name" => "b", "value" => Param::reference("a")),
234 )?;
235
236 pipe.returns("result", params!("a" => Param::reference("a")))?;
237
238 pipe.hook(StepFilter::deny(["blocked_step"]));
239
240 match pipe.compile()?.run().wait() {
241 Err(e) => println!(" Caught: {}", e),
242 Ok(_) => println!(" ERROR: should have been denied!"),
243 }
244 }
245
246 Ok(())
247}Sourcepub fn template(parts: Vec<Param>) -> Self
pub fn template(parts: Vec<Param>) -> Self
Constructs a Param::Template from an ordered list of parts.
Examples found in repository?
11fn main() -> Result<(), Box<dyn std::error::Error>> {
12 // ── Basic guard: Compare produces true, body executes ──
13 println!("=== Guard (true) ===");
14 {
15 let mut pipe = Pipeline::default();
16 pipe.var("status_code", 200i64)?;
17
18 pipe.step::<Compare>(
19 "is_ok",
20 params!(
21 "left" => Param::reference("status_code"),
22 "op" => "eq",
23 "right" => 200i64,
24 "name" => "should_run",
25 ),
26 )?;
27
28 pipe.guard("check", GuardSource::boolean("is_ok.should_run"), |body| {
29 body.step::<SetVar>(
30 "produce",
31 params!("name" => "result", "value" => "guard body executed"),
32 )?;
33 Ok(())
34 })?;
35
36 pipe.hook(Logger::new().writer(std::io::stdout()));
37 pipe.hook(Timeout::new(Duration::from_secs(5)));
38
39 let complete = pipe.compile()?.run().wait()?;
40 complete.debug();
41 }
42
43 // ── Guard skipped: Compare produces false ──
44 println!("\n=== Guard (false) ===");
45 {
46 let mut pipe = Pipeline::default();
47 pipe.var("status_code", 404i64)?;
48
49 pipe.step::<Compare>(
50 "is_ok",
51 params!(
52 "left" => Param::reference("status_code"),
53 "op" => "eq",
54 "right" => 200i64,
55 "name" => "should_run",
56 ),
57 )?;
58
59 pipe.guard("check", GuardSource::boolean("is_ok.should_run"), |body| {
60 body.step::<SetVar>(
61 "produce",
62 params!("name" => "result", "value" => "should not appear"),
63 )?;
64 Ok(())
65 })?;
66
67 pipe.hook(Logger::new().writer(std::io::stdout()));
68
69 let complete = pipe.compile()?.run().wait()?;
70 complete.debug();
71 }
72
73 // ── Guard output used by downstream step ──
74 println!("\n=== Guard output used downstream ===");
75 {
76 let mut pipe = Pipeline::default();
77 pipe.var("count", 5i64)?;
78
79 pipe.step::<Compare>(
80 "has_results",
81 params!(
82 "left" => Param::reference("count"),
83 "op" => "gt",
84 "right" => 0i64,
85 "name" => "flag",
86 ),
87 )?;
88
89 pipe.guard("gate", GuardSource::boolean("has_results.flag"), |body| {
90 body.step::<SetVar>(
91 "create",
92 params!("name" => "message", "value" => "from inside guard"),
93 )?;
94 Ok(())
95 })?;
96
97 pipe.step::<SetVar>(
98 "consume",
99 params!(
100 "name" => "final_output",
101 "value" => Param::template(vec![
102 Param::literal("received: "),
103 Param::reference("message"),
104 ]),
105 ),
106 )?;
107
108 pipe.hook(Logger::new().writer(std::io::stdout()));
109
110 let complete = pipe.compile()?.run().wait()?;
111 complete.debug();
112 }
113
114 // ── Error: unresolved guard reference ──
115 println!("\n=== Guard unresolved reference ===");
116 {
117 let mut pipe = Pipeline::default();
118
119 pipe.guard("check", GuardSource::boolean("nonexistent"), |_body| Ok(()))?;
120
121 match pipe.compile() {
122 Err(e) => println!(" Caught: {}", e),
123 Ok(_) => println!(" ERROR: should have failed!"),
124 }
125 }
126
127 Ok(())
128}More examples
12fn main() -> Result<(), Box<dyn std::error::Error>> {
13 // ── Basic iter_array: iterate over items and capture each ──
14 println!("=== Basic iter_array ===");
15 {
16 let mut pipe = Pipeline::default();
17 pipe.array("numbers")?.push(10)?.push(20)?.push(30)?;
18
19 pipe.iter_array(
20 "process",
21 IterSource::array("numbers"),
22 |_index, item, body| {
23 body.step::<SetVar>(
24 "capture",
25 params!(
26 "name" => "doubled",
27 "value" => Param::reference(item),
28 ),
29 )?;
30 Ok(())
31 },
32 )?;
33
34 pipe.hook(Logger::new().writer(std::io::stdout()));
35 pipe.hook(Timeout::new(Duration::from_secs(5)));
36
37 let complete = pipe.compile()?.run().wait()?;
38 complete.debug();
39 }
40
41 // ── iter_array with parent variable references ──
42 println!("\n=== iter_array referencing parent vars ===");
43 {
44 let mut pipe = Pipeline::default();
45 pipe.var("prefix", "item_")?;
46 pipe.array("items")?
47 .push("alpha")?
48 .push("beta")?
49 .push("gamma")?;
50
51 pipe.iter_array("loop", IterSource::array("items"), |_index, item, body| {
52 body.step::<SetVar>(
53 "combine",
54 params!(
55 "name" => "label",
56 "value" => Param::template(vec![
57 Param::reference("prefix"),
58 Param::reference(item),
59 ]),
60 ),
61 )?;
62 Ok(())
63 })?;
64
65 let complete = pipe.compile()?.run().wait()?;
66 complete.debug();
67 }
68
69 // ── iter_array with multiple body steps ──
70 println!("\n=== iter_array with multi-step body ===");
71 {
72 let mut pipe = Pipeline::default();
73 pipe.var("suffix", "_processed")?;
74 pipe.array("words")?.push("hello")?.push("world")?;
75
76 pipe.iter_array("each", IterSource::array("words"), |_index, item, body| {
77 body.step::<SetVar>(
78 "tag",
79 params!(
80 "name" => "tagged",
81 "value" => Param::template(vec![
82 Param::reference(item),
83 Param::reference("suffix"),
84 ]),
85 ),
86 )?;
87 body.step::<SetVar>(
88 "wrap",
89 params!(
90 "name" => "wrapped",
91 "value" => Param::template(vec![
92 Param::literal("["),
93 Param::reference("tagged"),
94 Param::literal("]"),
95 ]),
96 ),
97 )?;
98 Ok(())
99 })?;
100
101 let complete = pipe.compile()?.run().wait()?;
102 complete.debug();
103 }
104
105 // ── Error: iter_array with unresolved source ──
106 println!("\n=== iter_array unresolved source ===");
107 {
108 let mut pipe = Pipeline::default();
109
110 pipe.iter_array("loop", IterSource::array("nonexistent"), |_, _, _| Ok(()))?;
111
112 match pipe.compile() {
113 Err(e) => println!(" Caught: {}", e),
114 Ok(_) => println!(" ERROR: should have failed!"),
115 }
116 }
117
118 // ── Error: iter_array body references unknown variable ──
119 println!("\n=== iter_array unresolved body reference ===");
120 {
121 let mut pipe = Pipeline::default();
122 pipe.array("items")?.push(1)?;
123
124 pipe.iter_array("loop", IterSource::array("items"), |_, _, body| {
125 body.step::<SetVar>(
126 "bad",
127 params!(
128 "name" => "out",
129 "value" => Param::reference("ghost"),
130 ),
131 )?;
132 Ok(())
133 })?;
134
135 match pipe.compile() {
136 Err(e) => println!(" Caught: {}", e),
137 Ok(_) => println!(" ERROR: should have failed!"),
138 }
139 }
140
141 Ok(())
142}11fn main() -> Result<(), Box<dyn std::error::Error>> {
12 // ── Basic iter_map: iterate over key-value pairs ──
13 println!("=== Basic iter_map ===");
14 {
15 let mut pipe = Pipeline::default();
16 pipe.map("config")?
17 .insert("host", "localhost")?
18 .insert("port", "8080")?
19 .insert("protocol", "https")?;
20
21 pipe.iter_map(
22 "read_config",
23 IterSource::map("config"),
24 |key, value, body| {
25 body.step::<SetVar>(
26 "capture_key",
27 params!(
28 "name" => "current_key",
29 "value" => Param::reference(key),
30 ),
31 )?;
32 body.step::<SetVar>(
33 "capture_value",
34 params!(
35 "name" => "current_value",
36 "value" => Param::reference(value),
37 ),
38 )?;
39 Ok(())
40 },
41 )?;
42
43 pipe.hook(Logger::new().writer(std::io::stdout()));
44 pipe.hook(Timeout::new(Duration::from_secs(5)));
45
46 let complete = pipe.compile()?.run().wait()?;
47 complete.debug();
48 }
49
50 // ── iter_map with parent variable references ──
51 println!("\n=== iter_map referencing parent vars ===");
52 {
53 let mut pipe = Pipeline::default();
54 pipe.var("env", "production")?;
55 pipe.map("settings")?
56 .insert("db_host", "db.example.com")?
57 .insert("cache_host", "cache.example.com")?;
58
59 pipe.iter_map(
60 "apply_env",
61 IterSource::map("settings"),
62 |key, value, body| {
63 body.step::<SetVar>(
64 "label",
65 params!(
66 "name" => "entry",
67 "value" => Param::template(vec![
68 Param::reference("env"),
69 Param::literal("."),
70 Param::reference(key),
71 Param::literal("="),
72 Param::reference(value),
73 ]),
74 ),
75 )?;
76 Ok(())
77 },
78 )?;
79
80 let complete = pipe.compile()?.run().wait()?;
81 complete.debug();
82 }
83
84 // ── iter_map followed by regular steps ──
85 println!("\n=== iter_map then regular steps ===");
86 {
87 let mut pipe = Pipeline::default();
88 pipe.var("status", "ready")?;
89 pipe.map("headers")?
90 .insert("content-type", "application/json")?
91 .insert("accept", "text/html")?;
92
93 pipe.iter_map(
94 "scan_headers",
95 IterSource::map("headers"),
96 |_key, value, body| {
97 body.step::<SetVar>(
98 "save",
99 params!(
100 "name" => "last_header",
101 "value" => Param::reference(value),
102 ),
103 )?;
104 Ok(())
105 },
106 )?;
107
108 // Regular step after the iteration
109 pipe.step::<SetVar>(
110 "finalize",
111 params!(
112 "name" => "done",
113 "value" => Param::reference("status"),
114 ),
115 )?;
116
117 pipe.returns(
118 "result",
119 params!(
120 "status" => Param::reference("done"),
121 ),
122 )?;
123
124 let complete = pipe.compile()?.run().wait()?;
125 complete.debug();
126 }
127
128 // ── Error: iter_map with unresolved source ──
129 println!("\n=== iter_map unresolved source ===");
130 {
131 let mut pipe = Pipeline::default();
132
133 pipe.iter_map("loop", IterSource::map("missing"), |_, _, _| Ok(()))?;
134
135 match pipe.compile() {
136 Err(e) => println!(" Caught: {}", e),
137 Ok(_) => println!(" ERROR: should have failed!"),
138 }
139 }
140
141 // ── Error: iter_map body references unknown variable ──
142 println!("\n=== iter_map unresolved body reference ===");
143 {
144 let mut pipe = Pipeline::default();
145 pipe.map("data")?.insert("k", "v")?;
146
147 pipe.iter_map("loop", IterSource::map("data"), |_, _, body| {
148 body.step::<SetVar>(
149 "bad",
150 params!(
151 "name" => "out",
152 "value" => Param::reference("phantom"),
153 ),
154 )?;
155 Ok(())
156 })?;
157
158 match pipe.compile() {
159 Err(e) => println!(" Caught: {}", e),
160 Ok(_) => println!(" ERROR: should have failed!"),
161 }
162 }
163
164 Ok(())
165}13fn main() -> Result<(), Box<dyn std::error::Error>> {
14 // ── Happy path: derived outputs resolved at compile time ──
15 println!("=== Valid pipeline ===");
16 {
17 let mut pipe = Pipeline::default();
18
19 pipe.var("number", 1)?;
20 pipe.var("string", "example")?;
21 pipe.array("array")?.push(1)?.push(2)?.push(3)?;
22
23 // Derived output name from a variable ref: "string" → "example"
24 pipe.step::<SetVar>(
25 "set_var_1",
26 params!(
27 "name" => Param::reference("string"),
28 "value" => Param::reference("number"),
29 ),
30 )?;
31
32 // Derived output name from a literal: "greeting"
33 pipe.step::<SetVar>(
34 "set_var_2",
35 params!(
36 "name" => "greeting",
37 "value" => "hello world",
38 ),
39 )?;
40
41 // Chained: use derived output "greeting" from set_var_2 as a variable reference
42 pipe.step::<SetVar>(
43 "set_var_3",
44 params!(
45 "name" => "farewell",
46 "value" => Param::reference("greeting"),
47 ),
48 )?;
49
50 // Template referencing a derived output
51 pipe.step::<SetVar>(
52 "set_var_4",
53 params!(
54 "name" => "message",
55 "value" => Param::template(vec![
56 Param::reference("greeting"),
57 Param::literal(" and goodbye"),
58 ]),
59 ),
60 )?;
61
62 pipe.returns(
63 "summary",
64 params!(
65 "original_number" => Param::reference("number"),
66 "original_string" => Param::reference("string"),
67 "the_array" => Param::reference("array"),
68 "greeting" => Param::reference("greeting"),
69 "derived_from_ref" => Param::reference("example"),
70 "farewell" => Param::reference("farewell"),
71 "message" => Param::reference("message"),
72 ),
73 )?;
74
75 // Logger: writes each event to stdout
76 pipe.hook(Logger::new().writer(std::io::stdout()));
77
78 // Profiler: tracks per-step wall-clock duration
79 let profiler = Profiler::new().writer(std::io::stdout());
80 let timings = profiler.timings();
81 pipe.hook(profiler);
82
83 // EventLog: collects structured event records for post-mortem inspection
84 let event_log = EventLog::new();
85 let log = event_log.log();
86 pipe.hook(event_log);
87
88 // Timeout: aborts if pipeline exceeds 5 seconds
89 pipe.hook(Timeout::new(Duration::from_secs(5)));
90
91 // StoreValidator: assert "greeting" exists as Text after set_var_2
92 pipe.hook(
93 StoreValidator::new()
94 .after_step("set_var_2")
95 .expect("greeting", Type::Text),
96 );
97
98 let complete = pipe.compile()?.run().wait()?;
99 complete.debug();
100
101 // Read profiler timings programmatically
102 println!("\nProgrammatic timings:");
103 for (name, dur) in timings.lock().unwrap().iter() {
104 println!(" {} took {:.3}ms", name, dur.as_secs_f64() * 1000.0);
105 }
106
107 // Read event log
108 println!("\nEvent log ({} events):", log.lock().unwrap().len());
109 for record in log.lock().unwrap().iter() {
110 println!(
111 " [{:.3}ms] {:?}{}",
112 record.elapsed.as_secs_f64() * 1000.0,
113 record.kind,
114 record
115 .step_name
116 .as_deref()
117 .map(|s| format!(" ({})", s))
118 .unwrap_or_default(),
119 );
120 }
121 }
122
123 // ── Error: step references a variable that doesn't exist ──
124 println!("\n=== Unresolved step reference ===");
125 {
126 let mut pipe = Pipeline::default();
127 pipe.var("number", 1)?;
128
129 pipe.step::<SetVar>(
130 "bad_step",
131 params!(
132 "name" => "output",
133 "value" => Param::reference("does_not_exist"),
134 ),
135 )?;
136
137 match pipe.compile() {
138 Err(e) => println!(" Caught: {}", e),
139 Ok(_) => println!(" ERROR: should have failed!"),
140 }
141 }
142
143 // ── Error: return references something that was never produced ──
144 println!("\n=== Unresolved return reference ===");
145 {
146 let mut pipe = Pipeline::default();
147 pipe.var("number", 1)?;
148
149 pipe.step::<SetVar>(
150 "ok_step",
151 params!(
152 "name" => "output",
153 "value" => Param::reference("number"),
154 ),
155 )?;
156
157 pipe.returns(
158 "result",
159 params!(
160 "good" => Param::reference("output"),
161 "bad" => Param::reference("never_created"),
162 ),
163 )?;
164
165 match pipe.compile() {
166 Err(e) => println!(" Caught: {}", e),
167 Ok(_) => println!(" ERROR: should have failed!"),
168 }
169 }
170
171 // ── Error: step references a later step's output (ordering) ──
172 println!("\n=== Forward reference (wrong order) ===");
173 {
174 let mut pipe = Pipeline::default();
175 pipe.var("base", "start")?;
176
177 // This step tries to reference "later_output" which doesn't exist yet
178 pipe.step::<SetVar>(
179 "step_1",
180 params!(
181 "name" => "early",
182 "value" => Param::reference("later_output"),
183 ),
184 )?;
185
186 pipe.step::<SetVar>(
187 "step_2",
188 params!(
189 "name" => "later_output",
190 "value" => Param::reference("base"),
191 ),
192 )?;
193
194 match pipe.compile() {
195 Err(e) => println!(" Caught: {}", e),
196 Ok(_) => println!(" ERROR: should have failed!"),
197 }
198 }
199
200 // ── Error: duplicate variable name ──
201 println!("\n=== Duplicate variable ===");
202 {
203 let mut pipe = Pipeline::default();
204 pipe.var("x", 1)?;
205 match pipe.var("x", 2) {
206 Err(e) => println!(" Caught: {}", e),
207 Ok(_) => println!(" ERROR: should have failed!"),
208 }
209 }
210
211 // ── Error: duplicate step name ──
212 println!("\n=== Duplicate step ===");
213 {
214 let mut pipe = Pipeline::default();
215 pipe.var("v", "val")?;
216
217 pipe.step::<SetVar>("same_name", params!("name" => "a", "value" => "b"))?;
218 match pipe.step::<SetVar>("same_name", params!("name" => "c", "value" => "d")) {
219 Err(e) => println!(" Caught: {}", e),
220 Ok(_) => println!(" ERROR: should have failed!"),
221 }
222 }
223
224 // ── StepFilter: deny a step from executing ──
225 println!("\n=== StepFilter deny ===");
226 {
227 let mut pipe = Pipeline::default();
228 pipe.var("x", 1)?;
229
230 pipe.step::<SetVar>("allowed_step", params!("name" => "a", "value" => "ok"))?;
231 pipe.step::<SetVar>(
232 "blocked_step",
233 params!("name" => "b", "value" => Param::reference("a")),
234 )?;
235
236 pipe.returns("result", params!("a" => Param::reference("a")))?;
237
238 pipe.hook(StepFilter::deny(["blocked_step"]));
239
240 match pipe.compile()?.run().wait() {
241 Err(e) => println!(" Caught: {}", e),
242 Ok(_) => println!(" ERROR: should have been denied!"),
243 }
244 }
245
246 Ok(())
247}Sourcepub fn array(items: Vec<Param>) -> Self
pub fn array(items: Vec<Param>) -> Self
Constructs a Param::Array from an ordered list of parameters.
Sourcepub fn map(entries: HashMap<String, Param>) -> Self
pub fn map(entries: HashMap<String, Param>) -> Self
Constructs a Param::Map from a map of named parameters.
Sourcepub fn get_references(&self) -> Vec<String>
pub fn get_references(&self) -> Vec<String>
Returns every dotted store path referenced by this parameter, walking into nested templates, arrays, and maps. Used by draft-time validation to catch forward and unresolved references.