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;
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 ReadJson(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 #[error("error generating XDR: {0}")]
22 WriteXdr(crate::Error),
23}
24
25impl From<crate::Error> for Error {
26 fn from(e: crate::Error) -> Self {
27 match e {
28 crate::Error::Invalid
29 | crate::Error::Unsupported
30 | crate::Error::LengthExceedsMax
31 | crate::Error::LengthMismatch
32 | crate::Error::NonZeroPadding
33 | crate::Error::Utf8Error(_)
34 | crate::Error::InvalidHex
35 | crate::Error::Io(_)
36 | crate::Error::DepthLimitExceeded
37 | crate::Error::LengthLimitExceeded
38 | crate::Error::Arbitrary(_) => Error::WriteXdr(e),
39 crate::Error::Json(_) => Error::ReadJson(e),
40 }
41 }
42}
43
44#[derive(Args, Debug, Clone)]
45#[command()]
46pub struct Cmd {
47 #[arg()]
49 pub input: Vec<OsString>,
50
51 #[arg(long)]
53 pub r#type: String,
54
55 #[arg(long = "input", value_enum, default_value_t)]
57 pub input_format: InputFormat,
58
59 #[arg(long = "output", value_enum, default_value_t)]
61 pub output_format: OutputFormat,
62}
63
64#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
65pub enum InputFormat {
66 Json,
67}
68
69impl Default for InputFormat {
70 fn default() -> Self {
71 Self::Json
72 }
73}
74
75#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
76pub enum OutputFormat {
77 Single,
78 SingleBase64,
79 Stream,
80 }
83
84impl Default for OutputFormat {
85 fn default() -> Self {
86 Self::SingleBase64
87 }
88}
89
90macro_rules! run_x {
93 ($f:ident) => {
94 fn $f(&self) -> Result<(), Error> {
95 use crate::WriteXdr;
96 let mut input = util::parse_input(&self.input).map_err(Error::ReadFile)?;
97 let r#type = crate::TypeVariant::from_str(&self.r#type).map_err(|_| {
98 Error::UnknownType(self.r#type.clone(), &crate::TypeVariant::VARIANTS_STR)
99 })?;
100 for f in &mut input {
101 match self.input_format {
102 InputFormat::Json => match self.output_format {
103 OutputFormat::Single => {
104 let t = crate::Type::from_json(r#type, f)?;
105 let l = crate::Limits::none();
106 stdout()
107 .write_all(&t.to_xdr(l)?)
108 .map_err(Error::WriteOutput)?;
109 }
110 OutputFormat::SingleBase64 => {
111 let t = crate::Type::from_json(r#type, f)?;
112 let l = crate::Limits::none();
113 writeln!(stdout(), "{}", t.to_xdr_base64(l)?)
114 .map_err(Error::WriteOutput)?
115 }
116 OutputFormat::Stream => {
117 let mut de =
118 serde_json::Deserializer::new(serde_json::de::IoRead::new(f));
119 loop {
120 let t = match crate::Type::deserialize_json(r#type, &mut de) {
121 Ok(t) => t,
122 Err(crate::Error::Json(ref inner)) if inner.is_eof() => {
123 break;
124 }
125 Err(e) => Err(e)?,
126 };
127 let l = crate::Limits::none();
128 stdout()
129 .write_all(&t.to_xdr(l)?)
130 .map_err(Error::WriteOutput)?;
131 }
132 }
133 },
134 };
135 }
136 Ok(())
137 }
138 };
139}
140
141impl Cmd {
142 pub fn run(&self) -> Result<(), Error> {
148 let result = self.run_inner();
149 match result {
150 Ok(()) => Ok(()),
151 Err(Error::WriteOutput(e)) if e.kind() == std::io::ErrorKind::BrokenPipe => Ok(()),
152 Err(e) => Err(e),
153 }
154 }
155
156 run_x!(run_inner);
157}