1use clap::{Args, ValueEnum};
2use std::ffi::OsString;
3use std::fs::File;
4use std::io::{stdin, Cursor};
5use std::path::Path;
6use std::{
7 cmp,
8 io::{self, Read},
9};
10
11use crate::cli::Channel;
12
13#[derive(thiserror::Error, Debug)]
14#[allow(clippy::enum_variant_names)]
15pub enum Error {
16 #[error("error decoding XDR: {0}")]
17 ReadXdrCurr(#[from] crate::curr::Error),
18 #[error("error decoding XDR: {0}")]
19 ReadXdrNext(#[from] crate::next::Error),
20 #[error("error reading file: {0}")]
21 ReadFile(#[from] std::io::Error),
22}
23
24#[derive(Args, Debug, Clone)]
25#[command()]
26pub struct Cmd {
27 #[arg()]
29 pub input: Option<OsString>,
30
31 #[arg(long = "input", value_enum, default_value_t)]
33 pub input_format: InputFormat,
34
35 #[arg(long = "output", value_enum, default_value_t)]
37 pub output_format: OutputFormat,
38
39 #[arg(long, default_value = "2")]
41 pub certainty: usize,
42}
43
44#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
45pub enum InputFormat {
46 Single,
47 SingleBase64,
48 Stream,
49 StreamBase64,
50 StreamFramed,
51}
52
53impl Default for InputFormat {
54 fn default() -> Self {
55 Self::SingleBase64
56 }
57}
58
59#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
60pub enum OutputFormat {
61 List,
62}
63
64impl Default for OutputFormat {
65 fn default() -> Self {
66 Self::List
67 }
68}
69
70macro_rules! run_x {
71 ($f:ident, $m:ident) => {
72 fn $f(&self) -> Result<(), Error> {
73 let mut rr = ResetRead::new(self.input()?);
74 let mut guessed = false;
75 'variants: for v in crate::$m::TypeVariant::VARIANTS {
76 rr.reset();
77 let count: usize = match self.input_format {
78 InputFormat::Single => {
79 let mut l = crate::$m::Limited::new(&mut rr, crate::$m::Limits::none());
80 crate::$m::Type::read_xdr_to_end(v, &mut l)
81 .ok()
82 .map(|_| 1)
83 .unwrap_or_default()
84 }
85 InputFormat::SingleBase64 => {
86 let mut l = crate::$m::Limited::new(&mut rr, crate::$m::Limits::none());
87 crate::$m::Type::read_xdr_base64_to_end(v, &mut l)
88 .ok()
89 .map(|_| 1)
90 .unwrap_or_default()
91 }
92 InputFormat::Stream => {
93 let mut l = crate::$m::Limited::new(&mut rr, crate::$m::Limits::none());
94 let iter = crate::$m::Type::read_xdr_iter(v, &mut l);
95 let iter = iter.take(self.certainty);
96 let mut count = 0;
97 for v in iter {
98 match v {
99 Ok(_) => count += 1,
100 Err(_) => continue 'variants,
101 }
102 }
103 count
104 }
105 InputFormat::StreamBase64 => {
106 let mut l = crate::$m::Limited::new(&mut rr, 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 guessed = true;
135 }
136 }
137 if (!guessed) {
138 std::process::exit(1);
139 }
140 Ok(())
141 }
142 };
143}
144
145impl Cmd {
146 pub fn run(&self, channel: &Channel) -> Result<(), Error> {
152 match channel {
153 Channel::Curr => self.run_curr()?,
154 Channel::Next => self.run_next()?,
155 }
156 Ok(())
157 }
158
159 run_x!(run_curr, curr);
160 run_x!(run_next, next);
161
162 fn input(&self) -> Result<Box<dyn Read>, Error> {
163 if let Some(input) = &self.input {
164 let exist = Path::new(input).try_exists();
165 if let Ok(true) = exist {
166 Ok(Box::new(File::open(input)?))
167 } else {
168 Ok(Box::new(Cursor::new(input.clone().into_encoded_bytes())))
169 }
170 } else {
171 Ok(Box::new(stdin()))
172 }
173 }
174}
175
176struct ResetRead<R: Read> {
177 read: R,
178 buf: Vec<u8>,
179 cursor: usize,
180}
181
182impl<R: Read> ResetRead<R> {
183 fn new(r: R) -> Self {
184 Self {
185 read: r,
186 buf: Vec::new(),
187 cursor: 0,
188 }
189 }
190
191 fn reset(&mut self) {
192 self.cursor = 0;
193 }
194}
195
196impl<R: Read> Read for ResetRead<R> {
197 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
198 let n = cmp::min(self.buf.len() - self.cursor, buf.len());
200 buf[..n].copy_from_slice(&self.buf[self.cursor..self.cursor + n]);
201 if n < buf.len() {
203 let read_n = self.read.read(buf)?;
204 self.buf.extend_from_slice(&buf[n..n + read_n]);
205 self.cursor += n + read_n;
206 Ok(n + read_n)
207 } else {
208 self.cursor += n;
209 Ok(n)
210 }
211 }
212}
213
214#[cfg(test)]
215mod test {
216 use std::{
217 error,
218 io::{Cursor, Read},
219 };
220
221 use super::ResetRead;
222
223 #[test]
224 fn test_reset_read() -> Result<(), Box<dyn error::Error>> {
225 let source: Vec<u8> = (0..8).collect();
226 let reader = Cursor::new(source);
227 let mut rr = ResetRead::new(reader);
228
229 let mut buf = [0u8; 4];
230 let n = rr.read(&mut buf)?;
231 assert_eq!(n, 4);
232 assert_eq!(buf, [0, 1, 2, 3]);
233
234 let mut buf = [0u8; 4];
235 let n = rr.read(&mut buf)?;
236 assert_eq!(n, 4);
237 assert_eq!(buf, [4, 5, 6, 7]);
238
239 let n = rr.read(&mut buf)?;
240 assert_eq!(n, 0);
241
242 rr.reset();
243 let mut buf = [0u8; 4];
244 let n = rr.read(&mut buf)?;
245 assert_eq!(n, 4);
246 assert_eq!(buf, [0, 1, 2, 3]);
247
248 Ok(())
249 }
250}