Skip to main content

appcore_contracts/
error.rs

1// =============================================================================
2//        #######
3//     ###       ###     F: error.rs
4//    ##   ## ##   ##    P: AppCore-Runtime
5//         ## ##
6//                       C: 2026/07/21 23:21:21 by dnettoRaw
7//    ##   ## ##   ##    U: 2026/07/21 23:21:21 by dnettoRaw
8//      ###########      S: 1.0.1-rc.8
9// =============================================================================
10
11//! Errors returned while constructing or decoding contracts.
12
13use std::fmt::{Display, Formatter};
14
15/// Result type used by `AppCore` contracts.
16pub type ContractResult<T> = Result<T, ContractError>;
17
18/// Validation error for a versioned contract.
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub enum ContractError {
21    /// A required value was empty.
22    Empty {
23        /// Name of the invalid field.
24        field: &'static str,
25    },
26    /// A value exceeded its documented maximum length.
27    TooLong {
28        /// Name of the invalid field.
29        field: &'static str,
30        /// Maximum accepted UTF-8 byte length.
31        max_bytes: usize,
32    },
33    /// An identifier contains unsupported characters or delimiters.
34    InvalidIdentifier {
35        /// Name of the invalid identifier field.
36        field: &'static str,
37    },
38    /// A field combination is not valid.
39    InvalidValue {
40        /// Name of the invalid field.
41        field: &'static str,
42        /// Stable, non-sensitive validation reason.
43        reason: &'static str,
44    },
45    /// A collection contains the same logical key more than once.
46    Duplicate {
47        /// Name of the collection field.
48        field: &'static str,
49        /// Repeated non-sensitive identifier.
50        value: String,
51    },
52    /// A manifest attempted to store a secret instead of a secret reference.
53    SecretValue {
54        /// Field that must be replaced by a secret reference.
55        field: String,
56    },
57    /// An application manifest attempted to store an installation-local path.
58    LocalPath {
59        /// Application-owned field that attempted to carry a path.
60        field: String,
61    },
62}
63
64impl Display for ContractError {
65    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
66        match self {
67            Self::Empty { field } => write!(formatter, "{field} must not be empty"),
68            Self::TooLong { field, max_bytes } => {
69                write!(formatter, "{field} must not exceed {max_bytes} bytes")
70            }
71            Self::InvalidIdentifier { field } => {
72                write!(formatter, "{field} is not a valid distributed identifier")
73            }
74            Self::InvalidValue { field, reason } => write!(formatter, "{field}: {reason}"),
75            Self::Duplicate { field, value } => write!(formatter, "duplicate {field}: {value}"),
76            Self::SecretValue { field } => {
77                write!(formatter, "{field} must use a secret reference")
78            }
79            Self::LocalPath { field } => {
80                write!(formatter, "{field} must be declared by the deployment")
81            }
82        }
83    }
84}
85
86impl std::error::Error for ContractError {}