Skip to main content

licoup_workflow/
analysis.rs

1//! Privacy-safe, deterministic workflow validation.
2//!
3//! The raw-value and semantic passes are the generic engine's; this module
4//! binds them to this crate's workflow schema and to the `PreflightDiagnostic`
5//! record this crate surfaces (the engine's record is a strict subset of it).
6
7use serde_json::Value;
8use std::fmt::{Display, Formatter};
9
10use crate::compile::CompiledWorkflow;
11use crate::diagnostic::PreflightDiagnostic;
12use crate::syntax::ParsedWorkflow;
13use crate::{WorkflowDefinition, workflow_schema};
14
15#[derive(Clone, Debug)]
16pub struct WorkflowValidation {
17    pub definition: Option<WorkflowDefinition>,
18    pub diagnostics: Vec<PreflightDiagnostic>,
19}
20
21#[derive(Clone, Debug)]
22pub struct WorkflowValidationFailure {
23    pub diagnostics: Vec<PreflightDiagnostic>,
24}
25
26/// A canonical definition that has passed the single semantic analysis pass.
27/// Lowering is the only consumer of this phase marker.
28#[derive(Clone, Debug)]
29pub struct AnalyzedWorkflow(acativa::AnalyzedWorkflow);
30
31impl AnalyzedWorkflow {
32    pub fn source(&self) -> Option<&Value> {
33        self.0.source()
34    }
35}
36
37impl From<acativa::WorkflowValidationFailure> for WorkflowValidationFailure {
38    fn from(failure: acativa::WorkflowValidationFailure) -> Self {
39        Self {
40            diagnostics: failure
41                .diagnostics
42                .iter()
43                .cloned()
44                .map(PreflightDiagnostic::from)
45                .collect(),
46        }
47    }
48}
49
50impl Display for WorkflowValidationFailure {
51    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
52        formatter.write_str(
53            self.diagnostics
54                .first()
55                .map_or("workflow_invalid", |diagnostic| diagnostic.code.wire()),
56        )
57    }
58}
59
60impl std::error::Error for WorkflowValidationFailure {}
61
62pub fn compile_workflow_source(
63    source: &[u8],
64) -> Result<CompiledWorkflow, WorkflowValidationFailure> {
65    acativa::compile_workflow_source(source, &workflow_schema())
66        .map_err(WorkflowValidationFailure::from)
67}
68
69pub fn compile_workflow_value(
70    value: &Value,
71) -> Result<CompiledWorkflow, WorkflowValidationFailure> {
72    acativa::compile_workflow_value(value, &workflow_schema())
73        .map_err(WorkflowValidationFailure::from)
74}
75
76pub fn validate_workflow_value(value: &Value) -> WorkflowValidation {
77    let validation = acativa::validate_workflow_value(value, &workflow_schema());
78    WorkflowValidation {
79        definition: validation.definition,
80        diagnostics: validation
81            .diagnostics
82            .iter()
83            .cloned()
84            .map(PreflightDiagnostic::from)
85            .collect(),
86    }
87}
88
89pub fn analyze(parsed: ParsedWorkflow) -> Result<AnalyzedWorkflow, WorkflowValidationFailure> {
90    acativa::analyze(parsed, &workflow_schema())
91        .map(AnalyzedWorkflow)
92        .map_err(WorkflowValidationFailure::from)
93}