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 #[arg()]
28 pub file: Option<PathBuf>,
29
30 #[arg(long, value_enum, default_value_t)]
32 pub input: InputFormat,
33
34 #[arg(long, value_enum, default_value_t)]
36 pub output: 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.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 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 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 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}