use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum DecomposeStrategy {
#[default]
Semantic,
Static,
Nested,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct DecomposeSpec {
#[serde(default)]
pub strategy: DecomposeStrategy,
pub traverse: String,
pub source: String,
#[serde(default)]
pub mcp_server: Option<String>,
#[serde(default)]
pub max_items: Option<usize>,
#[serde(default)]
pub max_depth: Option<usize>,
}
impl DecomposeSpec {
pub fn mcp_server(&self) -> &str {
self.mcp_server.as_deref().unwrap_or("novanet")
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::serde_yaml;
#[test]
fn test_decompose_strategy_default_is_semantic() {
let strategy = DecomposeStrategy::default();
assert_eq!(strategy, DecomposeStrategy::Semantic);
}
#[test]
fn test_decompose_spec_parses_minimal() {
let yaml = r#"
traverse: HAS_CHILD
source: $entity
"#;
let spec: DecomposeSpec = serde_yaml::from_str(yaml).unwrap();
assert_eq!(spec.strategy, DecomposeStrategy::Semantic);
assert_eq!(spec.traverse, "HAS_CHILD");
assert_eq!(spec.source, "$entity");
assert_eq!(spec.mcp_server(), "novanet");
assert!(spec.max_items.is_none());
}
#[test]
fn test_decompose_spec_parses_full() {
let yaml = r#"
strategy: nested
traverse: HAS_NATIVE
source: "{{with.entity_key}}"
mcp_server: custom_mcp
max_items: 10
"#;
let spec: DecomposeSpec = serde_yaml::from_str(yaml).unwrap();
assert_eq!(spec.strategy, DecomposeStrategy::Nested);
assert_eq!(spec.traverse, "HAS_NATIVE");
assert_eq!(spec.source, "{{with.entity_key}}");
assert_eq!(spec.mcp_server(), "custom_mcp");
assert_eq!(spec.max_items, Some(10));
}
#[test]
fn test_decompose_spec_static_strategy() {
let yaml = r#"
strategy: static
traverse: DUMMY
source: $locales
"#;
let spec: DecomposeSpec = serde_yaml::from_str(yaml).unwrap();
assert_eq!(spec.strategy, DecomposeStrategy::Static);
}
#[test]
fn test_decompose_spec_serializes() {
let spec = DecomposeSpec {
strategy: DecomposeStrategy::Semantic,
traverse: "HAS_CHILD".to_string(),
source: "$entity".to_string(),
mcp_server: None,
max_items: Some(5),
max_depth: None,
};
let yaml = serde_yaml::to_string(&spec).unwrap();
assert!(yaml.contains("traverse: HAS_CHILD"));
assert!(yaml.contains("source: $entity"));
assert!(yaml.contains("max_items: 5"));
}
}