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(Default, Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
40pub enum InputFormat {
41 Single,
42 #[default]
43 SingleBase64,
44 Stream,
45 StreamBase64,
46 StreamFramed,
47}
48
49#[derive(Default, Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
50pub enum OutputFormat {
51 #[default]
52 List,
53}
54
55macro_rules! run_x {
58 ($f:ident) => {
59 fn $f(&self) -> Result<(), Error> {
60 let mut rr = ResetRead::new(self.input()?);
61 let mut guessed = false;
62 'variants: for v in crate::TypeVariant::VARIANTS {
63 rr.reset();
64 let count: usize = match self.input_format {
65 InputFormat::Single => {
66 let mut l = crate::Limited::new(&mut rr, crate::Limits::none());
67 crate::Type::read_xdr_to_end(v, &mut l)
68 .ok()
69 .map(|_| 1)
70 .unwrap_or_default()
71 }
72 InputFormat::SingleBase64 => {
73 let mut l = crate::Limited::new(&mut rr, crate::Limits::none());
74 crate::Type::read_xdr_base64_to_end(v, &mut l)
75 .ok()
76 .map(|_| 1)
77 .unwrap_or_default()
78 }
79 InputFormat::Stream => {
80 let mut l = crate::Limited::new(&mut rr, crate::Limits::none());
81 let iter = crate::Type::read_xdr_iter(v, &mut l);
82 let iter = iter.take(self.certainty);
83 let mut count = 0;
84 for v in iter {
85 match v {
86 Ok(_) => count += 1,
87 Err(_) => continue 'variants,
88 }
89 }
90 count
91 }
92 InputFormat::StreamBase64 => {
93 let mut l = crate::Limited::new(&mut rr, crate::Limits::none());
94 let iter = crate::Type::read_xdr_base64_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::StreamFramed => {
106 let mut l = crate::Limited::new(&mut rr, crate::Limits::none());
107 let iter = crate::Type::read_xdr_framed_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 };
119 if count > 0 {
120 writeln!(stdout(), "{}", v.name()).map_err(Error::WriteOutput)?;
121 guessed = true;
122 }
123 }
124 if (!guessed) {
125 std::process::exit(1);
126 }
127 Ok(())
128 }
129 };
130}
131
132impl Cmd {
133 pub fn run(&self) -> Result<(), Error> {
139 let result = self.run_inner();
140 match result {
141 Ok(()) => Ok(()),
142 Err(Error::WriteOutput(e)) if e.kind() == std::io::ErrorKind::BrokenPipe => Ok(()),
143 Err(e) => Err(e),
144 }
145 }
146
147 run_x!(run_inner);
148
149 fn input(&self) -> Result<Box<dyn Read>, Error> {
150 if let Some(input) = &self.input {
151 let exist = Path::new(input).try_exists();
152 if let Ok(true) = exist {
153 Ok(Box::new(File::open(input).map_err(Error::ReadFile)?))
154 } else {
155 Ok(Box::new(Cursor::new(input.clone().into_encoded_bytes())))
156 }
157 } else {
158 Ok(Box::new(stdin()))
159 }
160 }
161}
162
163struct ResetRead<R: Read> {
164 read: R,
165 buf: Vec<u8>,
166 cursor: usize,
167}
168
169impl<R: Read> ResetRead<R> {
170 fn new(r: R) -> Self {
171 Self {
172 read: r,
173 buf: Vec::new(),
174 cursor: 0,
175 }
176 }
177
178 fn reset(&mut self) {
179 self.cursor = 0;
180 }
181}
182
183impl<R: Read> Read for ResetRead<R> {
184 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
185 let n = cmp::min(self.buf.len() - self.cursor, buf.len());
187 buf[..n].copy_from_slice(&self.buf[self.cursor..self.cursor + n]);
188 if n < buf.len() {
190 let read_n = self.read.read(&mut buf[n..])?;
191 self.buf.extend_from_slice(&buf[n..n + read_n]);
192 self.cursor += n + read_n;
193 Ok(n + read_n)
194 } else {
195 self.cursor += n;
196 Ok(n)
197 }
198 }
199}
200
201#[cfg(test)]
202mod test {
203 use std::{
204 error,
205 io::{Cursor, Read},
206 };
207
208 use super::ResetRead;
209
210 #[test]
211 fn test_reset_read() -> Result<(), Box<dyn error::Error>> {
212 let source: Vec<u8> = (0..8).collect();
213 let reader = Cursor::new(source);
214 let mut rr = ResetRead::new(reader);
215
216 let mut buf = [0u8; 4];
217 let n = rr.read(&mut buf)?;
218 assert_eq!(n, 4);
219 assert_eq!(buf, [0, 1, 2, 3]);
220
221 let mut buf = [0u8; 4];
222 let n = rr.read(&mut buf)?;
223 assert_eq!(n, 4);
224 assert_eq!(buf, [4, 5, 6, 7]);
225
226 let n = rr.read(&mut buf)?;
227 assert_eq!(n, 0);
228
229 rr.reset();
230 let mut buf = [0u8; 4];
231 let n = rr.read(&mut buf)?;
232 assert_eq!(n, 4);
233 assert_eq!(buf, [0, 1, 2, 3]);
234
235 Ok(())
236 }
237
238 #[test]
242 fn test_reset_read_partial_cache_overlap() -> Result<(), Box<dyn error::Error>> {
243 let source: Vec<u8> = vec![
245 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
246 ];
247 let reader = Cursor::new(source);
248 let mut rr = ResetRead::new(reader);
249
250 let mut buf5 = [0u8; 5];
252 let n = rr.read(&mut buf5)?;
253 assert_eq!(n, 5);
254 assert_eq!(buf5, [0x00, 0x00, 0x00, 0x00, 0x01]);
255
256 rr.reset();
258
259 let mut buf4 = [0u8; 4];
261 let n = rr.read(&mut buf4)?;
262 assert_eq!(n, 4);
263 assert_eq!(buf4, [0x00, 0x00, 0x00, 0x00]);
264
265 let mut buf4 = [0u8; 4];
267 let n = rr.read(&mut buf4)?;
268 assert_eq!(n, 4);
269 assert_eq!(buf4, [0x01, 0x02, 0x03, 0x04]);
270
271 let mut buf3 = [0u8; 3];
273 let n = rr.read(&mut buf3)?;
274 assert_eq!(n, 3);
275 assert_eq!(buf3, [0x05, 0x06, 0x07]);
276
277 let mut buf1 = [0u8; 1];
279 let n = rr.read(&mut buf1)?;
280 assert_eq!(n, 1);
281 assert_eq!(buf1, [0x08]);
282
283 Ok(())
284 }
285}