Skip to main content

khive_fold/
error.rs

1//! Error types for khive-fold
2
3use thiserror::Error;
4use uuid::Uuid;
5
6/// Result type for fold operations
7pub type FoldResult<T> = std::result::Result<T, FoldError>;
8
9/// Errors that can occur in fold operations
10#[derive(Error, Debug)]
11pub enum FoldError {
12    /// Entry not found
13    #[error("Entry {0} not found")]
14    EntryNotFound(Uuid),
15
16    /// Invalid entry type for this fold
17    #[error("Invalid entry type: expected {expected}, got {actual}")]
18    InvalidEntryType {
19        /// The expected entry type name
20        expected: String,
21        /// The actual entry type that was provided
22        actual: String,
23    },
24
25    /// Fold context error
26    #[error("Context error: {0}")]
27    Context(String),
28
29    /// Serialization error
30    #[error("Serialization error: {0}")]
31    Serialization(#[from] serde_json::Error),
32
33    /// Storage error
34    #[error("Storage error: {0}")]
35    Storage(String),
36
37    /// Internal lock poisoned (concurrent panic)
38    #[error("Internal lock poisoned: {0}")]
39    LockPoisoned(String),
40
41    /// Invalid input to a cognitive primitive (Anchor, Selector).
42    #[error("invalid input: {0}")]
43    InvalidInput(String),
44
45    /// Budget exhausted during selection.
46    #[error("budget exhausted: needed {needed}, have {budget}")]
47    BudgetExhausted {
48        /// Budget needed.
49        needed: usize,
50        /// Budget available.
51        budget: usize,
52    },
53
54    /// Anchor not found during graph traversal.
55    #[error("anchor not found: {0}")]
56    AnchorNotFound(String),
57
58    /// Required component not configured.
59    #[error("required component not configured: {0}")]
60    ComponentMissing(String),
61
62    /// Checkpoint integrity check failed: stored hash does not match recomputed hash.
63    #[error("checkpoint integrity mismatch for '{id}': stored {stored}, computed {computed}")]
64    IntegrityMismatch {
65        /// Checkpoint id that failed verification.
66        id: String,
67        /// The hash stored in the checkpoint.
68        stored: String,
69        /// The hash recomputed from the loaded state.
70        computed: String,
71    },
72
73    /// A checkpoint with the given id was not found.
74    #[error("checkpoint not found: {0}")]
75    CheckpointNotFound(String),
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn test_entry_not_found_display() {
84        let id = Uuid::new_v4();
85        let err = FoldError::EntryNotFound(id);
86        assert!(err.to_string().contains(&id.to_string()));
87    }
88
89    #[test]
90    fn test_invalid_entry_type_display() {
91        let err = FoldError::InvalidEntryType {
92            expected: "data".into(),
93            actual: "item".into(),
94        };
95        let msg = err.to_string();
96        assert!(msg.contains("data"));
97        assert!(msg.contains("item"));
98    }
99
100    #[test]
101    fn test_context_display() {
102        let err = FoldError::Context("missing budget".into());
103        assert!(err.to_string().contains("missing budget"));
104    }
105
106    #[test]
107    fn test_from_serde_error() {
108        let json_err: serde_json::Error = serde_json::from_str::<i32>("invalid").unwrap_err();
109        let err: FoldError = json_err.into();
110        assert!(matches!(err, FoldError::Serialization(_)));
111    }
112
113    #[test]
114    fn test_budget_exhausted_display() {
115        let err = FoldError::BudgetExhausted {
116            needed: 100,
117            budget: 50,
118        };
119        let msg = err.to_string();
120        assert!(msg.contains("100"));
121        assert!(msg.contains("50"));
122    }
123
124    #[test]
125    fn test_anchor_not_found_display() {
126        let err = FoldError::AnchorNotFound("my-anchor".into());
127        assert!(err.to_string().contains("my-anchor"));
128    }
129}