stellar_xdr/cli/
guess.rs

1use std::{
2    cmp,
3    fs::File,
4    io::{self, stdin, Read},
5    path::PathBuf,
6};
7
8use clap::{Args, ValueEnum};
9
10use crate::cli::{skip_whitespace::SkipWhitespace, Channel};
11
12#[derive(thiserror::Error, Debug)]
13#[allow(clippy::enum_variant_names)]
14pub enum Error {
15    #[error("error decoding XDR: {0}")]
16    ReadXdrCurr(#[from] crate::curr::Error),
17    #[error("error decoding XDR: {0}")]
18    ReadXdrNext(#[from] crate::next::Error),
19    #[error("error reading file: {0}")]
20    ReadFile(#[from] std::io::Error),
21}
22
23#[derive(Args, Debug, Clone)]
24#[command()]
25pub struct Cmd {
26    /// File to decode, or stdin if omitted
27    #[arg()]
28    pub file: Option<PathBuf>,
29
30    // Input format of the XDR
31    #[arg(long, value_enum, default_value_t)]
32    pub input: InputFormat,
33
34    // Output format
35    #[arg(long, value_enum, default_value_t)]
36    pub output: OutputFormat,
37
38    /// Certainty as an arbitrary value
39    #[arg(long, default_value = "2")]
40    pub certainty: usize,
41}
42
43#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
44pub enum InputFormat {
45    Single,
46    SingleBase64,
47    Stream,
48    StreamBase64,
49    StreamFramed,
50}
51
52impl Default for InputFormat {
53    fn default() -> Self {
54        Self::SingleBase64
55    }
56}
57
58#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
59pub enum OutputFormat {
60    List,
61}
62
63impl Default for OutputFormat {
64    fn default() -> Self {
65        Self::List
66    }
67}
68
69macro_rules! run_x {
70    ($f:ident, $m:ident) => {
71        fn $f(&self) -> Result<(), Error> {
72            let mut rr = ResetRead::new(self.file()?);
73            'variants: for v in crate::$m::TypeVariant::VARIANTS {
74                rr.reset();
75                let count: usize = match self.input {
76                    InputFormat::Single => {
77                        let mut l = crate::$m::Limited::new(&mut rr, crate::$m::Limits::none());
78                        crate::$m::Type::read_xdr_to_end(v, &mut l)
79                            .ok()
80                            .map(|_| 1)
81                            .unwrap_or_default()
82                    }
83                    InputFormat::SingleBase64 => {
84                        let sw = SkipWhitespace::new(&mut rr);
85                        let mut l = crate::$m::Limited::new(sw, crate::$m::Limits::none());
86                        crate::$m::Type::read_xdr_base64_to_end(v, &mut l)
87                            .ok()
88                            .map(|_| 1)
89                            .unwrap_or_default()
90                    }
91                    InputFormat::Stream => {
92                        let mut l = crate::$m::Limited::new(&mut rr, crate::$m::Limits::none());
93                        let iter = crate::$m::Type::read_xdr_iter(v, &mut l);
94                        let iter = iter.take(self.certainty);
95                        let mut count = 0;
96                        for v in iter {
97                            match v {
98                                Ok(_) => count += 1,
99                                Err(_) => continue 'variants,
100                            }
101                        }
102                        count
103                    }
104                    InputFormat::StreamBase64 => {
105                        let sw = SkipWhitespace::new(&mut rr);
106                        let mut l = crate::$m::Limited::new(sw, crate::$m::Limits::none());
107                        let iter = crate::$m::Type::read_xdr_base64_iter(v, &mut l);
108                        let iter = iter.take(self.certainty);
109                        let mut count = 0;
110                        for v in iter {
111                            match v {
112                                Ok(_) => count += 1,
113                                Err(_) => continue 'variants,
114                            }
115                        }
116                        count
117                    }
118                    InputFormat::StreamFramed => {
119                        let mut l = crate::$m::Limited::new(&mut rr, crate::$m::Limits::none());
120                        let iter = crate::$m::Type::read_xdr_framed_iter(v, &mut l);
121                        let iter = iter.take(self.certainty);
122                        let mut count = 0;
123                        for v in iter {
124                            match v {
125                                Ok(_) => count += 1,
126                                Err(_) => continue 'variants,
127                            }
128                        }
129                        count
130                    }
131                };
132                if count > 0 {
133                    println!("{}", v.name());
134                }
135            }
136            Ok(())
137        }
138    };
139}
140
141impl Cmd {
142    /// Run the CLIs guess command.
143    ///
144    /// ## Errors
145    ///
146    /// If the command is configured with state that is invalid.
147    pub fn run(&self, channel: &Channel) -> Result<(), Error> {
148        match channel {
149            Channel::Curr => self.run_curr()?,
150            Channel::Next => self.run_next()?,
151        }
152        Ok(())
153    }
154
155    run_x!(run_curr, curr);
156    run_x!(run_next, next);
157
158    fn file(&self) -> Result<Box<dyn Read>, Error> {
159        if let Some(f) = &self.file {
160            Ok(Box::new(File::open(f)?))
161        } else {
162            Ok(Box::new(stdin()))
163        }
164    }
165}
166
167struct ResetRead<R: Read> {
168    read: R,
169    buf: Vec<u8>,
170    cursor: usize,
171}
172
173impl<R: Read> ResetRead<R> {
174    fn new(r: R) -> Self {
175        Self {
176            read: r,
177            buf: Vec::new(),
178            cursor: 0,
179        }
180    }
181
182    fn reset(&mut self) {
183        self.cursor = 0;
184    }
185}
186
187impl<R: Read> Read for ResetRead<R> {
188    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
189        // Read from the buffer first into buf.
190        let n = cmp::min(self.buf.len() - self.cursor, buf.len());
191        buf[..n].copy_from_slice(&self.buf[self.cursor..self.cursor + n]);
192        // Read from the reader and cache the result in the buf if the buf is consumed.
193        if n < buf.len() {
194            let read_n = self.read.read(buf)?;
195            self.buf.extend_from_slice(&buf[n..n + read_n]);
196            self.cursor += n + read_n;
197            Ok(n + read_n)
198        } else {
199            self.cursor += n;
200            Ok(n)
201        }
202    }
203}
204
205#[cfg(test)]
206mod test {
207    use std::{
208        error,
209        io::{Cursor, Read},
210    };
211
212    use super::ResetRead;
213
214    #[test]
215    fn test_reset_read() -> Result<(), Box<dyn error::Error>> {
216        let source: Vec<u8> = (0..8).collect();
217        let reader = Cursor::new(source);
218        let mut rr = ResetRead::new(reader);
219
220        let mut buf = [0u8; 4];
221        let n = rr.read(&mut buf)?;
222        assert_eq!(n, 4);
223        assert_eq!(buf, [0, 1, 2, 3]);
224
225        let mut buf = [0u8; 4];
226        let n = rr.read(&mut buf)?;
227        assert_eq!(n, 4);
228        assert_eq!(buf, [4, 5, 6, 7]);
229
230        let n = rr.read(&mut buf)?;
231        assert_eq!(n, 0);
232
233        rr.reset();
234        let mut buf = [0u8; 4];
235        let n = rr.read(&mut buf)?;
236        assert_eq!(n, 4);
237        assert_eq!(buf, [0, 1, 2, 3]);
238
239        Ok(())
240    }
241}