diff --git a/src/bin/stellar-xdr/main.rs b/src/bin/stellar-xdr/main.rs index 2873708b..fdfdcef3 100644 --- a/src/bin/stellar-xdr/main.rs +++ b/src/bin/stellar-xdr/main.rs @@ -9,7 +9,8 @@ fn main() { cli::Error::Types(_) | cli::Error::Guess(_) | cli::Error::Decode(_) - | cli::Error::Encode(_) => { + | cli::Error::Encode(_) + | cli::Error::Compare(_) => { Error::raw(clap::error::ErrorKind::ValueValidation, e).exit() } } diff --git a/src/cli/compare.rs b/src/cli/compare.rs new file mode 100644 index 00000000..55b195c6 --- /dev/null +++ b/src/cli/compare.rs @@ -0,0 +1,114 @@ +use std::{fmt::Debug, fs::File, path::PathBuf, str::FromStr}; + +use clap::{Args, ValueEnum}; + +use crate::cli::{skip_whitespace::SkipWhitespace, 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] crate::curr::Error), + #[error("error decoding XDR: {0}")] + ReadXdrNext(#[from] crate::next::Error), + #[error("error reading file: {0}")] + ReadFile(#[from] std::io::Error), +} + +/// Compare two XDR values with each other +/// +/// Outputs: +/// `-1` when the left XDR value is less than the right XDR value, +/// `0` when the left XDR value is equal to the right XDR value, +/// `1` when the left XDR value is greater than the right XDR value +#[derive(Args, Debug, Clone)] +#[command()] +pub struct Cmd { + /// XDR file to decode and compare with the right value + #[arg()] + left: PathBuf, + + /// XDR file to decode and compare with the left value + #[arg()] + right: PathBuf, + + /// XDR type of both inputs + #[arg(long)] + r#type: String, + + // Input format of the XDR + #[arg(long, value_enum, default_value_t)] + input: InputFormat, +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)] +pub enum InputFormat { + Single, + SingleBase64, +} + +impl Default for InputFormat { + fn default() -> Self { + Self::SingleBase64 + } +} + +macro_rules! run_x { + ($f:ident, $m:ident) => { + fn $f(&self) -> Result<(), Error> { + let f1 = File::open(&self.left)?; + let f2 = File::open(&self.right)?; + let r#type = crate::$m::TypeVariant::from_str(&self.r#type).map_err(|_| { + Error::UnknownType(self.r#type.clone(), &crate::$m::TypeVariant::VARIANTS_STR) + })?; + let (t1, t2) = match self.input { + InputFormat::Single => { + let t1 = { + let mut l1 = crate::$m::Limited::new(f1, crate::$m::Limits::none()); + crate::$m::Type::read_xdr_to_end(r#type, &mut l1)? + }; + let t2 = { + let mut l = crate::$m::Limited::new(f2, crate::$m::Limits::none()); + crate::$m::Type::read_xdr_to_end(r#type, &mut l)? + }; + (t1, t2) + } + InputFormat::SingleBase64 => { + let t1 = { + let sw = SkipWhitespace::new(f1); + let mut l = crate::$m::Limited::new(sw, crate::$m::Limits::none()); + crate::$m::Type::read_xdr_base64_to_end(r#type, &mut l)? + }; + let t2 = { + let sw = SkipWhitespace::new(f2); + let mut l = crate::$m::Limited::new(sw, crate::$m::Limits::none()); + crate::$m::Type::read_xdr_base64_to_end(r#type, &mut l)? + }; + (t1, t2) + } + }; + let cmp = t1.cmp(&t2) as i8; + println!("{cmp}"); + Ok(()) + } + }; +} + +impl Cmd { + /// Run the CLIs decode command. + /// + /// ## Errors + /// + /// If the command is configured with state that is invalid. + pub fn run(&self, channel: &Channel) -> Result<(), Error> { + match channel { + Channel::Curr => self.run_curr()?, + Channel::Next => self.run_next()?, + } + Ok(()) + } + + run_x!(run_curr, curr); + run_x!(run_next, next); +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 6128be04..ef64e159 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,3 +1,4 @@ +pub mod compare; pub mod decode; pub mod encode; pub mod guess; @@ -39,6 +40,7 @@ impl Root { Cmd::Guess(c) => c.run(&self.channel)?, Cmd::Decode(c) => c.run(&self.channel)?, Cmd::Encode(c) => c.run(&self.channel)?, + Cmd::Compare(c) => c.run(&self.channel)?, Cmd::Version => version::Cmd::run(), } Ok(()) @@ -69,6 +71,7 @@ pub enum Cmd { Decode(decode::Cmd), /// Encode XDR Encode(encode::Cmd), + Compare(compare::Cmd), /// Print version information Version, } @@ -86,6 +89,8 @@ pub enum Error { Decode(#[from] decode::Error), #[error("error reading file: {0}")] Encode(#[from] encode::Error), + #[error(transparent)] + Compare(#[from] compare::Error), } /// Run the CLI with the given args.