Skip to main content

scientific_workflow/system_state/
error.rs

1//! Errors produced while defining and manipulating system states.
2//!
3//! This module keeps the SystemState error surface in one place so template
4//! loading, layout validation, and typed payload access report failures
5//! consistently. The variants are deliberately specific enough for callers to
6//! inspect programmatically while their display messages retain the field or
7//! path context needed for logs.
8//!
9//! # Error sources
10//!
11//! Filesystem and JSON failures preserve their original errors through
12//! [`std::error::Error::source`]. Semantic template failures and state-access
13//! failures do not wrap another error because they are detected directly by
14//! this crate.
15//!
16//! # Performance
17//!
18//! These errors are constructed only on failure paths. Owned paths and field
19//! names are retained to make an error independent of the state or template
20//! that produced it; successful state access does not allocate error context.
21
22use std::io;
23use std::path::PathBuf;
24
25use thiserror::Error;
26
27/// A failure encountered while loading a state template or accessing a state.
28///
29/// `StateError` is non-exhaustive because later workflow features may add
30/// validation failures without forcing downstream crates to update exhaustive
31/// matches. Callers should match variants of interest and retain a fallback
32/// arm.
33#[derive(Debug, Error)]
34#[non_exhaustive]
35pub enum StateError {
36    /// A JSON state template could not be read from the filesystem.
37    #[error("failed to read state template `{path}`")]
38    TemplateRead {
39        /// Path passed to the template loader.
40        path: PathBuf,
41        /// Underlying filesystem error.
42        #[source]
43        source: io::Error,
44    },
45
46    /// A state template was readable but did not contain valid JSON.
47    #[error("failed to parse state template `{path}` as JSON")]
48    TemplateParse {
49        /// Path of the malformed template.
50        path: PathBuf,
51        /// Underlying JSON syntax or data-model error.
52        #[source]
53        source: serde_json::Error,
54    },
55
56    /// A field definition used an empty or whitespace-only name.
57    #[error("state template field at index {index} has an empty name")]
58    EmptyFieldName {
59        /// Zero-based position of the invalid field in template order.
60        index: usize,
61    },
62
63    /// Two field definitions used the same name.
64    #[error("state template declares duplicate field `{field}`")]
65    DuplicateField {
66        /// Repeated field name.
67        field: String,
68    },
69
70    /// A field definition used an empty or whitespace-only codec type tag.
71    #[error("state template field `{field}` has an empty type tag")]
72    EmptyTypeTag {
73        /// Name of the field with the invalid type tag.
74        field: String,
75    },
76
77    /// An operation addressed a key that is absent from the template layout.
78    #[error("state template does not declare field `{field}`")]
79    UnknownField {
80        /// Requested field name.
81        field: String,
82    },
83
84    /// An operation required a payload from a declared but currently empty
85    /// field.
86    #[error("state field `{field}` does not contain a payload")]
87    MissingValue {
88        /// Declared field whose slot is empty.
89        field: String,
90    },
91
92    /// A typed operation requested a different Rust type from the stored one.
93    #[error("state field `{field}` contains `{actual}`, but the operation requested `{expected}`")]
94    TypeMismatch {
95        /// Field on which the typed operation was attempted.
96        field: String,
97        /// Rust type requested by the caller.
98        expected: &'static str,
99        /// Rust type currently stored in the field.
100        actual: &'static str,
101    },
102
103    /// A reconstructed state supplied a slot count incompatible with its
104    /// template layout.
105    ///
106    /// Normal state creation allocates the correct number of slots. This
107    /// variant primarily protects deserialization and future storage backends
108    /// from constructing structurally invalid states.
109    #[error("state payload has {actual} field slots, but its template declares {expected}")]
110    FieldCountMismatch {
111        /// Number of fields declared by the template.
112        expected: usize,
113        /// Number of payload slots supplied for the state.
114        actual: usize,
115    },
116}