Skip to content

Commit

Permalink
Render friendly errors when errors occur in CLI (#203)
Browse files Browse the repository at this point in the history
  • Loading branch information
leighmcculloch authored Nov 30, 2022
1 parent 7532610 commit bb342f0
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 13 deletions.
21 changes: 21 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down
30 changes: 24 additions & 6 deletions src/bin/stellar-xdr/decode.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::{
error::Error,
fs::File,
io::{stdin, Read},
path::PathBuf,
Expand All @@ -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 {
Expand Down Expand Up @@ -60,9 +73,14 @@ impl Default for OutputFormat {

macro_rules! run_x {
($f:ident, $m:ident) => {
fn $f(&self) -> Result<(), Box<dyn Error>> {
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 => {
Expand Down Expand Up @@ -96,7 +114,7 @@ macro_rules! run_x {
}

impl Cmd {
pub fn run(&self, channel: &Channel) -> Result<(), Box<dyn Error>> {
pub fn run(&self, channel: &Channel) -> Result<(), Error> {
match channel {
Channel::Curr => self.run_curr()?,
Channel::Next => self.run_next()?,
Expand All @@ -107,7 +125,7 @@ impl Cmd {
run_x!(run_curr, curr);
run_x!(run_next, next);

fn files(&self) -> Result<Vec<Box<dyn Read>>, Box<dyn Error>> {
fn files(&self) -> Result<Vec<Box<dyn Read>>, Error> {
if self.files.is_empty() {
Ok(vec![Box::new(stdin())])
} else {
Expand All @@ -122,7 +140,7 @@ impl Cmd {
}
}

fn out(&self, v: &impl Serialize) -> Result<(), Box<dyn Error>> {
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)?),
Expand Down
17 changes: 11 additions & 6 deletions src/bin/stellar-xdr/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -49,14 +48,20 @@ enum Cmd {
Version,
}

fn main() -> Result<(), Box<dyn Error>> {
fn run() -> Result<(), Box<dyn Error>> {
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()
}
}

0 comments on commit bb342f0

Please sign in to comment.