use thiserror::Error;
#[derive(Error, Debug)]
pub enum CliError {
#[error("Configuration error: {0}")]
Config(#[from] ConfigError),
#[error("RPC error: {0}")]
Rpc(String),
#[error("Transport error: {0}")]
Transport(String),
#[error("Validation error: {0}")]
Validation(String),
#[error("{message}")]
Developers {
code: String,
message: String,
status: Option<u16>,
retry_after: Option<u64>,
signature: Option<String>,
},
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Crypto error: {0}")]
Crypto(String),
#[error("Upload error: {0}")]
ProgramUpload(String),
#[error("Uploader cleanup error: {0}")]
ProgramCleanup(String),
#[error("Transaction submission error: {0}")]
TransactionSubmission(String),
#[error("{message}")]
TransactionFailed {
message: String,
execution_result: u64,
vm_error: i32,
vm_error_label: String,
user_error_code: u64,
user_error_label: String,
signature: String,
},
#[error("Error already reported to user")]
Reported,
#[error("Nonce management error: {0}")]
NonceManagement(String),
#[error("Transaction verification error: {0}")]
TransactionVerification(String),
#[error(
"Transaction observed via stream but details unavailable (signature: {signature}): {message}"
)]
TransactionDetailsUnavailable {
signature: String,
outcome: Box<thru_client::proto::services::v1::SendAndTrackTxnResponse>,
message: String,
},
#[error("Resume validation error: {0}")]
ResumeValidation(String),
#[error("Resume validation error: {message}")]
ResumeValidationAccount {
message: String,
account: String,
seed: String,
},
#[error("Account not found: {0}")]
AccountNotFound(String),
#[error("Hash mismatch: {0}")]
HashMismatch(String),
#[error("Meta account closed: {0}")]
#[allow(dead_code)]
MetaAccountClosed(String),
#[error("{message}")]
Generic { message: String },
}
#[derive(Error, Debug)]
pub enum ConfigError {
#[error("Invalid config format: {0}")]
InvalidFormat(#[from] serde_yaml::Error),
#[error("Invalid private key: {0}")]
InvalidPrivateKey(String),
#[error("Invalid public key: {0}")]
InvalidPublicKey(String),
#[error("Invalid URL: {0}")]
InvalidUrl(String),
#[error("Failed to create config directory: {0}")]
DirectoryCreation(std::io::Error),
}
impl From<anyhow::Error> for CliError {
fn from(err: anyhow::Error) -> Self {
CliError::Generic {
message: err.to_string(),
}
}
}
impl From<tonic::Status> for CliError {
fn from(status: tonic::Status) -> Self {
CliError::Rpc(status.to_string())
}
}
impl From<tonic::transport::Error> for CliError {
fn from(err: tonic::transport::Error) -> Self {
CliError::Transport(err.to_string())
}
}
impl From<thru_client::ClientError> for CliError {
fn from(err: thru_client::ClientError) -> Self {
match err {
thru_client::ClientError::Rpc(msg) => CliError::Rpc(msg),
thru_client::ClientError::Transport(msg) => CliError::Transport(msg),
thru_client::ClientError::Validation(msg) => CliError::Validation(msg),
thru_client::ClientError::TransactionSubmission(msg) => {
CliError::TransactionSubmission(msg)
}
thru_client::ClientError::TransactionVerification(msg) => {
CliError::TransactionVerification(msg)
}
thru_client::ClientError::TransactionDetailsUnavailable {
signature,
outcome,
source,
} => {
if let Some(exec) = outcome
.execution_result
.as_ref()
.filter(|exec| exec.execution_result != 0 || exec.vm_error != 0)
{
let vm_error_label = crate::utils::format_vm_error(exec.vm_error);
let user_error_label = format!("0x{:X}", exec.user_error_code);
return CliError::TransactionFailed {
message: format!(
"Transaction failed with execution result: {} (VM error: {}, User error: {})",
exec.execution_result, vm_error_label, user_error_label
),
execution_result: exec.execution_result,
vm_error: exec.vm_error,
vm_error_label,
user_error_code: exec.user_error_code,
user_error_label,
signature,
};
}
CliError::TransactionDetailsUnavailable {
signature,
outcome,
message: source.to_string(),
}
}
thru_client::ClientError::StreamLagged(msg) => {
CliError::Rpc(format!("stream dropped for falling behind: {}", msg))
}
thru_client::ClientError::AccountNotFound(msg) => CliError::AccountNotFound(msg),
thru_client::ClientError::Generic { message } => CliError::Generic { message },
}
}
}