use core::fmt;
use paralock::{ParalockErrorCode, Swap};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Exit {
Ok,
Failed,
Usage,
SwapFailed,
}
impl Exit {
pub const fn code(self) -> u8 {
match self {
Self::Ok => 0,
Self::Failed => 1,
Self::Usage => 2,
Self::SwapFailed => 3,
}
}
}
#[derive(Debug)]
pub struct CliError {
pub exit: Exit,
pub message: String,
pub hint: Option<String>,
pub code: Option<ParalockErrorCode>,
pub swap: Option<Box<Swap>>,
}
pub type CliResult<T> = Result<T, CliError>;
impl CliError {
pub fn failed(message: impl Into<String>) -> Self {
Self { exit: Exit::Failed, message: message.into(), hint: None, code: None, swap: None }
}
pub fn usage(message: impl Into<String>) -> Self {
Self { exit: Exit::Usage, ..Self::failed(message) }
}
#[must_use]
pub fn hint(mut self, hint: impl Into<String>) -> Self {
self.hint = Some(hint.into());
self
}
#[must_use]
pub fn context(mut self, what: impl fmt::Display) -> Self {
self.message = format!("{what}: {}", self.message);
self
}
}
impl fmt::Display for CliError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.message)
}
}
impl std::error::Error for CliError {}
impl From<paralock::Error> for CliError {
fn from(e: paralock::Error) -> Self {
let code = e.code();
let swap = e.failed_swap().cloned().map(Box::new);
let exit = if swap.is_some() { Exit::SwapFailed } else { Exit::Failed };
let hint = match code {
ParalockErrorCode::Unavailable => {
Some("a node, the price feed or the maker is unreachable: `paralock doctor` checks each of them")
}
ParalockErrorCode::Busy => Some("the maker is busy or rate limiting you; try again in a moment"),
ParalockErrorCode::QuoteExpired => Some("ask for a new quote"),
ParalockErrorCode::InsufficientLiquidity => {
Some("the maker lacks the stablecoins for this amount; try a smaller one")
}
_ => None,
};
Self { exit, message: e.to_string(), hint: hint.map(str::to_owned), code: Some(code), swap }
}
}
impl From<paralock::StoreError> for CliError {
fn from(e: paralock::StoreError) -> Self {
let locked = matches!(e, paralock::StoreError::Locked(_));
let error = Self::failed(format!("swap store: {e}"));
if locked { error.hint("another paralock process uses this store; wait for it to finish") } else { error }
}
}
impl From<std::io::Error> for CliError {
fn from(e: std::io::Error) -> Self {
Self::failed(e.to_string())
}
}