greentic-flow-builder 0.1.0

AI-powered Adaptive Card flow builder with visual graph editor and demo runner
Documentation
//! Validation for compose mode: ensure every section's primitive exists.

use crate::template::{ComposeSection, PrimitivesManifest};
use anyhow::bail;

pub fn validate_compose_sections(
    sections: &[ComposeSection],
    manifest: &PrimitivesManifest,
) -> anyhow::Result<()> {
    let known: Vec<&str> = manifest
        .primitives
        .iter()
        .map(|p| p.name.as_str())
        .collect();
    for (idx, section) in sections.iter().enumerate() {
        if !known.contains(&section.primitive.as_str()) {
            let suggestion = crate::discovery::find_closest(&section.primitive, &known)
                .map(|s| format!(". Did you mean '{}'?", s))
                .unwrap_or_default();
            bail!(
                "section {}: primitive '{}' not found{}",
                idx,
                section.primitive,
                suggestion
            );
        }
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::template::{PrimitiveInfo, PrimitivesManifest};
    use serde_json::json;
    use std::collections::BTreeMap;

    fn manifest(names: &[&str]) -> PrimitivesManifest {
        PrimitivesManifest {
            version: "1.0".to_string(),
            primitives: names
                .iter()
                .map(|n| PrimitiveInfo {
                    name: n.to_string(),
                    description: String::new(),
                    tags: vec![],
                    props: BTreeMap::new(),
                })
                .collect(),
        }
    }

    fn section(primitive: &str) -> ComposeSection {
        ComposeSection {
            primitive: primitive.to_string(),
            data: json!({}),
        }
    }

    #[test]
    fn test_valid_sections() {
        let sections = vec![section("heading"), section("divider")];
        let m = manifest(&["heading", "divider"]);
        assert!(validate_compose_sections(&sections, &m).is_ok());
    }

    #[test]
    fn test_unknown_primitive() {
        let sections = vec![section("xyz")];
        let m = manifest(&["heading"]);
        assert!(validate_compose_sections(&sections, &m).is_err());
    }

    #[test]
    fn test_suggestion_on_typo() {
        let sections = vec![section("headig")];
        let m = manifest(&["heading"]);
        let err = validate_compose_sections(&sections, &m).unwrap_err();
        assert!(err.to_string().contains("Did you mean"));
    }
}