use thiserror::Error;
pub mod unified;
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum Neo3Error {
#[error("Cryptographic error: {0}")]
Crypto(#[from] CryptoError),
#[error("Wallet error: {0}")]
Wallet(#[from] WalletError),
#[error("Network error: {0}")]
Network(#[from] NetworkError),
#[error("Transaction error: {0}")]
Transaction(#[from] TransactionError),
#[error("Contract error: {0}")]
Contract(#[from] ContractError),
#[error("Serialization error: {0}")]
Serialization(#[from] SerializationError),
#[error("Configuration error: {0}")]
Config(String),
#[error("Error: {message}")]
Generic { message: String },
#[error("Unsupported operation: {0}")]
UnsupportedOperation(String),
}
#[derive(Error, Debug)]
pub enum CryptoError {
#[error("Invalid private key: {0}")]
InvalidPrivateKey(String),
#[error("Invalid public key: {0}")]
InvalidPublicKey(String),
#[error("Signature verification failed")]
SignatureVerificationFailed,
#[error("Key generation failed: {0}")]
KeyGenerationFailed(String),
#[error("Hash operation failed: {0}")]
HashFailed(String),
#[error("Encryption failed: {0}")]
EncryptionFailed(String),
#[error("Decryption failed: {0}")]
DecryptionFailed(String),
}
#[derive(Error, Debug)]
pub enum WalletError {
#[error("Wallet not found: {0}")]
NotFound(String),
#[error("Invalid password")]
InvalidPassword,
#[error("Account not found: {0}")]
AccountNotFound(String),
#[error("Wallet is locked")]
WalletLocked,
#[error("Backup operation failed: {0}")]
BackupFailed(String),
#[error("Recovery operation failed: {0}")]
RecoveryFailed(String),
#[error("YubiHSM error: {0}")]
YubiHsmError(String),
#[error("Invalid wallet format: {0}")]
InvalidFormat(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
}
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum NetworkError {
#[error("Connection failed: {0}")]
ConnectionFailed(String),
#[error("Request timeout")]
Timeout,
#[error("Invalid response: {0}")]
InvalidResponse(String),
#[error("RPC error: {code} - {message}")]
RpcError { code: i32, message: String },
#[error("Network unreachable: {0}")]
NetworkUnreachable(String),
#[error("Rate limit exceeded")]
RateLimitExceeded,
#[error("HTTP error: {0}")]
Http(#[from] reqwest::Error),
}
#[derive(Error, Debug)]
pub enum TransactionError {
#[error("Invalid transaction: {0}")]
Invalid(String),
#[error("Insufficient funds: required {required}, available {available}")]
InsufficientFunds { required: u64, available: u64 },
#[error("Transaction too large: {size} bytes (max: {max})")]
TooLarge { size: usize, max: usize },
#[error("Invalid signature")]
InvalidSignature,
#[error("Transaction expired")]
Expired,
#[error("Nonce too low: {provided} (expected: {expected})")]
NonceTooLow { provided: u64, expected: u64 },
#[error("Gas limit exceeded: {used} (limit: {limit})")]
GasLimitExceeded { used: u64, limit: u64 },
}
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum ContractError {
#[error("Contract not found: {0}")]
NotFound(String),
#[error("Method not found: {0}")]
MethodNotFound(String),
#[error("Invalid parameters: {0}")]
InvalidParameters(String),
#[error("Execution failed: {0}")]
ExecutionFailed(String),
#[error("Insufficient gas: {0}")]
InsufficientGas(String),
#[error("Contract deployment failed: {0}")]
DeploymentFailed(String),
}
#[derive(Error, Debug)]
pub enum SerializationError {
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("Invalid format: {0}")]
InvalidFormat(String),
#[error("Encoding error: {0}")]
Encoding(String),
#[error("Decoding error: {0}")]
Decoding(String),
}
pub type Neo3Result<T> = Result<T, Neo3Error>;
pub type NeoError = Neo3Error;
impl From<std::io::Error> for Neo3Error {
fn from(err: std::io::Error) -> Self {
Neo3Error::Wallet(WalletError::Io(err))
}
}
impl From<serde_json::Error> for Neo3Error {
fn from(err: serde_json::Error) -> Self {
Neo3Error::Serialization(SerializationError::Json(err))
}
}
pub trait ErrorContext<T> {
fn with_context<F>(self, f: F) -> Neo3Result<T>
where
F: FnOnce() -> String;
}
impl<T, E> ErrorContext<T> for Result<T, E>
where
E: Into<Neo3Error>,
{
fn with_context<F>(self, f: F) -> Neo3Result<T>
where
F: FnOnce() -> String,
{
self.map_err(|e| {
let base_error = e.into();
Neo3Error::Generic { message: format!("{}: {}", f(), base_error) }
})
}
}
#[macro_export]
macro_rules! neo3_error {
($msg:expr) => {
Neo3Error::Generic {
message: $msg.to_string(),
}
};
($fmt:expr, $($arg:tt)*) => {
Neo3Error::Generic {
message: format!($fmt, $($arg)*),
}
};
}
#[macro_export]
macro_rules! ensure {
($cond:expr, $msg:expr) => {
if !$cond {
return Err(neo3_error!($msg));
}
};
($cond:expr, $fmt:expr, $($arg:tt)*) => {
if !$cond {
return Err(neo3_error!($fmt, $($arg)*));
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_context() {
let result: Result<(), std::io::Error> =
Err(std::io::Error::new(std::io::ErrorKind::NotFound, "file not found"));
let with_context = result.with_context(|| "Failed to read configuration file".to_string());
assert!(with_context.is_err());
let error_msg = with_context.unwrap_err().to_string();
assert!(error_msg.contains("Failed to read configuration file"));
assert!(error_msg.contains("file not found"));
}
#[test]
fn test_error_macros() {
let error = neo3_error!("Something went wrong");
assert_eq!(error.to_string(), "Error: Something went wrong");
let error = neo3_error!("Value {} is invalid", 42);
assert_eq!(error.to_string(), "Error: Value 42 is invalid");
}
}