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
73#[derive(Error, Debug)]
75pub enum Bip39Error {
76 #[error("InvalidWordCount({0})")]
77 InvalidWordCount(usize),
78 #[error("Mnemonic({0})")]
79 Mnemonic(String),
80}
81
82#[derive(Error, Debug)]
84pub enum SignError {
85 #[error("InvalidBase58")]
86 InvalidBase58,
87 #[error("InvalidPubkeyFormat")]
88 InvalidPubkeyFormat,
89 #[error("InvalidKeyLength")]
90 InvalidKeyLength,
91 #[error("SigningFailed({0})")]
92 SigningFailed(String),
93
94 #[error("SignerKeyNotFound")]
95 SignerKeyNotFound,
96
97 #[error("SigningNotRequired")]
98 SigningNotRequiredForKey,
99
100 #[error("JsonParse({0})")]
101 JsonParse(#[source] serde_json::Error),
102}
103
104#[derive(Error, Debug)]
106pub enum VerifyError {
107 #[error("Base58Decode({0})")]
108 Base58Decode(#[from] bs58::decode::Error),
109 #[error("InvalidSigLen({0})")]
110 InvalidSignatureLength(usize),
111 #[error("InvalidPubkeyLen({0})")]
112 InvalidPubkeyLength(usize),
113 #[error("InvalidSigFormat")]
114 InvalidSignatureFormat,
115 #[error("InvalidPubkeyFormat")]
116 InvalidPubkeyFormat,
117 #[error("VerifyFailed")]
118 VerificationFailed,
119}
120
121#[derive(Error, Debug)]
123pub enum KeypairError {
124 #[error("SeedTooShort({0})")]
125 SeedTooShort(usize),
126 #[error("SeedSlice({0})")]
127 SeedSlice(&'static str),
128}
129
130#[derive(Error, Debug)]
132pub enum GenError {
133 #[error("InvalidSeedLength")]
134 InvalidSeedLength,
135 #[error("CryptoError({0})")]
136 CryptoError(String),
137 #[error("DerivationPath({0})")]
138 InvalidDerivationPath(String),
139}
140
141#[derive(Debug, Error)]
143pub enum TransactionParseError {
144 #[error("Base64({0})")]
145 InvalidBase64(String),
146 #[error("Base58({0})")]
147 InvalidBase58(String),
148 #[error("InstructionData({0})")]
149 InvalidInstructionData(String),
150 #[error("PubkeyFormat({0})")]
151 InvalidPubkeyFormat(String),
152 #[error("SigLen({0})")]
153 InvalidSignatureLength(usize),
154 #[error("PubkeyLen({0})")]
155 InvalidPubkeyLength(usize),
156 #[error("SigFormat({0})")]
157 InvalidSignatureFormat(String),
158
159 #[error("BlockhashLen({0})")]
160 InvalidBlockhashLength(usize),
161 #[error("BlockhashFormat({0})")]
162 InvalidBlockhashFormat(String),
163 #[error("Format({0})")]
164 InvalidFormat(String),
165 #[error("Serialization({0})")]
166 Serialization(String),
167}
168
169#[derive(Debug, Error)]
171pub enum DeserializeError {
172 #[error("Deserialize({0})")]
173 Deserialization(String),
174}
175
176#[derive(Debug, Error)]
178pub enum IoError {
179 Io(#[from] io::Error),
180
181 IoWithPath {
183 #[source]
184 source: std::io::Error,
185 path: Option<String>,
186 },
187}
188
189impl fmt::Display for IoError {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 match self {
192 IoError::Io(e) => write!(f, "io({})", e),
193 IoError::IoWithPath { source, path } => {
194 let p = path.as_deref().unwrap_or("stdio");
195 write!(f, "io({}: {})", p, source)
196 }
197 }
198 }
199}
200
201impl IoError {
202 pub fn kind(&self) -> io::ErrorKind {
203 match self {
204 IoError::Io(err) => err.kind(),
205 IoError::IoWithPath { source, .. } => source.kind(),
206 }
207 }
208
209 pub fn with_path(source: io::Error, path: impl Into<String>) -> Self {
210 Self::IoWithPath {
211 source,
212 path: Some(path.into()),
213 }
214 }
215
216 pub fn stdio(source: io::Error) -> Self {
217 Self::IoWithPath { source, path: None }
218 }
219}
220
221impl AsExitCode for IoError {
222 fn as_exit_code(&self) -> i32 {
223 use io::ErrorKind::*;
224 match self.kind() {
225 NotFound => ExitCode::NoInput.as_i32(),
226 InvalidInput => ExitCode::Usage.as_i32(),
227 PermissionDenied | AlreadyExists => ExitCode::IoErr.as_i32(),
228 _ => ExitCode::IoErr.as_i32(),
229 }
230 }
231}
232
233impl AsExitCode for ToolError {
234 fn as_exit_code(&self) -> i32 {
235 match self {
236 ToolError::InvalidInput(_) => ExitCode::Usage.as_i32(),
237 ToolError::Io(err) => err.as_exit_code(),
238 ToolError::FileExists { .. } => ExitCode::IoErr.as_i32(),
239 ToolError::Bip39(_)
240 | ToolError::Base58(_)
241 | ToolError::Sign(_)
242 | ToolError::Keypair(_)
243 | ToolError::Gen(_)
244 | ToolError::Verify(_)
245 | ToolError::Deserialize(_)
246 | ToolError::TransactionParse(_) => ExitCode::DataErr.as_i32(),
247 }
248 }
249}