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(#[from] std::io::Error),
21 #[error("error generating XDR: {0}")]
22 WriteXdrCurr(crate::curr::Error),
23 #[error("error generating XDR: {0}")]
24 WriteXdrNext(crate::next::Error),
25}
26
27impl From<crate::curr::Error> for Error {
28 fn from(e: crate::curr::Error) -> Self {
29 match e {
30 crate::curr::Error::Invalid
31 | crate::curr::Error::Unsupported
32 | crate::curr::Error::LengthExceedsMax
33 | crate::curr::Error::LengthMismatch
34 | crate::curr::Error::NonZeroPadding
35 | crate::curr::Error::Utf8Error(_)
36 | crate::curr::Error::InvalidHex
37 | crate::curr::Error::Io(_)
38 | crate::curr::Error::DepthLimitExceeded
39 | crate::curr::Error::LengthLimitExceeded
40 | crate::curr::Error::Arbitrary(_) => 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
59 | crate::next::Error::Arbitrary(_) => Error::WriteXdrNext(e),
60 crate::next::Error::Json(_) => Error::ReadJsonNext(e),
61 }
62 }
63}
64
65#[derive(Args, Debug, Clone)]
66#[command()]
67pub struct Cmd {
68 #[arg()]
70 pub input: Vec<OsString>,
71
72 #[arg(long)]
74 pub r#type: String,
75
76 #[arg(long = "input", value_enum, default_value_t)]
78 pub input_format: InputFormat,
79
80 #[arg(long = "output", value_enum, default_value_t)]
82 pub output_format: OutputFormat,
83}
84
85#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
86pub enum InputFormat {
87 Json,
88}
89
90impl Default for InputFormat {
91 fn default() -> Self {
92 Self::Json
93 }
94}
95
96#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
97pub enum OutputFormat {
98 Single,
99 SingleBase64,
100 Stream,
101 }
104
105impl Default for OutputFormat {
106 fn default() -> Self {
107 Self::SingleBase64
108 }
109}
110
111macro_rules! run_x {
112 ($f:ident, $m:ident) => {
113 fn $f(&self) -> Result<(), Error> {
114 use crate::$m::WriteXdr;
115 let mut input = util::parse_input::<Error>(&self.input)?;
116 let r#type = crate::$m::TypeVariant::from_str(&self.r#type).map_err(|_| {
117 Error::UnknownType(self.r#type.clone(), &crate::$m::TypeVariant::VARIANTS_STR)
118 })?;
119 for f in &mut input {
120 match self.input_format {
121 InputFormat::Json => match self.output_format {
122 OutputFormat::Single => {
123 let t = crate::$m::Type::from_json(r#type, f)?;
124 let l = crate::$m::Limits::none();
125 stdout().write_all(&t.to_xdr(l)?)?
126 }
127 OutputFormat::SingleBase64 => {
128 let t = crate::$m::Type::from_json(r#type, f)?;
129 let l = crate::$m::Limits::none();
130 println!("{}", t.to_xdr_base64(l)?)
131 }
132 OutputFormat::Stream => {
133 let mut de =
134 serde_json::Deserializer::new(serde_json::de::IoRead::new(f));
135 loop {
136 let t = match crate::$m::Type::deserialize_json(r#type, &mut de) {
137 Ok(t) => t,
138 Err(crate::$m::Error::Json(ref inner)) if inner.is_eof() => {
139 break;
140 }
141 Err(e) => Err(e)?,
142 };
143 let l = crate::$m::Limits::none();
144 stdout().write_all(&t.to_xdr(l)?)?
145 }
146 }
147 },
148 };
149 }
150 Ok(())
151 }
152 };
153}
154
155impl Cmd {
156 pub fn run(&self, channel: &Channel) -> Result<(), Error> {
162 match channel {
163 Channel::Curr => self.run_curr()?,
164 Channel::Next => self.run_next()?,
165 }
166 Ok(())
167 }
168
169 run_x!(run_curr, curr);
170 run_x!(run_next, next);
171}