1use schemars::JsonSchema;
14use serde::{Deserialize, Serialize};
15use std::collections::HashMap;
16
17include!("schema_types.rs");
19
20pub mod paths;
22
23pub mod agent_meta;
25
26pub mod spec;
28
29mod step_registration;
31
32pub fn parse_execution_graph(json: &serde_json::Value) -> Result<ExecutionGraph, String> {
38 serde_json::from_value(json.clone())
39 .map_err(|e| format!("Failed to parse execution graph: {}", e))
40}
41
42pub fn parse_scenario(json: &serde_json::Value) -> Result<Scenario, String> {
44 serde_json::from_value(json.clone()).map_err(|e| format!("Failed to parse scenario: {}", e))
45}
46
47#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
49pub struct StepTypeInfo {
50 #[serde(rename = "type")]
51 pub step_type: String,
52 pub category: String,
53 pub description: String,
54}
55
56pub fn get_step_types() -> Vec<StepTypeInfo> {
62 let mut steps = vec![StepTypeInfo {
64 step_type: "Start".to_string(),
65 category: "control".to_string(),
66 description: "Entry point - receives scenario inputs".to_string(),
67 }];
68
69 for meta in agent_meta::get_all_step_types() {
71 steps.push(StepTypeInfo {
72 step_type: meta.id.to_string(),
73 category: meta.category.to_string(),
74 description: meta.description.to_string(),
75 });
76 }
77
78 steps.sort_by(|a, b| a.step_type.cmp(&b.step_type));
80
81 steps
82}
83
84impl MemoryTier {
89 pub fn total_memory_bytes(&self) -> usize {
91 match self {
92 MemoryTier::S => 8 * 1024 * 1024, MemoryTier::M => 64 * 1024 * 1024, MemoryTier::L => 128 * 1024 * 1024, MemoryTier::XL => 256 * 1024 * 1024, }
97 }
98
99 pub fn stack_size_bytes(&self) -> usize {
101 match self {
102 MemoryTier::S => 1 * 1024 * 1024, MemoryTier::M => 4 * 1024 * 1024, MemoryTier::L => 8 * 1024 * 1024, MemoryTier::XL => 8 * 1024 * 1024, }
107 }
108
109 pub fn as_str(&self) -> &'static str {
111 match self {
112 MemoryTier::S => "S",
113 MemoryTier::M => "M",
114 MemoryTier::L => "L",
115 MemoryTier::XL => "XL",
116 }
117 }
118
119 pub fn from_str(s: &str) -> Option<Self> {
121 match s.to_uppercase().as_str() {
122 "S" => Some(MemoryTier::S),
123 "M" => Some(MemoryTier::M),
124 "L" => Some(MemoryTier::L),
125 "XL" => Some(MemoryTier::XL),
126 _ => None,
127 }
128 }
129}
130
131impl std::fmt::Display for MemoryTier {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 write!(f, "{}", self.as_str())
134 }
135}
136
137impl SchemaFieldType {
142 pub fn as_str(&self) -> &'static str {
144 match self {
145 SchemaFieldType::String => "string",
146 SchemaFieldType::Integer => "integer",
147 SchemaFieldType::Number => "number",
148 SchemaFieldType::Boolean => "boolean",
149 SchemaFieldType::Array => "array",
150 SchemaFieldType::Object => "object",
151 }
152 }
153}
154
155impl std::fmt::Display for SchemaFieldType {
156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157 write!(f, "{}", self.as_str())
158 }
159}
160
161impl From<&SchemaFieldType> for String {
162 fn from(t: &SchemaFieldType) -> Self {
163 t.as_str().to_string()
164 }
165}
166
167impl MappingValue {
172 pub fn is_reference(&self) -> bool {
174 matches!(self, MappingValue::Reference(_))
175 }
176
177 pub fn is_immediate(&self) -> bool {
179 matches!(self, MappingValue::Immediate(_))
180 }
181
182 pub fn as_reference_str(&self) -> Option<&str> {
184 match self {
185 MappingValue::Reference(r) => Some(&r.value),
186 _ => None,
187 }
188 }
189
190 pub fn as_immediate_value(&self) -> Option<&serde_json::Value> {
192 match self {
193 MappingValue::Immediate(i) => Some(&i.value),
194 _ => None,
195 }
196 }
197}
198
199#[cfg(test)]
204mod tests {
205 use super::*;
206
207 #[test]
208 fn test_get_step_types_from_inventory() {
209 let step_types = get_step_types();
210
211 assert!(
213 step_types.len() >= 7,
214 "Expected at least 7 step types, got {}",
215 step_types.len()
216 );
217
218 let step_ids: Vec<&str> = step_types.iter().map(|s| s.step_type.as_str()).collect();
220
221 assert!(step_ids.contains(&"Start"), "Missing Start step type");
222 assert!(step_ids.contains(&"Finish"), "Missing Finish step type");
223 assert!(step_ids.contains(&"Agent"), "Missing Agent step type");
224 assert!(
225 step_ids.contains(&"Conditional"),
226 "Missing Conditional step type"
227 );
228 assert!(step_ids.contains(&"Split"), "Missing Split step type");
229 assert!(step_ids.contains(&"Switch"), "Missing Switch step type");
230 assert!(
231 step_ids.contains(&"StartScenario"),
232 "Missing StartScenario step type"
233 );
234 }
235
236 #[test]
237 fn test_step_type_categories() {
238 let step_types = get_step_types();
239
240 for step in &step_types {
241 match step.step_type.as_str() {
242 "Agent" | "StartScenario" => {
243 assert_eq!(
244 step.category, "execution",
245 "{} should be execution category",
246 step.step_type
247 );
248 }
249 "Start" | "Finish" | "Conditional" | "Split" | "Switch" => {
250 assert_eq!(
251 step.category, "control",
252 "{} should be control category",
253 step.step_type
254 );
255 }
256 _ => {}
257 }
258 }
259 }
260
261 #[test]
262 fn test_step_type_schema_generation() {
263 for meta in agent_meta::get_all_step_types() {
265 let schema = (meta.schema_fn)();
266 assert!(
268 schema.schema.metadata.is_some() || schema.definitions.len() > 0,
269 "Schema for {} should have metadata or definitions",
270 meta.id
271 );
272 }
273 }
274
275 #[test]
280 fn test_value_type_serialization() {
281 assert_eq!(
283 serde_json::to_string(&ValueType::Integer).unwrap(),
284 "\"integer\""
285 );
286 assert_eq!(
287 serde_json::to_string(&ValueType::Number).unwrap(),
288 "\"number\""
289 );
290 assert_eq!(
291 serde_json::to_string(&ValueType::Boolean).unwrap(),
292 "\"boolean\""
293 );
294 assert_eq!(
295 serde_json::to_string(&ValueType::String).unwrap(),
296 "\"string\""
297 );
298 assert_eq!(serde_json::to_string(&ValueType::Json).unwrap(), "\"json\"");
299 assert_eq!(serde_json::to_string(&ValueType::File).unwrap(), "\"file\"");
300 }
301
302 #[test]
303 fn test_value_type_deserialization() {
304 assert_eq!(
306 serde_json::from_str::<ValueType>("\"integer\"").unwrap(),
307 ValueType::Integer
308 );
309 assert_eq!(
310 serde_json::from_str::<ValueType>("\"number\"").unwrap(),
311 ValueType::Number
312 );
313 assert_eq!(
314 serde_json::from_str::<ValueType>("\"boolean\"").unwrap(),
315 ValueType::Boolean
316 );
317 }
318
319 #[test]
320 fn test_schema_field_type_serialization() {
321 assert_eq!(
322 serde_json::to_string(&SchemaFieldType::String).unwrap(),
323 "\"string\""
324 );
325 assert_eq!(
326 serde_json::to_string(&SchemaFieldType::Integer).unwrap(),
327 "\"integer\""
328 );
329 assert_eq!(
330 serde_json::to_string(&SchemaFieldType::Number).unwrap(),
331 "\"number\""
332 );
333 assert_eq!(
334 serde_json::to_string(&SchemaFieldType::Boolean).unwrap(),
335 "\"boolean\""
336 );
337 assert_eq!(
338 serde_json::to_string(&SchemaFieldType::Array).unwrap(),
339 "\"array\""
340 );
341 assert_eq!(
342 serde_json::to_string(&SchemaFieldType::Object).unwrap(),
343 "\"object\""
344 );
345 }
346
347 #[test]
348 fn test_schema_field_type_as_str() {
349 assert_eq!(SchemaFieldType::String.as_str(), "string");
350 assert_eq!(SchemaFieldType::Integer.as_str(), "integer");
351 assert_eq!(SchemaFieldType::Number.as_str(), "number");
352 assert_eq!(SchemaFieldType::Boolean.as_str(), "boolean");
353 assert_eq!(SchemaFieldType::Array.as_str(), "array");
354 assert_eq!(SchemaFieldType::Object.as_str(), "object");
355 }
356
357 #[test]
358 fn test_schema_field_type_display() {
359 assert_eq!(format!("{}", SchemaFieldType::String), "string");
360 assert_eq!(format!("{}", SchemaFieldType::Integer), "integer");
361 }
362
363 #[test]
364 fn test_switch_match_type_serialization() {
365 assert_eq!(
366 serde_json::to_string(&SwitchMatchType::Eq).unwrap(),
367 "\"EQ\""
368 );
369 assert_eq!(
370 serde_json::to_string(&SwitchMatchType::Gt).unwrap(),
371 "\"GT\""
372 );
373 assert_eq!(
374 serde_json::to_string(&SwitchMatchType::Between).unwrap(),
375 "\"BETWEEN\""
376 );
377 }
378
379 #[test]
380 fn test_switch_config_serialization() {
381 let config = SwitchConfig {
382 value: MappingValue::Reference(ReferenceValue {
383 value: "data.status".to_string(),
384 type_hint: None,
385 default: None,
386 }),
387 cases: vec![SwitchCase {
388 match_type: SwitchMatchType::Eq,
389 match_value: serde_json::json!("active"),
390 output: serde_json::json!({"result": true}),
391 }],
392 default: Some(serde_json::json!({"result": false})),
393 };
394
395 let json = serde_json::to_value(&config).unwrap();
396 assert!(json.get("value").is_some());
397 assert!(json.get("cases").is_some());
398 assert!(json.get("default").is_some());
399 }
400
401 #[test]
402 fn test_split_config_serialization() {
403 let config = SplitConfig {
404 value: MappingValue::Reference(ReferenceValue {
405 value: "data.items".to_string(),
406 type_hint: None,
407 default: None,
408 }),
409 parallelism: Some(5),
410 sequential: Some(false),
411 dont_stop_on_failed: Some(true),
412 variables: None,
413 max_retries: None,
414 retry_delay: None,
415 timeout: None,
416 };
417
418 let json = serde_json::to_value(&config).unwrap();
419 assert!(json.get("value").is_some());
420 assert_eq!(json.get("parallelism").unwrap(), 5);
421 assert_eq!(json.get("sequential").unwrap(), false);
422 assert_eq!(json.get("dontStopOnFailed").unwrap(), true);
423 }
424
425 #[test]
426 fn test_switch_step_with_config() {
427 let step = SwitchStep {
428 id: "switch1".to_string(),
429 name: Some("My Switch".to_string()),
430 config: Some(SwitchConfig {
431 value: MappingValue::Immediate(ImmediateValue {
432 value: serde_json::json!("test"),
433 }),
434 cases: vec![],
435 default: None,
436 }),
437 };
438
439 let json = serde_json::to_value(&step).unwrap();
440 assert_eq!(json.get("id").unwrap(), "switch1");
441 assert!(json.get("config").is_some());
442 }
443
444 #[test]
445 fn test_split_step_with_config() {
446 let step = SplitStep {
447 id: "split1".to_string(),
448 name: None,
449 subgraph: Box::new(ExecutionGraph {
450 name: None,
451 description: None,
452 steps: HashMap::new(),
453 entry_point: "start".to_string(),
454 execution_plan: vec![],
455 variables: HashMap::new(),
456 input_schema: HashMap::new(),
457 output_schema: HashMap::new(),
458 notes: None,
459 nodes: None,
460 edges: None,
461 }),
462 config: Some(SplitConfig {
463 value: MappingValue::Reference(ReferenceValue {
464 value: "data.items".to_string(),
465 type_hint: None,
466 default: None,
467 }),
468 parallelism: None,
469 sequential: None,
470 dont_stop_on_failed: None,
471 variables: None,
472 max_retries: None,
473 retry_delay: None,
474 timeout: None,
475 }),
476 input_schema: HashMap::new(),
477 output_schema: HashMap::new(),
478 };
479
480 let json = serde_json::to_value(&step).unwrap();
481 assert_eq!(json.get("id").unwrap(), "split1");
482 assert!(json.get("config").is_some());
483 assert!(json.get("subgraph").is_some());
484 }
485
486 #[test]
487 fn test_dsl_version() {
488 assert_eq!(DSL_VERSION, "3.0.0");
489 }
490}