nika_core/ast/
decompose.rs1use serde::{Deserialize, Serialize};
20
21#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq)]
23#[serde(rename_all = "snake_case")]
24pub enum DecomposeStrategy {
25 #[default]
27 Semantic,
28 Static,
30 Nested,
32}
33
34#[derive(Debug, Clone, Deserialize, Serialize)]
39pub struct DecomposeSpec {
40 #[serde(default)]
42 pub strategy: DecomposeStrategy,
43 pub traverse: String,
45 pub source: String,
47 #[serde(default)]
49 pub mcp_server: Option<String>,
50 #[serde(default)]
52 pub max_items: Option<usize>,
53 #[serde(default)]
55 pub max_depth: Option<usize>,
56}
57
58impl DecomposeSpec {
59 pub fn mcp_server(&self) -> &str {
61 self.mcp_server.as_deref().unwrap_or("novanet")
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use crate::serde_yaml;
69
70 #[test]
71 fn test_decompose_strategy_default_is_semantic() {
72 let strategy = DecomposeStrategy::default();
73 assert_eq!(strategy, DecomposeStrategy::Semantic);
74 }
75
76 #[test]
77 fn test_decompose_spec_parses_minimal() {
78 let yaml = r#"
79traverse: HAS_CHILD
80source: $entity
81"#;
82 let spec: DecomposeSpec = serde_yaml::from_str(yaml).unwrap();
83 assert_eq!(spec.strategy, DecomposeStrategy::Semantic);
84 assert_eq!(spec.traverse, "HAS_CHILD");
85 assert_eq!(spec.source, "$entity");
86 assert_eq!(spec.mcp_server(), "novanet");
87 assert!(spec.max_items.is_none());
88 }
89
90 #[test]
91 fn test_decompose_spec_parses_full() {
92 let yaml = r#"
93strategy: nested
94traverse: HAS_NATIVE
95source: "{{with.entity_key}}"
96mcp_server: custom_mcp
97max_items: 10
98"#;
99 let spec: DecomposeSpec = serde_yaml::from_str(yaml).unwrap();
100 assert_eq!(spec.strategy, DecomposeStrategy::Nested);
101 assert_eq!(spec.traverse, "HAS_NATIVE");
102 assert_eq!(spec.source, "{{with.entity_key}}");
103 assert_eq!(spec.mcp_server(), "custom_mcp");
104 assert_eq!(spec.max_items, Some(10));
105 }
106
107 #[test]
108 fn test_decompose_spec_static_strategy() {
109 let yaml = r#"
110strategy: static
111traverse: DUMMY
112source: $locales
113"#;
114 let spec: DecomposeSpec = serde_yaml::from_str(yaml).unwrap();
115 assert_eq!(spec.strategy, DecomposeStrategy::Static);
116 }
117
118 #[test]
119 fn test_decompose_spec_serializes() {
120 let spec = DecomposeSpec {
121 strategy: DecomposeStrategy::Semantic,
122 traverse: "HAS_CHILD".to_string(),
123 source: "$entity".to_string(),
124 mcp_server: None,
125 max_items: Some(5),
126 max_depth: None,
127 };
128 let yaml = serde_yaml::to_string(&spec).unwrap();
129 assert!(yaml.contains("traverse: HAS_CHILD"));
130 assert!(yaml.contains("source: $entity"));
131 assert!(yaml.contains("max_items: 5"));
132 }
133}