Skip to content

Commit 7c6c843

Browse files
committed
leave error enums outside cli untouched
1 parent 924d479 commit 7c6c843

File tree

30 files changed

+438
-142
lines changed

30 files changed

+438
-142
lines changed

catalyst-toolbox/src/archive.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use chain_core::{packer::Codec, property::DeserializeFromSlice};
33
use chain_impl_mockchain::{
44
block::Block, chaintypes::HeaderId, fragment::Fragment, transaction::InputEnum,
55
};
6-
use color_eyre::{eyre::bail, Report};
76
use jormungandr_lib::interfaces::{AccountIdentifier, Address};
87

98
use serde::Serialize;
@@ -12,6 +11,21 @@ use std::{collections::HashMap, path::Path};
1211

1312
const MAIN_TAG: &str = "HEAD";
1413

14+
#[derive(Debug, thiserror::Error)]
15+
pub enum Error {
16+
#[error(transparent)]
17+
Storage(#[from] chain_storage::Error),
18+
19+
#[error(transparent)]
20+
Io(#[from] std::io::Error),
21+
22+
#[error(transparent)]
23+
Csv(#[from] csv::Error),
24+
25+
#[error("Only accounts inputs are supported not Utxos")]
26+
UnhandledInput,
27+
}
28+
1529
#[derive(Serialize)]
1630
struct Vote {
1731
fragment_id: String,
@@ -22,10 +36,7 @@ struct Vote {
2236
raw_fragment: String,
2337
}
2438

25-
pub fn generate_archive_files(
26-
jormungandr_database: &Path,
27-
output_dir: &Path,
28-
) -> Result<(), Report> {
39+
pub fn generate_archive_files(jormungandr_database: &Path, output_dir: &Path) -> Result<(), Error> {
2940
let db = chain_storage::BlockStore::file(
3041
jormungandr_database,
3142
HeaderId::zero_hash()
@@ -56,7 +67,7 @@ pub fn generate_archive_files(
5667
AccountIdentifier::from(account_id)
5768
.into_address(Discrimination::Production, "ca")
5869
} else {
59-
bail!("unhandled input")
70+
return Err(Error::UnhandledInput);
6071
};
6172
let certificate = tx.as_slice().payload().into_payload();
6273

catalyst-toolbox/src/bin/cli/archive/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub struct Archive {
1616

1717
impl Archive {
1818
pub fn exec(self) -> Result<(), Report> {
19-
generate_archive_files(&self.jormungandr_database, &self.output_dir)
19+
generate_archive_files(&self.jormungandr_database, &self.output_dir)?;
20+
Ok(())
2021
}
2122
}

catalyst-toolbox/src/bin/cli/kedqr/decode/payload.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,5 @@ pub fn secret_from_payload(
5454
// use parsed pin from args
5555
let pwd = pin.password;
5656
// generate qrcode with key and parsed pin
57-
decode(payload_str, &pwd)
57+
Ok(decode(payload_str, &pwd)?)
5858
}

catalyst-toolbox/src/bin/cli/stats/archive.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,6 @@ pub struct BatchSizeByCaster {
8787

8888
impl BatchSizeByCaster {
8989
pub fn exec(&self, archiver: ArchiveStats) -> Result<BTreeMap<String, usize>, Report> {
90-
archiver.max_batch_size_per_caster(self.slots_in_epoch)
90+
Ok(archiver.max_batch_size_per_caster(self.slots_in_epoch)?)
9191
}
9292
}

catalyst-toolbox/src/ideascale/fetch.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
use crate::ideascale::models::de::{Fund, Funnel, Proposal, Stage};
22

3-
use color_eyre::Report;
43
use once_cell::sync::Lazy;
54
use serde::de::DeserializeOwned;
65
use url::Url;
76

87
use std::collections::HashMap;
98
use std::convert::TryInto;
109

10+
#[derive(thiserror::Error, Debug)]
11+
pub enum Error {
12+
#[error(transparent)]
13+
RequestError(#[from] reqwest::Error),
14+
15+
#[error("Could not get value from json, missing attribute {attribute_name}")]
16+
MissingAttribute { attribute_name: &'static str },
17+
}
18+
1119
pub type Scores = HashMap<u32, f32>;
1220
pub type Sponsors = HashMap<String, String>;
1321

@@ -19,30 +27,31 @@ static BASE_IDEASCALE_URL: Lazy<url::Url> = Lazy::new(|| {
1927

2028
static CLIENT: Lazy<reqwest::Client> = Lazy::new(reqwest::Client::new);
2129

22-
async fn request_data<T: DeserializeOwned>(api_token: String, url: Url) -> Result<T, Report> {
23-
Ok(CLIENT
30+
async fn request_data<T: DeserializeOwned>(api_token: String, url: Url) -> Result<T, Error> {
31+
CLIENT
2432
.get(url)
2533
.header("api_token", api_token)
2634
.send()
2735
.await?
2836
.json()
29-
.await?)
37+
.await
38+
.map_err(Error::RequestError)
3039
}
3140

32-
pub async fn get_funds_data(api_token: String) -> Result<Vec<Fund>, Report> {
41+
pub async fn get_funds_data(api_token: String) -> Result<Vec<Fund>, Error> {
3342
request_data(
3443
api_token,
3544
BASE_IDEASCALE_URL.join("campaigns/groups").unwrap(),
3645
)
3746
.await
3847
}
3948

40-
pub async fn get_stages(api_token: String) -> Result<Vec<Stage>, Report> {
49+
pub async fn get_stages(api_token: String) -> Result<Vec<Stage>, Error> {
4150
request_data(api_token, BASE_IDEASCALE_URL.join("stages").unwrap()).await
4251
}
4352

4453
/// we test token by running lightweight query and observe response code
45-
pub async fn is_token_valid(api_token: String) -> Result<bool, Report> {
54+
pub async fn is_token_valid(api_token: String) -> Result<bool, Error> {
4655
let url = BASE_IDEASCALE_URL.join("profile/avatars").unwrap();
4756

4857
let response = CLIENT
@@ -57,7 +66,7 @@ pub async fn is_token_valid(api_token: String) -> Result<bool, Report> {
5766
pub async fn get_proposals_data(
5867
challenge_id: u32,
5968
api_token: String,
60-
) -> Result<Vec<Proposal>, Report> {
69+
) -> Result<Vec<Proposal>, Error> {
6170
request_data(
6271
api_token,
6372
BASE_IDEASCALE_URL
@@ -70,7 +79,7 @@ pub async fn get_proposals_data(
7079
.await
7180
}
7281

73-
pub async fn get_funnels_data_for_fund(api_token: String) -> Result<Vec<Funnel>, Report> {
82+
pub async fn get_funnels_data_for_fund(api_token: String) -> Result<Vec<Funnel>, Error> {
7483
let challenges: Vec<Funnel> =
7584
request_data(api_token, BASE_IDEASCALE_URL.join("funnels").unwrap()).await?;
7685
Ok(challenges)

catalyst-toolbox/src/ideascale/mod.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ use crate::ideascale::models::de::{clean_str, Challenge, Fund, Funnel, Proposal,
55

66
use std::collections::{HashMap, HashSet};
77

8-
use color_eyre::eyre::bail;
9-
use color_eyre::Report;
108
use regex::Regex;
119

1210
pub use crate::ideascale::fetch::{Scores, Sponsors};
@@ -16,6 +14,24 @@ pub use crate::ideascale::models::custom_fields::CustomFieldTags;
1614
// should not change
1715
const PROCESS_IMPROVEMENTS_ID: u32 = 7666;
1816

17+
#[derive(thiserror::Error, Debug)]
18+
pub enum Error {
19+
#[error(transparent)]
20+
Fetch(#[from] fetch::Error),
21+
22+
#[error(transparent)]
23+
Join(#[from] tokio::task::JoinError),
24+
25+
#[error(transparent)]
26+
Serde(#[from] serde_json::Error),
27+
28+
#[error(transparent)]
29+
Regex(#[from] regex::Error),
30+
31+
#[error("invalid token")]
32+
InvalidToken,
33+
}
34+
1935
#[derive(Debug)]
2036
pub struct IdeaScaleData {
2137
pub funnels: HashMap<u32, Funnel>,
@@ -30,10 +46,11 @@ pub async fn fetch_all(
3046
stages_filters: &[&str],
3147
excluded_proposals: &HashSet<u32>,
3248
api_token: String,
33-
) -> Result<IdeaScaleData, Report> {
49+
) -> Result<IdeaScaleData, Error> {
3450
if !fetch::is_token_valid(api_token.clone()).await? {
35-
bail!("invalid token");
51+
return Err(Error::InvalidToken);
3652
}
53+
3754
let funnels_task = tokio::spawn(fetch::get_funnels_data_for_fund(api_token.clone()));
3855
let funds_task = tokio::spawn(fetch::get_funds_data(api_token.clone()));
3956
let funnels = funnels_task

catalyst-toolbox/src/kedqr/img.rs

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,49 @@
11
use super::payload;
2-
use chain_crypto::{Ed25519Extended, SecretKey};
3-
use color_eyre::Report;
4-
use image::{DynamicImage, ImageBuffer, Luma};
2+
use chain_crypto::{Ed25519Extended, SecretKey, SecretKeyError};
3+
use image::{DynamicImage, ImageBuffer, ImageError, Luma};
54
use qrcode::{
65
render::{svg, unicode},
76
EcLevel, QrCode,
87
};
98
use std::fmt;
109
use std::fs::File;
11-
use std::io::prelude::*;
10+
use std::io::{self, prelude::*};
1211
use std::path::Path;
12+
use symmetric_cipher::Error as SymmetricCipherError;
13+
use thiserror::Error;
1314

1415
pub struct KeyQrCode {
1516
inner: QrCode,
1617
}
1718

19+
#[derive(Error, Debug)]
20+
pub enum KeyQrCodeError {
21+
#[error("encryption-decryption protocol error")]
22+
SymmetricCipher(#[from] SymmetricCipherError),
23+
#[error("io error")]
24+
Io(#[from] io::Error),
25+
#[error("invalid secret key")]
26+
SecretKey(#[from] SecretKeyError),
27+
#[error("couldn't decode QR code")]
28+
QrDecodeError(#[from] QrDecodeError),
29+
#[error("failed to decode hex")]
30+
HexDecodeError(#[from] hex::FromHexError),
31+
#[error("failed to decode hex")]
32+
QrCodeHashError(#[from] super::payload::Error),
33+
#[error(transparent)]
34+
Image(#[from] ImageError),
35+
}
36+
37+
#[derive(Error, Debug)]
38+
pub enum QrDecodeError {
39+
#[error("couldn't decode QR code")]
40+
DecodeError(#[from] quircs::DecodeError),
41+
#[error("couldn't extract QR code")]
42+
ExtractError(#[from] quircs::ExtractError),
43+
#[error("QR code payload is not valid uf8")]
44+
NonUtf8Payload,
45+
}
46+
1847
impl KeyQrCode {
1948
pub fn generate(key: SecretKey<Ed25519Extended>, password: &[u8]) -> Self {
2049
let enc_hex = payload::generate(key, password);
@@ -23,7 +52,7 @@ impl KeyQrCode {
2352
KeyQrCode { inner }
2453
}
2554

26-
pub fn write_svg(&self, path: impl AsRef<Path>) -> Result<(), Report> {
55+
pub fn write_svg(&self, path: impl AsRef<Path>) -> Result<(), KeyQrCodeError> {
2756
let mut out = File::create(path)?;
2857
let svg_file = self
2958
.inner
@@ -46,20 +75,23 @@ impl KeyQrCode {
4675
pub fn decode(
4776
img: DynamicImage,
4877
password: &[u8],
49-
) -> Result<Vec<SecretKey<Ed25519Extended>>, Report> {
78+
) -> Result<Vec<SecretKey<Ed25519Extended>>, KeyQrCodeError> {
5079
let mut decoder = quircs::Quirc::default();
5180

5281
let img = img.into_luma8();
5382

5483
let codes = decoder.identify(img.width() as usize, img.height() as usize, &img);
5584

5685
codes
57-
.map(|code| -> Result<_, Report> {
58-
let decoded = code?.decode()?;
86+
.map(|code| -> Result<_, KeyQrCodeError> {
87+
let decoded = code
88+
.map_err(QrDecodeError::ExtractError)
89+
.and_then(|c| c.decode().map_err(QrDecodeError::DecodeError))?;
5990

6091
// TODO: I actually don't know if this can fail
61-
let h = std::str::from_utf8(&decoded.payload)?;
62-
payload::decode(h, password)
92+
let h = std::str::from_utf8(&decoded.payload)
93+
.map_err(|_| QrDecodeError::NonUtf8Payload)?;
94+
payload::decode(h, password).map_err(Into::into)
6395
})
6496
.collect()
6597
}

catalyst-toolbox/src/kedqr/mod.rs

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
mod img;
22
mod payload;
33

4-
use color_eyre::eyre::{bail, eyre};
5-
use color_eyre::Report;
6-
pub use img::KeyQrCode;
7-
pub use payload::{decode, generate};
4+
pub use img::{KeyQrCode, KeyQrCodeError};
5+
pub use payload::{decode, generate, Error as KeyQrCodePayloadError};
86
use std::path::PathBuf;
97
use std::str::FromStr;
8+
use thiserror::Error;
109

1110
pub const PIN_LENGTH: usize = 4;
1211

@@ -15,18 +14,37 @@ pub struct QrPin {
1514
pub password: [u8; 4],
1615
}
1716

17+
#[derive(Error, Debug)]
18+
pub enum Error {
19+
#[error(transparent)]
20+
BadPin(#[from] BadPinError),
21+
#[error(transparent)]
22+
KeyQrCodeHash(#[from] KeyQrCodePayloadError),
23+
#[error(transparent)]
24+
KeyQrCode(#[from] KeyQrCodeError),
25+
}
26+
27+
#[derive(Error, Debug)]
28+
pub enum BadPinError {
29+
#[error("The PIN must consist of {PIN_LENGTH} digits, found {0}")]
30+
InvalidLength(usize),
31+
#[error("Invalid digit {0}")]
32+
InvalidDigit(char),
33+
#[error("cannot detect file name from path {0:?} in order to read qr pin from it")]
34+
UnableToDetectFileName(PathBuf),
35+
}
36+
1837
impl FromStr for QrPin {
19-
type Err = Report;
38+
type Err = BadPinError;
2039

2140
fn from_str(s: &str) -> Result<Self, Self::Err> {
2241
if s.chars().count() != PIN_LENGTH {
23-
let len = s.len();
24-
bail!("invalid length: {len}");
42+
return Err(BadPinError::InvalidLength(s.len()));
2543
}
2644

2745
let mut pwd = [0u8; 4];
2846
for (i, digit) in s.chars().enumerate() {
29-
pwd[i] = digit.to_digit(10).ok_or(eyre!("invalid digit"))? as u8;
47+
pwd[i] = digit.to_digit(10).ok_or(BadPinError::InvalidDigit(digit))? as u8;
3048
}
3149
Ok(QrPin { password: pwd })
3250
}
@@ -40,13 +58,13 @@ pub enum PinReadMode {
4058

4159
/// supported format is *1234.png
4260
impl PinReadMode {
43-
pub fn into_qr_pin(&self) -> Result<QrPin, Report> {
61+
pub fn into_qr_pin(&self) -> Result<QrPin, BadPinError> {
4462
match self {
4563
PinReadMode::Global(ref global) => QrPin::from_str(global),
4664
PinReadMode::FromFileName(qr) => {
4765
let file_name = qr
4866
.file_stem()
49-
.ok_or_else(|| eyre!("unable to detext filename: {}", qr.to_string_lossy()))?;
67+
.ok_or_else(|| BadPinError::UnableToDetectFileName(qr.to_path_buf()))?;
5068
QrPin::from_str(
5169
&file_name
5270
.to_str()

catalyst-toolbox/src/kedqr/payload.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
1-
use chain_crypto::{Ed25519Extended, SecretKey};
2-
use color_eyre::Report;
3-
use symmetric_cipher::{decrypt, encrypt};
1+
use chain_crypto::{Ed25519Extended, SecretKey, SecretKeyError};
2+
use std::io;
3+
use symmetric_cipher::{decrypt, encrypt, Error as SymmetricCipherError};
4+
use thiserror::Error;
5+
6+
#[derive(Error, Debug)]
7+
pub enum Error {
8+
#[error("encryption-decryption protocol error")]
9+
SymmetricCipher(#[from] SymmetricCipherError),
10+
#[error("io error")]
11+
Io(#[from] io::Error),
12+
#[error("invalid secret key")]
13+
SecretKey(#[from] SecretKeyError),
14+
#[error("failed to decode hex")]
15+
HexDecode(#[from] hex::FromHexError),
16+
}
417

518
pub fn generate(key: SecretKey<Ed25519Extended>, password: &[u8]) -> String {
619
let secret = key.leak_secret();
@@ -16,7 +29,7 @@ pub fn generate(key: SecretKey<Ed25519Extended>, password: &[u8]) -> String {
1629
pub fn decode<S: Into<String>>(
1730
payload: S,
1831
password: &[u8],
19-
) -> Result<SecretKey<Ed25519Extended>, Report> {
32+
) -> Result<SecretKey<Ed25519Extended>, Error> {
2033
let encrypted_bytes = hex::decode(payload.into())?;
2134
let key = decrypt(password, &encrypted_bytes)?;
2235
Ok(SecretKey::from_binary(&key)?)

0 commit comments

Comments
 (0)