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