Skip to main content

effect_runtime/
error.rs

1//! Validation error types and helper functions for effect-runtime artifact invariant checks.
2
3use thiserror::Error;
4
5/// Validation failures for effect-runtime owned constitutional artifacts.
6#[derive(Debug, Clone, PartialEq, Eq, Error)]
7pub enum EffectRuntimeValidationError {
8    #[error("missing required field: {0}")]
9    MissingField(&'static str),
10    #[error("invalid schema version for {artifact}: expected {expected}, found {found}")]
11    InvalidSchemaVersion {
12        artifact: &'static str,
13        expected: &'static str,
14        found: String,
15    },
16    #[error("invalid artifact state: {0}")]
17    InvalidState(&'static str),
18    #[error("invalid timestamp in field {field}: {value}")]
19    InvalidTimestamp { field: &'static str, value: String },
20}
21
22impl EffectRuntimeValidationError {
23    /// Returns a stable string discriminant for this validation error kind.
24    pub fn kind(&self) -> &'static str {
25        match self {
26            Self::MissingField(..) => "missing_field",
27            Self::InvalidSchemaVersion { .. } => "invalid_schema_version",
28            Self::InvalidState(..) => "invalid_state",
29            Self::InvalidTimestamp { .. } => "invalid_timestamp",
30        }
31    }
32}
33
34/// Shorthand result type for artifact validation methods.
35pub type EffectValidationResult = Result<(), EffectRuntimeValidationError>;
36
37pub(crate) fn require_non_empty(value: &str, field: &'static str) -> EffectValidationResult {
38    if value.trim().is_empty() {
39        return Err(EffectRuntimeValidationError::MissingField(field));
40    }
41    Ok(())
42}
43
44pub(crate) fn require_id(value: &impl AsRef<str>, field: &'static str) -> EffectValidationResult {
45    require_non_empty(value.as_ref(), field)
46}
47
48pub(crate) fn require_non_empty_slice<T>(
49    values: &[T],
50    field: &'static str,
51) -> EffectValidationResult {
52    if values.is_empty() {
53        return Err(EffectRuntimeValidationError::MissingField(field));
54    }
55    Ok(())
56}
57
58pub(crate) fn require_valid_timestamp(value: &str, field: &'static str) -> EffectValidationResult {
59    if value.trim().is_empty() {
60        return Err(EffectRuntimeValidationError::MissingField(field));
61    }
62    chrono::DateTime::parse_from_rfc3339(value).map_err(|_| {
63        EffectRuntimeValidationError::InvalidTimestamp {
64            field,
65            value: value.to_string(),
66        }
67    })?;
68    Ok(())
69}
70
71pub(crate) fn require_schema_version(
72    artifact: &'static str,
73    expected: &'static str,
74    found: &str,
75) -> EffectValidationResult {
76    if found != expected {
77        return Err(EffectRuntimeValidationError::InvalidSchemaVersion {
78            artifact,
79            expected,
80            found: found.to_string(),
81        });
82    }
83    Ok(())
84}