Skip to main content

swf_core/validation/
document.rs

1use super::one_of_validators::validate_schedule_one_of;
2use super::{
3    is_valid_hostname, is_valid_semver, validate_required_hostname, validate_required_semver,
4    ValidationResult, ValidationRule,
5};
6use crate::models::duration::OneOfDurationOrIso8601Expression;
7use crate::models::workflow::*;
8
9/// Validates a complete workflow definition
10pub fn validate_workflow(workflow: &WorkflowDefinition) -> ValidationResult {
11    let mut result = ValidationResult::new();
12
13    // Validate document
14    validate_document(&workflow.document, &mut result);
15
16    // Validate input if present
17    if let Some(ref input) = workflow.input {
18        validate_input(input, "input", &mut result);
19    }
20
21    // Validate timeout if present
22    if let Some(ref timeout) = workflow.timeout {
23        validate_timeout(timeout, "timeout", &mut result);
24    }
25
26    // Validate schedule if present
27    if let Some(ref schedule) = workflow.schedule {
28        validate_schedule_one_of(schedule, "schedule", &mut result);
29    }
30
31    // Validate tasks
32    super::task::validate_task_map(&workflow.do_, "do", &mut result);
33
34    result
35}
36
37/// Validates workflow document metadata
38pub(crate) fn validate_document(doc: &WorkflowDefinitionMetadata, result: &mut ValidationResult) {
39    validate_required_hostname(&doc.name, "document.name", result);
40    validate_required_semver(&doc.version, "document.version", result);
41    if !doc.dsl.is_empty() && !is_valid_semver(&doc.dsl) {
42        result.add_error(
43            "document.dsl",
44            ValidationRule::Semver,
45            "DSL version must be a valid semantic version",
46        );
47    }
48    if !doc.namespace.is_empty() && !is_valid_hostname(&doc.namespace) {
49        result.add_error(
50            "document.namespace",
51            ValidationRule::Hostname,
52            "namespace must be a valid RFC 1123 hostname",
53        );
54    }
55}
56
57/// Validates an input data model definition
58pub(crate) fn validate_input(
59    input: &crate::models::input::InputDataModelDefinition,
60    prefix: &str,
61    result: &mut ValidationResult,
62) {
63    if let Some(ref from) = input.from {
64        if from.is_null() {
65            result.add_error(
66                &format!("{}.from", prefix),
67                ValidationRule::Required,
68                "input 'from' must not be null",
69            );
70        }
71    }
72}
73
74/// Validates a timeout definition or reference
75pub(crate) fn validate_timeout(
76    timeout: &crate::models::timeout::OneOfTimeoutDefinitionOrReference,
77    prefix: &str,
78    result: &mut ValidationResult,
79) {
80    match timeout {
81        crate::models::timeout::OneOfTimeoutDefinitionOrReference::Timeout(t) => {
82            validate_duration(&t.after, &format!("{}.after", prefix), result);
83        }
84        crate::models::timeout::OneOfTimeoutDefinitionOrReference::Reference(_) => {
85            // References are assumed valid at this level
86        }
87    }
88}
89
90/// Validates a duration value
91pub(crate) fn validate_duration(
92    duration: &OneOfDurationOrIso8601Expression,
93    prefix: &str,
94    result: &mut ValidationResult,
95) {
96    match duration {
97        OneOfDurationOrIso8601Expression::Iso8601Expression(expr) => {
98            if !crate::models::duration::is_iso8601_duration_valid(expr) {
99                result.add_error(
100                    prefix,
101                    ValidationRule::Iso8601Duration,
102                    &format!("'{}' is not a valid ISO 8601 duration", expr),
103                );
104            }
105        }
106        OneOfDurationOrIso8601Expression::Duration(_) => {
107            // Structured duration is always valid
108        }
109    }
110}