diff --git a/Cargo.lock b/Cargo.lock index b02d1013..fd08a7d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -567,6 +567,7 @@ dependencies = [ "serde", "serde_json", "serde_with", + "thiserror", ] [[package]] @@ -595,6 +596,26 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.3.15" diff --git a/Cargo.toml b/Cargo.toml index d06c2afe..d9fcf470 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ hex = { version = "0.4.3", optional = true } arbitrary = {version = "1.1.3", features = ["derive"], optional = true} clap = { version = "4.0.26", features = ["derive"], optional = true } serde_json = { version = "1.0.89", optional = true } +thiserror = { version = "1.0.37", optional = true } [dev_dependencies] serde_json = "1.0.86" @@ -44,7 +45,7 @@ arbitrary = ["std", "dep:arbitrary"] hex = [] # Features for the CLI. -cli = ["std", "curr", "next", "base64", "serde", "dep:clap", "dep:serde_json"] +cli = ["std", "curr", "next", "base64", "serde", "dep:clap", "dep:serde_json", "dep:thiserror"] [package.metadata.docs.rs] all-features = true diff --git a/src/bin/stellar-xdr/decode.rs b/src/bin/stellar-xdr/decode.rs index c831c4c5..1f16ddc2 100644 --- a/src/bin/stellar-xdr/decode.rs +++ b/src/bin/stellar-xdr/decode.rs @@ -1,5 +1,4 @@ use std::{ - error::Error, fs::File, io::{stdin, Read}, path::PathBuf, @@ -11,6 +10,20 @@ use serde::Serialize; use crate::Channel; +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("unknown type {0}, choose one of {1:?}")] + UnknownType(String, &'static [&'static str]), + #[error("error decoding XDR: {0}")] + ReadXdrCurr(#[from] stellar_xdr::curr::Error), + #[error("error decoding XDR: {0}")] + ReadXdrNext(#[from] stellar_xdr::next::Error), + #[error("error reading file: {0}")] + ReadFile(#[from] std::io::Error), + #[error("error generating JSON: {0}")] + GenerateJson(#[from] serde_json::Error), +} + #[derive(Args, Debug, Clone)] #[command()] pub struct Cmd { @@ -60,9 +73,14 @@ impl Default for OutputFormat { macro_rules! run_x { ($f:ident, $m:ident) => { - fn $f(&self) -> Result<(), Box> { + fn $f(&self) -> Result<(), Error> { let mut files = self.files()?; - let r#type = stellar_xdr::$m::TypeVariant::from_str(&self.r#type)?; + let r#type = stellar_xdr::$m::TypeVariant::from_str(&self.r#type).map_err(|_| { + Error::UnknownType( + self.r#type.clone(), + &stellar_xdr::$m::TypeVariant::VARIANTS_STR, + ) + })?; for f in &mut files { match self.input { InputFormat::Single => { @@ -96,7 +114,7 @@ macro_rules! run_x { } impl Cmd { - pub fn run(&self, channel: &Channel) -> Result<(), Box> { + pub fn run(&self, channel: &Channel) -> Result<(), Error> { match channel { Channel::Curr => self.run_curr()?, Channel::Next => self.run_next()?, @@ -107,7 +125,7 @@ impl Cmd { run_x!(run_curr, curr); run_x!(run_next, next); - fn files(&self) -> Result>, Box> { + fn files(&self) -> Result>, Error> { if self.files.is_empty() { Ok(vec![Box::new(stdin())]) } else { @@ -122,7 +140,7 @@ impl Cmd { } } - fn out(&self, v: &impl Serialize) -> Result<(), Box> { + fn out(&self, v: &impl Serialize) -> Result<(), Error> { match self.output { OutputFormat::Json => println!("{}", serde_json::to_string(v)?), OutputFormat::JsonFormatted => println!("{}", serde_json::to_string_pretty(v)?), diff --git a/src/bin/stellar-xdr/main.rs b/src/bin/stellar-xdr/main.rs index daeef4c0..1a6c0f1b 100644 --- a/src/bin/stellar-xdr/main.rs +++ b/src/bin/stellar-xdr/main.rs @@ -2,9 +2,8 @@ mod decode; mod types; mod version; -use std::error::Error; - -use clap::{Parser, Subcommand, ValueEnum}; +use clap::{CommandFactory, Parser, Subcommand, ValueEnum}; +use std::{error::Error, fmt::Debug}; #[derive(Parser, Debug, Clone)] #[command( @@ -49,14 +48,20 @@ enum Cmd { Version, } -fn main() -> Result<(), Box> { +fn run() -> Result<(), Box> { let root = Root::parse(); - match root.cmd { Cmd::Types(c) => c.run(&root.channel)?, Cmd::Decode(c) => c.run(&root.channel)?, Cmd::Version => version::Cmd::run(), } - Ok(()) } + +fn main() { + if let Err(e) = run() { + Root::command() + .error(clap::error::ErrorKind::ValueValidation, e) + .exit() + } +}