smolder_core/
error.rs

1use crate::types::DeploymentId;
2use thiserror::Error;
3
4/// Result type alias using the crate's Error type
5pub type Result<T> = std::result::Result<T, Error>;
6
7#[derive(Error, Debug)]
8pub enum Error {
9    // =========================================================================
10    // External errors (with automatic From implementations)
11    // =========================================================================
12    #[error("Database error: {0}")]
13    Database(#[from] sqlx::Error),
14
15    #[error("Serialization error: {0}")]
16    Serialization(#[from] serde_json::Error),
17
18    #[error("Hex decode error: {0}")]
19    HexDecode(#[from] hex::FromHexError),
20
21    // =========================================================================
22    // Entity not found errors
23    // =========================================================================
24    #[error("Network not found: {0}")]
25    NetworkNotFound(String),
26
27    #[error("Contract not found: {0}")]
28    ContractNotFound(String),
29
30    #[error("Deployment not found: {0}")]
31    DeploymentNotFound(String),
32
33    #[error("Deployment not found by ID: {0}")]
34    DeploymentNotFoundById(DeploymentId),
35
36    #[error("Wallet not found: {0}")]
37    WalletNotFound(String),
38
39    #[error("Function '{function}' not found in contract '{contract}'")]
40    FunctionNotFound { contract: String, function: String },
41
42    #[error("Artifact not found: {0}")]
43    ArtifactNotFound(String),
44
45    // =========================================================================
46    // ABI errors
47    // =========================================================================
48    #[error("ABI parse error: {0}")]
49    AbiParse(String),
50
51    #[error("ABI encoding error: {0}")]
52    AbiEncode(String),
53
54    #[error("ABI decoding error: {0}")]
55    AbiDecode(String),
56
57    // =========================================================================
58    // RPC/Chain errors
59    // =========================================================================
60    #[error("RPC error: {0}")]
61    Rpc(String),
62
63    #[error("RPC error on chain {chain_id}: {message}")]
64    RpcWithChain { chain_id: u64, message: String },
65
66    #[error("Transaction failed: {0}")]
67    TransactionFailed(String),
68
69    #[error("Transaction reverted: {reason}")]
70    TransactionReverted {
71        reason: String,
72        tx_hash: Option<String>,
73    },
74
75    // =========================================================================
76    // Validation errors
77    // =========================================================================
78    #[error("Invalid parameter '{name}': {reason}")]
79    InvalidParameter { name: String, reason: String },
80
81    #[error("Validation error: {0}")]
82    Validation(String),
83
84    // =========================================================================
85    // Cryptography errors
86    // =========================================================================
87    #[error("Keyring error: {0}")]
88    Keyring(String),
89
90    #[error("Encryption error: {0}")]
91    Encryption(String),
92
93    #[error("Decryption error: {0}")]
94    Decryption(String),
95
96    // =========================================================================
97    // IO errors
98    // =========================================================================
99    #[error("File not found: {0}")]
100    FileNotFound(String),
101
102    #[error("IO error: {0}")]
103    Io(String),
104
105    // =========================================================================
106    // Configuration errors
107    // =========================================================================
108    #[error("Configuration error: {0}")]
109    Config(String),
110
111    #[error("Environment variable '{name}' not set")]
112    EnvVarNotSet { name: String },
113}
114
115impl Error {
116    /// Returns true if this is a "not found" type error
117    pub fn is_not_found(&self) -> bool {
118        matches!(
119            self,
120            Error::NetworkNotFound(_)
121                | Error::ContractNotFound(_)
122                | Error::DeploymentNotFound(_)
123                | Error::DeploymentNotFoundById(_)
124                | Error::WalletNotFound(_)
125                | Error::FunctionNotFound { .. }
126                | Error::ArtifactNotFound(_)
127                | Error::FileNotFound(_)
128        )
129    }
130
131    /// Returns true if this is a database error
132    pub fn is_database(&self) -> bool {
133        matches!(self, Error::Database(_))
134    }
135
136    /// Returns true if this is a validation error
137    pub fn is_validation(&self) -> bool {
138        matches!(self, Error::InvalidParameter { .. } | Error::Validation(_))
139    }
140
141    /// Returns an error code suitable for API responses
142    pub fn code(&self) -> &'static str {
143        match self {
144            Error::Database(_) => "DATABASE_ERROR",
145            Error::Serialization(_) => "SERIALIZATION_ERROR",
146            Error::HexDecode(_) => "HEX_DECODE_ERROR",
147            Error::NetworkNotFound(_) => "NETWORK_NOT_FOUND",
148            Error::ContractNotFound(_) => "CONTRACT_NOT_FOUND",
149            Error::DeploymentNotFound(_) | Error::DeploymentNotFoundById(_) => {
150                "DEPLOYMENT_NOT_FOUND"
151            }
152            Error::WalletNotFound(_) => "WALLET_NOT_FOUND",
153            Error::FunctionNotFound { .. } => "FUNCTION_NOT_FOUND",
154            Error::ArtifactNotFound(_) => "ARTIFACT_NOT_FOUND",
155            Error::AbiParse(_) => "ABI_PARSE_ERROR",
156            Error::AbiEncode(_) => "ABI_ENCODE_ERROR",
157            Error::AbiDecode(_) => "ABI_DECODE_ERROR",
158            Error::Rpc(_) | Error::RpcWithChain { .. } => "RPC_ERROR",
159            Error::TransactionFailed(_) => "TRANSACTION_FAILED",
160            Error::TransactionReverted { .. } => "TRANSACTION_REVERTED",
161            Error::InvalidParameter { .. } => "INVALID_PARAMETER",
162            Error::Validation(_) => "VALIDATION_ERROR",
163            Error::Keyring(_) => "KEYRING_ERROR",
164            Error::Encryption(_) => "ENCRYPTION_ERROR",
165            Error::Decryption(_) => "DECRYPTION_ERROR",
166            Error::FileNotFound(_) => "FILE_NOT_FOUND",
167            Error::Io(_) => "IO_ERROR",
168            Error::Config(_) => "CONFIG_ERROR",
169            Error::EnvVarNotSet { .. } => "ENV_VAR_NOT_SET",
170        }
171    }
172}
173
174// Convenience constructors for common error patterns
175impl Error {
176    pub fn function_not_found(contract: impl Into<String>, function: impl Into<String>) -> Self {
177        Error::FunctionNotFound {
178            contract: contract.into(),
179            function: function.into(),
180        }
181    }
182
183    pub fn invalid_param(name: impl Into<String>, reason: impl Into<String>) -> Self {
184        Error::InvalidParameter {
185            name: name.into(),
186            reason: reason.into(),
187        }
188    }
189
190    pub fn rpc_error(chain_id: u64, message: impl Into<String>) -> Self {
191        Error::RpcWithChain {
192            chain_id,
193            message: message.into(),
194        }
195    }
196}