stellar_xdr/cli/generate/
default.rs

1use clap::{Args, ValueEnum};
2use std::{
3    io::{stdout, Write},
4    str::FromStr,
5};
6
7use crate::cli::{util, Channel};
8
9#[derive(thiserror::Error, Debug)]
10pub enum Error {
11    #[error("unknown type {0}, choose one of {1:?}")]
12    UnknownType(String, &'static [&'static str]),
13    #[error("error reading file: {0}")]
14    ReadFile(#[from] std::io::Error),
15    #[error("error generating XDR: {0}")]
16    WriteXdrCurr(crate::curr::Error),
17    #[error("error generating XDR: {0}")]
18    WriteXdrNext(crate::next::Error),
19    #[error("error generating JSON: {0}")]
20    GenerateJson(#[from] serde_json::Error),
21    #[error("error generating arbitrary value: {0}")]
22    Arbitrary(#[from] arbitrary::Error),
23    #[error("type doesn't have a text representation, use 'json' as output")]
24    TextUnsupported,
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(_)
41            | crate::curr::Error::Json(_) => Error::WriteXdrCurr(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(_)
60            | crate::next::Error::Json(_) => Error::WriteXdrNext(e),
61        }
62    }
63}
64
65/// Generate default XDR values
66#[derive(Args, Debug, Clone)]
67#[command()]
68pub struct Cmd {
69    /// XDR type to generate
70    #[arg(long)]
71    pub r#type: String,
72
73    // Output format to encode to
74    #[arg(long = "output", value_enum, default_value_t)]
75    pub output_format: OutputFormat,
76}
77
78#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
79pub enum OutputFormat {
80    Single,
81    SingleBase64,
82    // TODO: Stream,
83    // TODO: StreamBase64,
84    // TODO: StreamFramed,
85    Json,
86    JsonFormatted,
87    Text,
88}
89
90impl Default for OutputFormat {
91    fn default() -> Self {
92        Self::SingleBase64
93    }
94}
95
96macro_rules! run_x {
97    ($f:ident, $m:ident) => {
98        fn $f(&self) -> Result<(), Error> {
99            use crate::$m::WriteXdr;
100            let r#type = crate::$m::TypeVariant::from_str(&self.r#type).map_err(|_| {
101                Error::UnknownType(self.r#type.clone(), &crate::$m::TypeVariant::VARIANTS_STR)
102            })?;
103            let v = crate::$m::Type::default(r#type);
104            match self.output_format {
105                OutputFormat::Single => {
106                    let l = crate::$m::Limits::none();
107                    stdout().write_all(&v.to_xdr(l)?)?
108                }
109                OutputFormat::SingleBase64 => {
110                    let l = crate::$m::Limits::none();
111                    println!("{}", v.to_xdr_base64(l)?)
112                }
113                OutputFormat::Json => {
114                    println!("{}", serde_json::to_string(&v)?);
115                }
116                OutputFormat::JsonFormatted => {
117                    println!("{}", serde_json::to_string_pretty(&v)?);
118                }
119                OutputFormat::Text => {
120                    let v = serde_json::to_value(v)?;
121                    let text = util::serde_json_value_to_text(v).ok_or(Error::TextUnsupported)?;
122                    println!("{text}")
123                }
124            }
125            Ok(())
126        }
127    };
128}
129
130impl Cmd {
131    /// Run the CLIs generate zero command.
132    ///
133    /// ## Errors
134    ///
135    /// If the command is configured with state that is invalid.
136    pub fn run(&self, channel: &Channel) -> Result<(), Error> {
137        match channel {
138            Channel::Curr => self.run_curr()?,
139            Channel::Next => self.run_next()?,
140        }
141        Ok(())
142    }
143
144    run_x!(run_curr, curr);
145    run_x!(run_next, next);
146}