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