1use clap::{Args, ValueEnum};
2use std::cmp;
3use std::ffi::OsString;
4use std::fs::File;
5use std::io::{self, stdin, stdout, Cursor, Read, Write};
6use std::path::Path;
7
8use crate::cli::Channel;
9
10#[derive(thiserror::Error, Debug)]
11#[allow(clippy::enum_variant_names)]
12pub enum Error {
13 #[error("error decoding XDR: {0}")]
14 ReadXdrCurr(#[from] crate::curr::Error),
15 #[error("error decoding XDR: {0}")]
16 ReadXdrNext(#[from] crate::next::Error),
17 #[error("error reading file: {0}")]
18 ReadFile(std::io::Error),
19 #[error("error writing output: {0}")]
20 WriteOutput(std::io::Error),
21}
22
23#[derive(Args, Debug, Clone)]
24#[command()]
25pub struct Cmd {
26 #[arg()]
28 pub input: Option<OsString>,
29
30 #[arg(long = "input", value_enum, default_value_t)]
32 pub input_format: InputFormat,
33
34 #[arg(long = "output", value_enum, default_value_t)]
36 pub output_format: OutputFormat,
37
38 #[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.input()?);
73 let mut guessed = false;
74 'variants: for v in crate::$m::TypeVariant::VARIANTS {
75 rr.reset();
76 let count: usize = match self.input_format {
77 InputFormat::Single => {
78 let mut l = crate::$m::Limited::new(&mut rr, crate::$m::Limits::none());
79 crate::$m::Type::read_xdr_to_end(v, &mut l)
80 .ok()
81 .map(|_| 1)
82 .unwrap_or_default()
83 }
84 InputFormat::SingleBase64 => {
85 let mut l = crate::$m::Limited::new(&mut rr, 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 mut l = crate::$m::Limited::new(&mut rr, crate::$m::Limits::none());
106 let iter = crate::$m::Type::read_xdr_base64_iter(v, &mut l);
107 let iter = iter.take(self.certainty);
108 let mut count = 0;
109 for v in iter {
110 match v {
111 Ok(_) => count += 1,
112 Err(_) => continue 'variants,
113 }
114 }
115 count
116 }
117 InputFormat::StreamFramed => {
118 let mut l = crate::$m::Limited::new(&mut rr, crate::$m::Limits::none());
119 let iter = crate::$m::Type::read_xdr_framed_iter(v, &mut l);
120 let iter = iter.take(self.certainty);
121 let mut count = 0;
122 for v in iter {
123 match v {
124 Ok(_) => count += 1,
125 Err(_) => continue 'variants,
126 }
127 }
128 count
129 }
130 };
131 if count > 0 {
132 writeln!(stdout(), "{}", v.name()).map_err(Error::WriteOutput)?;
133 guessed = true;
134 }
135 }
136 if (!guessed) {
137 std::process::exit(1);
138 }
139 Ok(())
140 }
141 };
142}
143
144impl Cmd {
145 pub fn run(&self, channel: &Channel) -> Result<(), Error> {
151 let result = match channel {
152 Channel::Curr => self.run_curr(),
153 Channel::Next => self.run_next(),
154 };
155
156 match result {
157 Ok(()) => Ok(()),
158 Err(Error::WriteOutput(e)) if e.kind() == std::io::ErrorKind::BrokenPipe => Ok(()),
159 Err(e) => Err(e),
160 }
161 }
162
163 run_x!(run_curr, curr);
164 run_x!(run_next, next);
165
166 fn input(&self) -> Result<Box<dyn Read>, Error> {
167 if let Some(input) = &self.input {
168 let exist = Path::new(input).try_exists();
169 if let Ok(true) = exist {
170 Ok(Box::new(File::open(input).map_err(Error::ReadFile)?))
171 } else {
172 Ok(Box::new(Cursor::new(input.clone().into_encoded_bytes())))
173 }
174 } else {
175 Ok(Box::new(stdin()))
176 }
177 }
178}
179
180struct ResetRead<R: Read> {
181 read: R,
182 buf: Vec<u8>,
183 cursor: usize,
184}
185
186impl<R: Read> ResetRead<R> {
187 fn new(r: R) -> Self {
188 Self {
189 read: r,
190 buf: Vec::new(),
191 cursor: 0,
192 }
193 }
194
195 fn reset(&mut self) {
196 self.cursor = 0;
197 }
198}
199
200impl<R: Read> Read for ResetRead<R> {
201 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
202 let n = cmp::min(self.buf.len() - self.cursor, buf.len());
204 buf[..n].copy_from_slice(&self.buf[self.cursor..self.cursor + n]);
205 if n < buf.len() {
207 let read_n = self.read.read(&mut buf[n..])?;
208 self.buf.extend_from_slice(&buf[n..n + read_n]);
209 self.cursor += n + read_n;
210 Ok(n + read_n)
211 } else {
212 self.cursor += n;
213 Ok(n)
214 }
215 }
216}
217
218#[cfg(test)]
219mod test {
220 use std::{
221 error,
222 io::{Cursor, Read},
223 };
224
225 use super::ResetRead;
226
227 #[test]
228 fn test_reset_read() -> Result<(), Box<dyn error::Error>> {
229 let source: Vec<u8> = (0..8).collect();
230 let reader = Cursor::new(source);
231 let mut rr = ResetRead::new(reader);
232
233 let mut buf = [0u8; 4];
234 let n = rr.read(&mut buf)?;
235 assert_eq!(n, 4);
236 assert_eq!(buf, [0, 1, 2, 3]);
237
238 let mut buf = [0u8; 4];
239 let n = rr.read(&mut buf)?;
240 assert_eq!(n, 4);
241 assert_eq!(buf, [4, 5, 6, 7]);
242
243 let n = rr.read(&mut buf)?;
244 assert_eq!(n, 0);
245
246 rr.reset();
247 let mut buf = [0u8; 4];
248 let n = rr.read(&mut buf)?;
249 assert_eq!(n, 4);
250 assert_eq!(buf, [0, 1, 2, 3]);
251
252 Ok(())
253 }
254
255 #[test]
259 fn test_reset_read_partial_cache_overlap() -> Result<(), Box<dyn error::Error>> {
260 let source: Vec<u8> = vec![
262 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
263 ];
264 let reader = Cursor::new(source);
265 let mut rr = ResetRead::new(reader);
266
267 let mut buf5 = [0u8; 5];
269 let n = rr.read(&mut buf5)?;
270 assert_eq!(n, 5);
271 assert_eq!(buf5, [0x00, 0x00, 0x00, 0x00, 0x01]);
272
273 rr.reset();
275
276 let mut buf4 = [0u8; 4];
278 let n = rr.read(&mut buf4)?;
279 assert_eq!(n, 4);
280 assert_eq!(buf4, [0x00, 0x00, 0x00, 0x00]);
281
282 let mut buf4 = [0u8; 4];
284 let n = rr.read(&mut buf4)?;
285 assert_eq!(n, 4);
286 assert_eq!(buf4, [0x01, 0x02, 0x03, 0x04]);
287
288 let mut buf3 = [0u8; 3];
290 let n = rr.read(&mut buf3)?;
291 assert_eq!(n, 3);
292 assert_eq!(buf3, [0x05, 0x06, 0x07]);
293
294 let mut buf1 = [0u8; 1];
296 let n = rr.read(&mut buf1)?;
297 assert_eq!(n, 1);
298 assert_eq!(buf1, [0x08]);
299
300 Ok(())
301 }
302}