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
8#[derive(thiserror::Error, Debug)]
9#[allow(clippy::enum_variant_names)]
10pub enum Error {
11 #[error("error decoding XDR: {0}")]
12 ReadXdr(#[from] crate::Error),
13 #[error("error reading file: {0}")]
14 ReadFile(std::io::Error),
15 #[error("error writing output: {0}")]
16 WriteOutput(std::io::Error),
17}
18
19#[derive(Args, Debug, Clone)]
20#[command()]
21pub struct Cmd {
22 #[arg()]
24 pub input: Option<OsString>,
25
26 #[arg(long = "input", value_enum, default_value_t)]
28 pub input_format: InputFormat,
29
30 #[arg(long = "output", value_enum, default_value_t)]
32 pub output_format: OutputFormat,
33
34 #[arg(long, default_value = "2")]
36 pub certainty: usize,
37}
38
39#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
40pub enum InputFormat {
41 Single,
42 SingleBase64,
43 Stream,
44 StreamBase64,
45 StreamFramed,
46}
47
48impl Default for InputFormat {
49 fn default() -> Self {
50 Self::SingleBase64
51 }
52}
53
54#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
55pub enum OutputFormat {
56 List,
57}
58
59impl Default for OutputFormat {
60 fn default() -> Self {
61 Self::List
62 }
63}
64
65macro_rules! run_x {
68 ($f:ident) => {
69 fn $f(&self) -> Result<(), Error> {
70 let mut rr = ResetRead::new(self.input()?);
71 let mut guessed = false;
72 'variants: for v in crate::TypeVariant::VARIANTS {
73 rr.reset();
74 let count: usize = match self.input_format {
75 InputFormat::Single => {
76 let mut l = crate::Limited::new(&mut rr, crate::Limits::none());
77 crate::Type::read_xdr_to_end(v, &mut l)
78 .ok()
79 .map(|_| 1)
80 .unwrap_or_default()
81 }
82 InputFormat::SingleBase64 => {
83 let mut l = crate::Limited::new(&mut rr, crate::Limits::none());
84 crate::Type::read_xdr_base64_to_end(v, &mut l)
85 .ok()
86 .map(|_| 1)
87 .unwrap_or_default()
88 }
89 InputFormat::Stream => {
90 let mut l = crate::Limited::new(&mut rr, crate::Limits::none());
91 let iter = crate::Type::read_xdr_iter(v, &mut l);
92 let iter = iter.take(self.certainty);
93 let mut count = 0;
94 for v in iter {
95 match v {
96 Ok(_) => count += 1,
97 Err(_) => continue 'variants,
98 }
99 }
100 count
101 }
102 InputFormat::StreamBase64 => {
103 let mut l = crate::Limited::new(&mut rr, crate::Limits::none());
104 let iter = crate::Type::read_xdr_base64_iter(v, &mut l);
105 let iter = iter.take(self.certainty);
106 let mut count = 0;
107 for v in iter {
108 match v {
109 Ok(_) => count += 1,
110 Err(_) => continue 'variants,
111 }
112 }
113 count
114 }
115 InputFormat::StreamFramed => {
116 let mut l = crate::Limited::new(&mut rr, crate::Limits::none());
117 let iter = crate::Type::read_xdr_framed_iter(v, &mut l);
118 let iter = iter.take(self.certainty);
119 let mut count = 0;
120 for v in iter {
121 match v {
122 Ok(_) => count += 1,
123 Err(_) => continue 'variants,
124 }
125 }
126 count
127 }
128 };
129 if count > 0 {
130 writeln!(stdout(), "{}", v.name()).map_err(Error::WriteOutput)?;
131 guessed = true;
132 }
133 }
134 if (!guessed) {
135 std::process::exit(1);
136 }
137 Ok(())
138 }
139 };
140}
141
142impl Cmd {
143 pub fn run(&self) -> Result<(), Error> {
149 let result = self.run_inner();
150 match result {
151 Ok(()) => Ok(()),
152 Err(Error::WriteOutput(e)) if e.kind() == std::io::ErrorKind::BrokenPipe => Ok(()),
153 Err(e) => Err(e),
154 }
155 }
156
157 run_x!(run_inner);
158
159 fn input(&self) -> Result<Box<dyn Read>, Error> {
160 if let Some(input) = &self.input {
161 let exist = Path::new(input).try_exists();
162 if let Ok(true) = exist {
163 Ok(Box::new(File::open(input).map_err(Error::ReadFile)?))
164 } else {
165 Ok(Box::new(Cursor::new(input.clone().into_encoded_bytes())))
166 }
167 } else {
168 Ok(Box::new(stdin()))
169 }
170 }
171}
172
173struct ResetRead<R: Read> {
174 read: R,
175 buf: Vec<u8>,
176 cursor: usize,
177}
178
179impl<R: Read> ResetRead<R> {
180 fn new(r: R) -> Self {
181 Self {
182 read: r,
183 buf: Vec::new(),
184 cursor: 0,
185 }
186 }
187
188 fn reset(&mut self) {
189 self.cursor = 0;
190 }
191}
192
193impl<R: Read> Read for ResetRead<R> {
194 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
195 let n = cmp::min(self.buf.len() - self.cursor, buf.len());
197 buf[..n].copy_from_slice(&self.buf[self.cursor..self.cursor + n]);
198 if n < buf.len() {
200 let read_n = self.read.read(&mut buf[n..])?;
201 self.buf.extend_from_slice(&buf[n..n + read_n]);
202 self.cursor += n + read_n;
203 Ok(n + read_n)
204 } else {
205 self.cursor += n;
206 Ok(n)
207 }
208 }
209}
210
211#[cfg(test)]
212mod test {
213 use std::{
214 error,
215 io::{Cursor, Read},
216 };
217
218 use super::ResetRead;
219
220 #[test]
221 fn test_reset_read() -> Result<(), Box<dyn error::Error>> {
222 let source: Vec<u8> = (0..8).collect();
223 let reader = Cursor::new(source);
224 let mut rr = ResetRead::new(reader);
225
226 let mut buf = [0u8; 4];
227 let n = rr.read(&mut buf)?;
228 assert_eq!(n, 4);
229 assert_eq!(buf, [0, 1, 2, 3]);
230
231 let mut buf = [0u8; 4];
232 let n = rr.read(&mut buf)?;
233 assert_eq!(n, 4);
234 assert_eq!(buf, [4, 5, 6, 7]);
235
236 let n = rr.read(&mut buf)?;
237 assert_eq!(n, 0);
238
239 rr.reset();
240 let mut buf = [0u8; 4];
241 let n = rr.read(&mut buf)?;
242 assert_eq!(n, 4);
243 assert_eq!(buf, [0, 1, 2, 3]);
244
245 Ok(())
246 }
247
248 #[test]
252 fn test_reset_read_partial_cache_overlap() -> Result<(), Box<dyn error::Error>> {
253 let source: Vec<u8> = vec![
255 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
256 ];
257 let reader = Cursor::new(source);
258 let mut rr = ResetRead::new(reader);
259
260 let mut buf5 = [0u8; 5];
262 let n = rr.read(&mut buf5)?;
263 assert_eq!(n, 5);
264 assert_eq!(buf5, [0x00, 0x00, 0x00, 0x00, 0x01]);
265
266 rr.reset();
268
269 let mut buf4 = [0u8; 4];
271 let n = rr.read(&mut buf4)?;
272 assert_eq!(n, 4);
273 assert_eq!(buf4, [0x00, 0x00, 0x00, 0x00]);
274
275 let mut buf4 = [0u8; 4];
277 let n = rr.read(&mut buf4)?;
278 assert_eq!(n, 4);
279 assert_eq!(buf4, [0x01, 0x02, 0x03, 0x04]);
280
281 let mut buf3 = [0u8; 3];
283 let n = rr.read(&mut buf3)?;
284 assert_eq!(n, 3);
285 assert_eq!(buf3, [0x05, 0x06, 0x07]);
286
287 let mut buf1 = [0u8; 1];
289 let n = rr.read(&mut buf1)?;
290 assert_eq!(n, 1);
291 assert_eq!(buf1, [0x08]);
292
293 Ok(())
294 }
295}