1use std::fmt;
2use std::io;
3use thiserror::Error;
4
5pub type Result<T, E = ToolError> = std::result::Result<T, E>;
7
8pub trait AsExitCode {
10 fn as_exit_code(&self) -> i32;
11}
12
13#[repr(i32)]
14#[derive(Copy, Clone, Debug, Eq, PartialEq)]
15pub enum ExitCode {
16 Usage = 64,
17 DataErr = 65,
18 NoInput = 66,
19 Software = 70,
20 IoErr = 74,
21}
22
23impl ExitCode {
24 pub const fn as_i32(self) -> i32 {
25 self as i32
26 }
27}
28
29#[derive(Debug, Error)]
38pub enum ToolError {
39 #[error("bip39: {0}")]
40 Bip39(#[from] Bip39Error),
41
42 #[error("base58: {0}")]
43 Base58(#[from] bs58::decode::Error),
44
45 #[error("sign: {0}")]
46 Sign(#[from] SignError),
47
48 #[error("keypair: {0}")]
49 Keypair(#[from] KeypairError),
50
51 #[error("gen: {0}")]
52 Gen(#[from] GenError),
53
54 #[error("tx_parse: {0}")]
55 TransactionParse(#[from] TransactionParseError),
56
57 #[error("deserialize: {0}")]
58 Deserialize(#[from] DeserializeError),
59
60 #[error("io: {0}")]
61 Io(#[from] IoError),
62
63 #[error("verify: {0}")]
64 Verify(#[from] VerifyError),
65
66 #[error("file_exists: {path}")]
67 FileExists { path: String },
68
69 #[error("{0}")]
70 InvalidInput(String),
71
72 #[error("configuration: {0}")]
73 ConfigurationError(String),
74}
75
76#[derive(Error, Debug)]
78pub enum Bip39Error {
79 #[error("InvalidWordCount({0})")]
80 InvalidWordCount(usize),
81 #[error("Mnemonic({0})")]
82 Mnemonic(String),
83}
84
85#[derive(Error, Debug)]
87pub enum SignError {
88 #[error("InvalidBase58")]
89 InvalidBase58,
90 #[error("InvalidPubkeyFormat")]
91 InvalidPubkeyFormat,
92 #[error("InvalidKeyLength")]
93 InvalidKeyLength,
94 #[error("SigningFailed({0})")]
95 SigningFailed(String),
96
97 #[error("SignerKeyNotFound")]
98 SignerKeyNotFound,
99
100 #[error("SigningNotRequired")]
101 SigningNotRequiredForKey,
102
103 #[error("JsonParse({0})")]
104 JsonParse(#[source] serde_json::Error),
105}
106
107#[derive(Error, Debug)]
109pub enum VerifyError {
110 #[error("Base58Decode({0})")]
111 Base58Decode(#[from] bs58::decode::Error),
112 #[error("InvalidSigLen({0})")]
113 InvalidSignatureLength(usize),
114 #[error("InvalidPubkeyLen({0})")]
115 InvalidPubkeyLength(usize),
116 #[error("InvalidSigFormat")]
117 InvalidSignatureFormat,
118 #[error("InvalidPubkeyFormat")]
119 InvalidPubkeyFormat,
120 #[error("VerifyFailed")]
121 VerificationFailed,
122}
123
124#[derive(Error, Debug)]
126pub enum KeypairError {
127 #[error("SeedTooShort({0})")]
128 SeedTooShort(usize),
129 #[error("SeedSlice({0})")]
130 SeedSlice(&'static str),
131}
132
133#[derive(Error, Debug)]
135pub enum GenError {
136 #[error("InvalidSeedLength")]
137 InvalidSeedLength,
138 #[error("CryptoError({0})")]
139 CryptoError(String),
140 #[error("DerivationPath({0})")]
141 InvalidDerivationPath(String),
142}
143
144#[derive(Debug, Error)]
146pub enum TransactionParseError {
147 #[error("Base64({0})")]
148 InvalidBase64(String),
149 #[error("Base58({0})")]
150 InvalidBase58(String),
151 #[error("InstructionData({0})")]
152 InvalidInstructionData(String),
153 #[error("PubkeyFormat({0})")]
154 InvalidPubkeyFormat(String),
155 #[error("SigLen({0})")]
156 InvalidSignatureLength(usize),
157 #[error("PubkeyLen({0})")]
158 InvalidPubkeyLength(usize),
159 #[error("SigFormat({0})")]
160 InvalidSignatureFormat(String),
161
162 #[error("BlockhashLen({0})")]
163 InvalidBlockhashLength(usize),
164 #[error("BlockhashFormat({0})")]
165 InvalidBlockhashFormat(String),
166 #[error("Format({0})")]
167 InvalidFormat(String),
168 #[error("Serialization({0})")]
169 Serialization(String),
170}
171
172#[derive(Debug, Error)]
174pub enum DeserializeError {
175 #[error("Deserialize({0})")]
176 Deserialization(String),
177}
178
179#[derive(Debug, Error)]
181pub enum IoError {
182 Io(#[from] io::Error),
183
184 IoWithPath {
186 #[source]
187 source: std::io::Error,
188 path: Option<String>,
189 },
190}
191
192impl fmt::Display for IoError {
193 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194 match self {
195 IoError::Io(e) => write!(f, "io({})", e),
196 IoError::IoWithPath { source, path } => {
197 let p = path.as_deref().unwrap_or("stdio");
198 write!(f, "io({}: {})", p, source)
199 }
200 }
201 }
202}
203
204impl IoError {
205 pub fn kind(&self) -> io::ErrorKind {
206 match self {
207 IoError::Io(err) => err.kind(),
208 IoError::IoWithPath { source, .. } => source.kind(),
209 }
210 }
211
212 pub fn with_path(source: io::Error, path: impl Into<String>) -> Self {
213 Self::IoWithPath {
214 source,
215 path: Some(path.into()),
216 }
217 }
218
219 pub fn stdio(source: io::Error) -> Self {
220 Self::IoWithPath { source, path: None }
221 }
222}
223
224impl AsExitCode for IoError {
225 fn as_exit_code(&self) -> i32 {
226 use io::ErrorKind::*;
227 match self.kind() {
228 NotFound => ExitCode::NoInput.as_i32(),
229 InvalidInput => ExitCode::Usage.as_i32(),
230 PermissionDenied | AlreadyExists => ExitCode::IoErr.as_i32(),
231 _ => ExitCode::IoErr.as_i32(),
232 }
233 }
234}
235
236impl AsExitCode for ToolError {
237 fn as_exit_code(&self) -> i32 {
238 match self {
239 ToolError::InvalidInput(_) => ExitCode::Usage.as_i32(),
240 ToolError::ConfigurationError(_) => ExitCode::Software.as_i32(),
241 ToolError::Io(err) => err.as_exit_code(),
242 ToolError::FileExists { .. } => ExitCode::IoErr.as_i32(),
243 ToolError::Bip39(_)
244 | ToolError::Base58(_)
245 | ToolError::Sign(_)
246 | ToolError::Keypair(_)
247 | ToolError::Gen(_)
248 | ToolError::Verify(_)
249 | ToolError::Deserialize(_)
250 | ToolError::TransactionParse(_) => ExitCode::DataErr.as_i32(),
251 }
252 }
253}