1use bpaf::*;
4
5mod boilerplate {
6 use super::*;
7 pub trait ExtraParsers<T> {
8 fn my_last(self) -> ParseLast<T>;
11 }
12
13 impl<T> Parser<T> for ParseLast<T> {
14 fn eval(&self, args: &mut State) -> Result<T, Error> {
15 self.inner.eval(args)
16 }
17
18 fn meta(&self) -> Meta {
19 self.inner.meta()
20 }
21 }
22
23 pub struct ParseLast<T> {
24 inner: Box<dyn Parser<T>>,
25 }
26
27 impl<T, P> ExtraParsers<T> for P
28 where
29 P: Parser<T> + 'static,
30 T: 'static,
31 {
32 fn my_last(self) -> ParseLast<T> {
33 let p = self
34 .some("need to specify at least once")
35 .map(|mut xs| xs.pop().unwrap());
36 ParseLast { inner: p.boxed() }
37 }
38 }
39}
40pub mod shared {
41 use super::boilerplate::*;
42 use bpaf::*;
43
44 #[derive(Debug, Clone, Copy, Bpaf)]
45 pub enum Verbosity {
46 #[bpaf(short, long)]
48 Warn,
49 #[bpaf(short, long)]
51 Quiet,
52 #[bpaf(short, long)]
54 Status,
55 }
56
57 pub fn parse_verbosity() -> impl Parser<Verbosity> {
58 verbosity().my_last().fallback(Verbosity::Status)
59 }
60
61 pub fn parse_binary() -> impl Parser<bool> {
62 #[derive(Debug, Clone, Copy, Bpaf, Eq, PartialEq)]
63 enum Mode {
64 #[bpaf(short, long)]
66 Binary,
67 #[bpaf(short, long)]
69 Text,
70 }
71 mode()
72 .last()
73 .fallback(Mode::Text)
74 .debug_fallback()
75 .map(|mode| mode == Mode::Binary)
76 }
77}
78
79mod arch {
80 use bpaf::*;
81
82 #[derive(Debug, Clone, Bpaf)]
83 #[bpaf(command)]
84 pub struct Arch;
86}
87
88mod b2sum {
89 use super::shared::*;
90 use bpaf::*;
91 use std::path::PathBuf;
92
93 #[derive(Debug, Clone, Bpaf)]
94 #[bpaf(command("b2sum"))]
95 pub struct B2Sum {
97 #[bpaf(external(parse_binary))]
98 pub binary: bool,
99
100 #[bpaf(short, long)]
102 pub check: bool,
103
104 pub tag: bool,
106
107 #[bpaf(external(parse_verbosity))]
108 pub check_output: Verbosity,
109
110 pub strict: bool,
112
113 #[bpaf(positional("FILE"))]
114 pub files: Vec<PathBuf>,
115 }
116}
117
118mod base32 {
119 use bpaf::*;
120 use std::path::PathBuf;
121
122 fn non_zero(val: Option<usize>) -> Option<usize> {
123 val.and_then(|v| (v > 0).then_some(v))
124 }
125
126 #[derive(Debug, Clone, Bpaf)]
127 #[bpaf(command)]
128 pub struct Base32 {
130 #[bpaf(long, short)]
132 pub decode: bool,
133 #[bpaf(long, short)]
134 pub ignore_garbage: bool,
136
137 #[bpaf(
138 long,
139 short,
140 argument("COLS"),
141 optional,
142 map(non_zero),
143 fallback(Some(76)),
144 debug_fallback
145 )]
146 pub wrap: Option<usize>,
149
150 #[bpaf(positional("FILE"))]
151 pub file: Option<PathBuf>,
153 }
154}
155
156mod basename {
157 use bpaf::*;
158
159 #[derive(Debug, Clone, Bpaf)]
160 #[bpaf(command)]
161 pub struct Basename {
162 #[bpaf(short('a'), long)]
164 pub multiple: bool,
165
166 #[bpaf(short, long, argument("SUFFIX"), optional)]
168 pub suffix: Option<String>,
169
170 #[bpaf(short, long)]
172 pub zero: bool,
173
174 #[bpaf(positional("NAME"), many)]
176 pub names: Vec<String>,
177 }
178
179 pub fn parse_basename() -> impl Parser<Basename> {
180 basename().map(|mut b| {
181 if b.suffix.is_some() {
182 b.multiple = true;
183 }
184 b
185 })
186 }
187}
188
189mod cat {
190 use std::path::PathBuf;
191
192 use bpaf::*;
193
194 #[derive(Debug, Clone, Bpaf)]
195 struct Extra {
196 #[bpaf(short('A'), long)]
197 show_all: bool,
199
200 #[bpaf(short('b'), long)]
201 number_nonblank: bool,
203
204 #[bpaf(short('e'))]
205 show_non_printing_ends: bool,
207 }
208
209 #[derive(Debug, Clone, Bpaf)]
210 #[bpaf(fallback(NumberingMode::None))]
211 pub enum NumberingMode {
212 #[bpaf(hide)]
213 None,
215
216 #[bpaf(short('b'), long("number-nonblank"))]
218 NonEmpty,
219
220 #[bpaf(short('n'), long("number"))]
222 All,
223 }
224
225 #[derive(Debug, Clone, Bpaf)]
226 pub struct Cat {
227 #[bpaf(short('T'), long)]
228 pub show_tabs: bool,
230
231 #[bpaf(short('E'))]
233 pub show_ends: bool,
234
235 #[bpaf(short('n'), long("number"))]
237 show_nonprinting: bool,
238
239 #[bpaf(external(numbering_mode))]
240 pub number: NumberingMode,
241
242 #[bpaf(short('s'), long)]
243 pub squeeze_blank: bool,
245
246 #[bpaf(positional("FILE"), many)]
247 pub files: Vec<PathBuf>,
249 }
250
251 pub fn parse_cat() -> impl Parser<Cat> {
252 construct!(extra(), cat())
253 .map(|(extra, mut cat)| {
254 if extra.show_all {
255 cat.show_tabs = true;
256 cat.show_ends = true;
257 cat.show_nonprinting = true;
258 }
259 if extra.show_non_printing_ends {
260 cat.show_nonprinting = true;
261 cat.show_ends = true;
262 }
263 if extra.number_nonblank {
264 cat.number = NumberingMode::NonEmpty;
265 }
266 cat
267 })
268 .to_options()
269 .command("cat")
270 }
271}
272
273#[derive(Debug, Clone, Bpaf)]
274#[bpaf(options)]
275pub enum Options {
276 Arch(#[bpaf(external(arch::arch))] arch::Arch),
277 B2Sum(#[bpaf(external(b2sum::b2_sum))] b2sum::B2Sum),
278 Base32(#[bpaf(external(base32::base32))] base32::Base32),
279 Basename(#[bpaf(external(basename::parse_basename))] basename::Basename),
280 Cat(#[bpaf(external(cat::parse_cat))] cat::Cat),
281}
282
283fn main() {
284 let parser = options();
285
286 println!("{:?}", parser.run());
287}