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 {}