stellar_xdr/cli/
decode.rs1use std::{
2 fmt::Debug,
3 fs::File,
4 io::{stdin, Read},
5 path::PathBuf,
6 str::FromStr,
7};
8
9use clap::{Args, ValueEnum};
10use serde::Serialize;
11
12use crate::cli::{skip_whitespace::SkipWhitespace, Channel};
13
14#[derive(thiserror::Error, Debug)]
15pub enum Error {
16 #[error("unknown type {0}, choose one of {1:?}")]
17 UnknownType(String, &'static [&'static str]),
18 #[error("error decoding XDR: {0}")]
19 ReadXdrCurr(#[from] crate::curr::Error),
20 #[error("error decoding XDR: {0}")]
21 ReadXdrNext(#[from] crate::next::Error),
22 #[error("error reading file: {0}")]
23 ReadFile(#[from] std::io::Error),
24 #[error("error generating JSON: {0}")]
25 GenerateJson(#[from] serde_json::Error),
26}
27
28#[derive(Args, Debug, Clone)]
29#[command()]
30pub struct Cmd {
31 #[arg()]
33 pub files: Vec<PathBuf>,
34
35 #[arg(long)]
37 pub r#type: String,
38
39 #[arg(long, value_enum, default_value_t)]
41 pub input: InputFormat,
42
43 #[arg(long, value_enum, default_value_t)]
45 pub output: OutputFormat,
46}
47
48#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
49pub enum InputFormat {
50 Single,
51 SingleBase64,
52 Stream,
53 StreamBase64,
54 StreamFramed,
55}
56
57impl Default for InputFormat {
58 fn default() -> Self {
59 Self::StreamBase64
60 }
61}
62
63#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
64pub enum OutputFormat {
65 Json,
66 JsonFormatted,
67 RustDebug,
68 RustDebugFormatted,
69}
70
71impl Default for OutputFormat {
72 fn default() -> Self {
73 Self::Json
74 }
75}
76
77macro_rules! run_x {
78 ($f:ident, $m:ident) => {
79 fn $f(&self) -> Result<(), Error> {
80 let mut files = self.files()?;
81 let r#type = crate::$m::TypeVariant::from_str(&self.r#type).map_err(|_| {
82 Error::UnknownType(self.r#type.clone(), &crate::$m::TypeVariant::VARIANTS_STR)
83 })?;
84 for f in &mut files {
85 match self.input {
86 InputFormat::Single => {
87 let mut l = crate::$m::Limited::new(f, crate::$m::Limits::none());
88 let t = crate::$m::Type::read_xdr_to_end(r#type, &mut l)?;
89 self.out(&t)?;
90 }
91 InputFormat::SingleBase64 => {
92 let sw = SkipWhitespace::new(f);
93 let mut l = crate::$m::Limited::new(sw, crate::$m::Limits::none());
94 let t = crate::$m::Type::read_xdr_base64_to_end(r#type, &mut l)?;
95 self.out(&t)?;
96 }
97 InputFormat::Stream => {
98 let mut l = crate::$m::Limited::new(f, crate::$m::Limits::none());
99 for t in crate::$m::Type::read_xdr_iter(r#type, &mut l) {
100 self.out(&t?)?;
101 }
102 }
103 InputFormat::StreamBase64 => {
104 let sw = SkipWhitespace::new(f);
105 let mut l = crate::$m::Limited::new(sw, crate::$m::Limits::none());
106 for t in crate::$m::Type::read_xdr_base64_iter(r#type, &mut l) {
107 self.out(&t?)?;
108 }
109 }
110 InputFormat::StreamFramed => {
111 let mut l = crate::$m::Limited::new(f, crate::$m::Limits::none());
112 for t in crate::$m::Type::read_xdr_framed_iter(r#type, &mut l) {
113 self.out(&t?)?;
114 }
115 }
116 };
117 }
118 Ok(())
119 }
120 };
121}
122
123impl Cmd {
124 pub fn run(&self, channel: &Channel) -> Result<(), Error> {
130 match channel {
131 Channel::Curr => self.run_curr()?,
132 Channel::Next => self.run_next()?,
133 }
134 Ok(())
135 }
136
137 run_x!(run_curr, curr);
138 run_x!(run_next, next);
139
140 fn files(&self) -> Result<Vec<Box<dyn Read>>, Error> {
141 if self.files.is_empty() {
142 Ok(vec![Box::new(stdin())])
143 } else {
144 Ok(self
145 .files
146 .iter()
147 .map(File::open)
148 .collect::<Result<Vec<_>, _>>()?
149 .into_iter()
150 .map(|f| -> Box<dyn Read> { Box::new(f) })
151 .collect())
152 }
153 }
154
155 fn out(&self, v: &(impl Serialize + Debug)) -> Result<(), Error> {
156 match self.output {
157 OutputFormat::Json => println!("{}", serde_json::to_string(v)?),
158 OutputFormat::JsonFormatted => println!("{}", serde_json::to_string_pretty(v)?),
159 OutputFormat::RustDebug => println!("{v:?}"),
160 OutputFormat::RustDebugFormatted => println!("{v:#?}"),
161 }
162 Ok(())
163 }
164}