1use core::fmt;
4
5use paralock::{ParalockErrorCode, Swap};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum Exit {
17 Ok,
19 Failed,
21 Usage,
23 SwapFailed,
25}
26
27impl Exit {
28 pub const fn code(self) -> u8 {
30 match self {
31 Self::Ok => 0,
32 Self::Failed => 1,
33 Self::Usage => 2,
34 Self::SwapFailed => 3,
35 }
36 }
37}
38
39#[derive(Debug)]
42pub struct CliError {
43 pub exit: Exit,
45 pub message: String,
47 pub hint: Option<String>,
49 pub code: Option<ParalockErrorCode>,
51 pub swap: Option<Box<Swap>>,
53}
54
55pub type CliResult<T> = Result<T, CliError>;
57
58impl CliError {
59 pub fn failed(message: impl Into<String>) -> Self {
61 Self { exit: Exit::Failed, message: message.into(), hint: None, code: None, swap: None }
62 }
63
64 pub fn usage(message: impl Into<String>) -> Self {
66 Self { exit: Exit::Usage, ..Self::failed(message) }
67 }
68
69 #[must_use]
71 pub fn hint(mut self, hint: impl Into<String>) -> Self {
72 self.hint = Some(hint.into());
73 self
74 }
75
76 #[must_use]
78 pub fn context(mut self, what: impl fmt::Display) -> Self {
79 self.message = format!("{what}: {}", self.message);
80 self
81 }
82}
83
84impl fmt::Display for CliError {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 f.write_str(&self.message)
87 }
88}
89
90impl std::error::Error for CliError {}
91
92impl From<paralock::Error> for CliError {
93 fn from(e: paralock::Error) -> Self {
94 let code = e.code();
95 let swap = e.failed_swap().cloned().map(Box::new);
96 let exit = if swap.is_some() { Exit::SwapFailed } else { Exit::Failed };
97 let hint = match code {
98 ParalockErrorCode::Unavailable => {
99 Some("a node, the price feed or the maker is unreachable: `paralock doctor` checks each of them")
100 }
101 ParalockErrorCode::Busy => Some("the maker is busy or rate limiting you; try again in a moment"),
102 ParalockErrorCode::QuoteExpired => Some("ask for a new quote"),
103 ParalockErrorCode::InsufficientLiquidity => {
104 Some("the maker lacks the stablecoins for this amount; try a smaller one")
105 }
106 _ => None,
107 };
108 Self { exit, message: e.to_string(), hint: hint.map(str::to_owned), code: Some(code), swap }
109 }
110}
111
112impl From<paralock::StoreError> for CliError {
113 fn from(e: paralock::StoreError) -> Self {
114 let locked = matches!(e, paralock::StoreError::Locked(_));
115 let error = Self::failed(format!("swap store: {e}"));
116 if locked { error.hint("another paralock process uses this store; wait for it to finish") } else { error }
117 }
118}
119
120impl From<std::io::Error> for CliError {
121 fn from(e: std::io::Error) -> Self {
122 Self::failed(e.to_string())
123 }
124}