icarus_canister/
result.rs

1//! Common result types and error handling for Icarus canisters
2
3use candid::{CandidType, Deserialize};
4use serde::Serialize;
5
6/// Standard result type for Icarus tools
7pub type IcarusResult<T> = Result<T, IcarusError>;
8
9/// Standard error type for Icarus operations
10#[derive(Debug, Clone, Serialize, Deserialize, CandidType)]
11pub enum IcarusError {
12    /// Authentication or authorization error
13    Unauthorized(String),
14    /// Validation error with field and message
15    ValidationError { field: String, message: String },
16    /// Resource not found
17    NotFound(String),
18    /// Resource already exists
19    AlreadyExists(String),
20    /// Storage operation failed
21    StorageError(String),
22    /// Generic error with message
23    Other(String),
24}
25
26impl IcarusError {
27    /// Create an unauthorized error
28    pub fn unauthorized() -> Self {
29        Self::Unauthorized("Unauthorized access".to_string())
30    }
31
32    /// Create a validation error
33    pub fn validation(field: impl Into<String>, message: impl Into<String>) -> Self {
34        Self::ValidationError {
35            field: field.into(),
36            message: message.into(),
37        }
38    }
39
40    /// Create a not found error
41    pub fn not_found(resource: impl Into<String>) -> Self {
42        Self::NotFound(format!("{} not found", resource.into()))
43    }
44
45    /// Create an already exists error
46    pub fn already_exists(resource: impl Into<String>) -> Self {
47        Self::AlreadyExists(format!("{} already exists", resource.into()))
48    }
49
50    /// Create a storage error
51    pub fn storage(message: impl Into<String>) -> Self {
52        Self::StorageError(message.into())
53    }
54
55    /// Convert to a trap message for compatibility
56    pub fn trap(self) -> ! {
57        ic_cdk::trap(self.to_string())
58    }
59}
60
61impl std::fmt::Display for IcarusError {
62    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        match self {
64            Self::Unauthorized(msg) => write!(f, "Unauthorized: {}", msg),
65            Self::ValidationError { field, message } => {
66                write!(f, "Validation error on '{}': {}", field, message)
67            }
68            Self::NotFound(msg) => write!(f, "Not found: {}", msg),
69            Self::AlreadyExists(msg) => write!(f, "Already exists: {}", msg),
70            Self::StorageError(msg) => write!(f, "Storage error: {}", msg),
71            Self::Other(msg) => write!(f, "{}", msg),
72        }
73    }
74}
75
76impl std::error::Error for IcarusError {}
77
78impl From<String> for IcarusError {
79    fn from(s: String) -> Self {
80        Self::Other(s)
81    }
82}
83
84impl From<&str> for IcarusError {
85    fn from(s: &str) -> Self {
86        Self::Other(s.to_string())
87    }
88}
89
90/// Extension trait for Result types to convert to trap
91pub trait TrapExt<T> {
92    /// Unwrap the result or trap with the error message
93    fn unwrap_or_trap(self) -> T;
94}
95
96impl<T> TrapExt<T> for Result<T, IcarusError> {
97    fn unwrap_or_trap(self) -> T {
98        match self {
99            Ok(v) => v,
100            Err(e) => e.trap(),
101        }
102    }
103}