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