stellar_xdr/cli/
encode.rs1use std::ffi::OsString;
2use std::{
3 io::{stdout, Write},
4 str::FromStr,
5};
6
7use clap::{Args, ValueEnum};
8
9use crate::cli::{util, Channel};
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 JSON: {0}")]
16 ReadJsonCurr(crate::curr::Error),
17 #[error("error decoding JSON: {0}")]
18 ReadJsonNext(crate::next::Error),
19 #[error("error reading file: {0}")]
20 ReadFile(std::io::Error),
21 #[error("error writing output: {0}")]
22 WriteOutput(std::io::Error),
23 #[error("error generating XDR: {0}")]
24 WriteXdrCurr(crate::curr::Error),
25 #[error("error generating XDR: {0}")]
26 WriteXdrNext(crate::next::Error),
27}
28
29impl From<crate::curr::Error> for Error {
30 fn from(e: crate::curr::Error) -> Self {
31 match e {
32 crate::curr::Error::Invalid
33 | crate::curr::Error::Unsupported
34 | crate::curr::Error::LengthExceedsMax
35 | crate::curr::Error::LengthMismatch
36 | crate::curr::Error::NonZeroPadding
37 | crate::curr::Error::Utf8Error(_)
38 | crate::curr::Error::InvalidHex
39 | crate::curr::Error::Io(_)
40 | crate::curr::Error::DepthLimitExceeded
41 | crate::curr::Error::LengthLimitExceeded
42 | crate::curr::Error::Arbitrary(_) => Error::WriteXdrCurr(e),
43 crate::curr::Error::Json(_) => Error::ReadJsonCurr(e),
44 }
45 }
46}
47
48impl From<crate::next::Error> for Error {
49 fn from(e: crate::next::Error) -> Self {
50 match e {
51 crate::next::Error::Invalid
52 | crate::next::Error::Unsupported
53 | crate::next::Error::LengthExceedsMax
54 | crate::next::Error::LengthMismatch
55 | crate::next::Error::NonZeroPadding
56 | crate::next::Error::Utf8Error(_)
57 | crate::next::Error::InvalidHex
58 | crate::next::Error::Io(_)
59 | crate::next::Error::DepthLimitExceeded
60 | crate::next::Error::LengthLimitExceeded
61 | crate::next::Error::Arbitrary(_) => Error::WriteXdrNext(e),
62 crate::next::Error::Json(_) => Error::ReadJsonNext(e),
63 }
64 }
65}
66
67#[derive(Args, Debug, Clone)]
68#[command()]
69pub struct Cmd {
70 #[arg()]
72 pub input: Vec<OsString>,
73
74 #[arg(long)]
76 pub r#type: String,
77
78 #[arg(long = "input", value_enum, default_value_t)]
80 pub input_format: InputFormat,
81
82 #[arg(long = "output", value_enum, default_value_t)]
84 pub output_format: OutputFormat,
85}
86
87#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
88pub enum InputFormat {
89 Json,
90}
91
92impl Default for InputFormat {
93 fn default() -> Self {
94 Self::Json
95 }
96}
97
98#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
99pub enum OutputFormat {
100 Single,
101 SingleBase64,
102 Stream,
103 }
106
107impl Default for OutputFormat {
108 fn default() -> Self {
109 Self::SingleBase64
110 }
111}
112
113macro_rules! run_x {
114 ($f:ident, $m:ident) => {
115 fn $f(&self) -> Result<(), Error> {
116 use crate::$m::WriteXdr;
117 let mut input = util::parse_input(&self.input).map_err(Error::ReadFile)?;
118 let r#type = crate::$m::TypeVariant::from_str(&self.r#type).map_err(|_| {
119 Error::UnknownType(self.r#type.clone(), &crate::$m::TypeVariant::VARIANTS_STR)
120 })?;
121 for f in &mut input {
122 match self.input_format {
123 InputFormat::Json => match self.output_format {
124 OutputFormat::Single => {
125 let t = crate::$m::Type::from_json(r#type, f)?;
126 let l = crate::$m::Limits::none();
127 stdout()
128 .write_all(&t.to_xdr(l)?)
129 .map_err(Error::WriteOutput)?;
130 }
131 OutputFormat::SingleBase64 => {
132 let t = crate::$m::Type::from_json(r#type, f)?;
133 let l = crate::$m::Limits::none();
134 writeln!(stdout(), "{}", t.to_xdr_base64(l)?)
135 .map_err(Error::WriteOutput)?
136 }
137 OutputFormat::Stream => {
138 let mut de =
139 serde_json::Deserializer::new(serde_json::de::IoRead::new(f));
140 loop {
141 let t = match crate::$m::Type::deserialize_json(r#type, &mut de) {
142 Ok(t) => t,
143 Err(crate::$m::Error::Json(ref inner)) if inner.is_eof() => {
144 break;
145 }
146 Err(e) => Err(e)?,
147 };
148 let l = crate::$m::Limits::none();
149 stdout()
150 .write_all(&t.to_xdr(l)?)
151 .map_err(Error::WriteOutput)?;
152 }
153 }
154 },
155 };
156 }
157 Ok(())
158 }
159 };
160}
161
162impl Cmd {
163 pub fn run(&self, channel: &Channel) -> Result<(), Error> {
169 let result = match channel {
170 Channel::Curr => self.run_curr(),
171 Channel::Next => self.run_next(),
172 };
173
174 match result {
175 Ok(()) => Ok(()),
176 Err(Error::WriteOutput(e)) if e.kind() == std::io::ErrorKind::BrokenPipe => Ok(()),
177 Err(e) => Err(e),
178 }
179 }
180
181 run_x!(run_curr, curr);
182 run_x!(run_next, next);
183}