etdl-compiler 0.1.0

ETDL semantic validator, fault tree resolver, and code generator
Documentation
use etdl_parser::ast::EtlDocument;
use etdl_parser::asyncapi::AsyncApiRegistry;

pub mod codegen;
pub mod fault_tree;
mod typeck;
pub mod validate;

pub use codegen::{CodeGenerator, RustCodeGenerator};
pub use validate::Diagnostic;

pub struct Compiler {
    pub rust_codegen: RustCodeGenerator,
}

#[derive(Debug, Clone)]
pub struct CompilationResult {
    pub diagnostics: Vec<Diagnostic>,
    pub rust_output: Option<String>,
}

impl Compiler {
    pub fn new() -> Self {
        Compiler {
            rust_codegen: RustCodeGenerator::new(),
        }
    }

    /// Run the full validation pipeline (semantic checks, fault-tree
    /// resolution, probability validation, ECEL type checking) without
    /// generating any code.
    pub fn validate(
        &self,
        doc: &EtlDocument,
        asyncapi_registry: &AsyncApiRegistry,
    ) -> Vec<Diagnostic> {
        let mut diagnostics = Vec::new();

        validate::validate_document(doc, asyncapi_registry, &mut diagnostics);

        if diagnostics.iter().any(|d| d.is_error()) {
            return diagnostics;
        }

        let fault_tree_probs = fault_tree::resolve_fault_trees(doc, &mut diagnostics);
        let resolved_probabilities =
            validate::resolve_probability_links(doc, &fault_tree_probs, &mut diagnostics);

        validate::validate_probability_sums(doc, &resolved_probabilities, &mut diagnostics);

        if diagnostics.iter().any(|d| d.is_error()) {
            return diagnostics;
        }

        typeck::type_check_conditions(doc, asyncapi_registry, &mut diagnostics);

        diagnostics
    }

    pub fn compile(
        &self,
        doc: &EtlDocument,
        asyncapi_registry: &AsyncApiRegistry,
    ) -> CompilationResult {
        let mut diagnostics = self.validate(doc, asyncapi_registry);

        let has_errors = diagnostics.iter().any(|d| d.is_error());
        if has_errors {
            return CompilationResult {
                diagnostics,
                rust_output: None,
            };
        }

        let fault_tree_probs = fault_tree::resolve_fault_trees(doc, &mut Vec::new());

        let mut rust_output = String::new();
        let gen_result = self
            .rust_codegen
            .generate_all(doc, &fault_tree_probs, &asyncapi_registry, &mut diagnostics);

        if gen_result.is_ok() {
            rust_output = gen_result.unwrap();
        }

        CompilationResult {
            diagnostics,
            rust_output: if rust_output.is_empty() {
                None
            } else {
                Some(rust_output)
            },
        }
    }
}

impl Default for Compiler {
    fn default() -> Self {
        Self::new()
    }
}