stellar_xdr/cli/
compare.rs1use std::{
2 fmt::Debug,
3 fs::File,
4 io::{stdout, Write},
5 path::PathBuf,
6 str::FromStr,
7};
8
9use clap::{Args, ValueEnum};
10
11#[derive(thiserror::Error, Debug)]
12pub enum Error {
13 #[error("unknown type {0}, choose one of {1:?}")]
14 UnknownType(String, &'static [&'static str]),
15 #[error("error decoding XDR: {0}")]
16 ReadXdr(#[from] crate::Error),
17 #[error("error reading file: {0}")]
18 ReadFile(std::io::Error),
19 #[error("error writing output: {0}")]
20 WriteOutput(std::io::Error),
21}
22
23#[derive(Args, Debug, Clone)]
30#[command()]
31pub struct Cmd {
32 #[arg()]
34 pub left: PathBuf,
35
36 #[arg()]
38 pub right: PathBuf,
39
40 #[arg(long)]
42 pub r#type: String,
43
44 #[arg(long, value_enum, default_value_t)]
46 pub input: InputFormat,
47}
48
49#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
50pub enum InputFormat {
51 Single,
52 SingleBase64,
53}
54
55impl Default for InputFormat {
56 fn default() -> Self {
57 Self::SingleBase64
58 }
59}
60
61macro_rules! run_x {
64 ($f:ident) => {
65 fn $f(&self) -> Result<(), Error> {
66 let f1 = File::open(&self.left).map_err(Error::ReadFile)?;
67 let f2 = File::open(&self.right).map_err(Error::ReadFile)?;
68 let r#type = crate::TypeVariant::from_str(&self.r#type).map_err(|_| {
69 Error::UnknownType(self.r#type.clone(), &crate::TypeVariant::VARIANTS_STR)
70 })?;
71 let (t1, t2) = match self.input {
72 InputFormat::Single => {
73 let t1 = {
74 let mut l1 = crate::Limited::new(f1, crate::Limits::none());
75 crate::Type::read_xdr_to_end(r#type, &mut l1)?
76 };
77 let t2 = {
78 let mut l = crate::Limited::new(f2, crate::Limits::none());
79 crate::Type::read_xdr_to_end(r#type, &mut l)?
80 };
81 (t1, t2)
82 }
83 InputFormat::SingleBase64 => {
84 let t1 = {
85 let mut l = crate::Limited::new(f1, crate::Limits::none());
86 crate::Type::read_xdr_base64_to_end(r#type, &mut l)?
87 };
88 let t2 = {
89 let mut l = crate::Limited::new(f2, crate::Limits::none());
90 crate::Type::read_xdr_base64_to_end(r#type, &mut l)?
91 };
92 (t1, t2)
93 }
94 };
95 let cmp = t1.cmp(&t2) as i8;
96 writeln!(stdout(), "{cmp}").map_err(Error::WriteOutput)?;
97 Ok(())
98 }
99 };
100}
101
102impl Cmd {
103 pub fn run(&self) -> Result<(), Error> {
109 let result = self.run_inner();
110 match result {
111 Ok(()) => Ok(()),
112 Err(Error::WriteOutput(e)) if e.kind() == std::io::ErrorKind::BrokenPipe => Ok(()),
113 Err(e) => Err(e),
114 }
115 }
116
117 run_x!(run_inner);
118}