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
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn test_entry_not_found_display() {
69        let id = Uuid::new_v4();
70        let err = FoldError::EntryNotFound(id);
71        assert!(err.to_string().contains(&id.to_string()));
72    }
73
74    #[test]
75    fn test_invalid_entry_type_display() {
76        let err = FoldError::InvalidEntryType {
77            expected: "data".into(),
78            actual: "item".into(),
79        };
80        let msg = err.to_string();
81        assert!(msg.contains("data"));
82        assert!(msg.contains("item"));
83    }
84
85    #[test]
86    fn test_context_display() {
87        let err = FoldError::Context("missing budget".into());
88        assert!(err.to_string().contains("missing budget"));
89    }
90
91    #[test]
92    fn test_from_serde_error() {
93        let json_err: serde_json::Error = serde_json::from_str::<i32>("invalid").unwrap_err();
94        let err: FoldError = json_err.into();
95        assert!(matches!(err, FoldError::Serialization(_)));
96    }
97
98    #[test]
99    fn test_budget_exhausted_display() {
100        let err = FoldError::BudgetExhausted {
101            needed: 100,
102            budget: 50,
103        };
104        let msg = err.to_string();
105        assert!(msg.contains("100"));
106        assert!(msg.contains("50"));
107    }
108
109    #[test]
110    fn test_anchor_not_found_display() {
111        let err = FoldError::AnchorNotFound("my-anchor".into());
112        assert!(err.to_string().contains("my-anchor"));
113    }
114}