1use crate::{buffer::Doc, item::Item};
2
3#[doc(hidden)]
4#[derive(Clone, Debug, Default)]
5pub enum Meta {
6 And(Vec<Meta>),
8 Or(Vec<Meta>),
10 Optional(Box<Meta>),
12 Required(Box<Meta>),
14 Adjacent(Box<Meta>),
16 Item(Box<Item>),
18 Many(Box<Meta>),
20 Subsection(Box<Meta>, Box<Doc>),
24 Suffix(Box<Meta>, Box<Doc>),
26 #[default]
28 Skip,
29 CustomUsage(Box<Meta>, Box<Doc>),
31 Strict(Box<Meta>),
33}
34
35#[derive(Debug, Clone, Copy)]
42enum StrictNorm {
43 Pull,
45 Push,
46 Strip,
48}
49
50impl StrictNorm {
51 fn push(&mut self) {
52 match *self {
53 StrictNorm::Pull => *self = StrictNorm::Push,
54 StrictNorm::Push | StrictNorm::Strip => {}
55 }
56 }
57}
58
59impl Meta {
60 fn is_command(&self) -> bool {
63 match self {
64 Meta::Item(i) => matches!(i.as_ref(), Item::Command { .. }),
65 Meta::Subsection(m, _) => m.is_command(),
66 _ => false,
67 }
68 }
69
70 pub(crate) fn positional_invariant_check(&self, verbose: bool) {
72 fn go(meta: &Meta, is_pos: &mut bool, v: bool) {
73 match meta {
74 Meta::And(xs) => {
75 for x in xs {
76 go(x, is_pos, v);
77 }
78 }
79 Meta::Or(xs) => {
80 let mut out = *is_pos;
81 for x in xs {
82 let mut this_pos = *is_pos;
83 go(x, &mut this_pos, v);
84 out |= this_pos;
85 }
86 *is_pos = out;
87 }
88 Meta::Item(i) => {
89 match (*is_pos, i.is_pos()) {
90 (true, true) | (false, false) => {}
91 (true, false) => {
92 panic!("bpaf usage BUG: all positional and command items must be placed in the right \
93 most position of the structure or tuple they are in but {:?} breaks this rule. \
94 See bpaf documentation for `positional` for details.", i);
95 }
96 (false, true) => {
97 *is_pos = true;
98 }
99 }
100 if let Item::Command { meta, .. } = &**i {
101 let mut command_pos = false;
102 if v {
103 println!("Checking\n{:#?}", meta);
104 }
105 go(meta, &mut command_pos, v);
106 }
107 }
108 Meta::Adjacent(m) => {
109 if let Some(i) = Meta::first_item(m) {
110 if i.is_pos() {
111 go(m, is_pos, v);
112 } else {
113 let mut inner = false;
114 go(m, &mut inner, v);
115 }
116 }
117 }
118 Meta::Optional(m)
119 | Meta::Required(m)
120 | Meta::Many(m)
121 | Meta::CustomUsage(m, _)
122 | Meta::Subsection(m, _)
123 | Meta::Strict(m)
124 | Meta::Suffix(m, _) => go(m, is_pos, v),
125 Meta::Skip => {}
126 }
127 }
128 let mut is_pos = false;
129 if verbose {
130 println!("Checking\n{:#?}", self);
131 }
132 go(self, &mut is_pos, verbose);
133 }
134
135 pub(crate) fn normalized(&self, for_usage: bool) -> Meta {
136 let mut m = self.clone();
137 let mut norm = StrictNorm::Pull;
138 m.normalize(for_usage, &mut norm);
139 if let Meta::Required(i) = m {
141 m = *i;
142 }
143 if matches!(m, Meta::Or(_)) {
144 m = Meta::Required(Box::new(m));
145 }
146 if matches!(norm, StrictNorm::Push) {
147 m = Meta::Strict(Box::new(m));
148 }
149 m
150 }
151
152 pub(crate) fn first_item(meta: &Meta) -> Option<&Item> {
154 match meta {
155 Meta::And(xs) => xs.first().and_then(Self::first_item),
156 Meta::Item(item) => Some(item),
157 Meta::Skip | Meta::Or(_) => None,
158 Meta::Optional(x)
159 | Meta::Strict(x)
160 | Meta::Required(x)
161 | Meta::Adjacent(x)
162 | Meta::Many(x)
163 | Meta::Subsection(x, _)
164 | Meta::Suffix(x, _)
165 | Meta::CustomUsage(x, _) => Self::first_item(x),
166 }
167 }
168
169 fn normalize(&mut self, for_usage: bool, norm: &mut StrictNorm) {
171 fn normalize_vec(
172 xs: &mut Vec<Meta>,
173 for_usage: bool,
174 norm: &mut StrictNorm,
175 or: bool,
176 ) -> Option<Meta> {
177 let mut final_norm = *norm;
178 for m in xs.iter_mut() {
179 let mut this_norm = *norm;
180 m.normalize(for_usage, &mut this_norm);
181 let target: &mut StrictNorm = if or { &mut final_norm } else { norm };
182
183 match (*target, this_norm) {
184 (_, StrictNorm::Pull) | (StrictNorm::Strip, _) => {}
185 (StrictNorm::Pull, StrictNorm::Push) => {
186 *m = Meta::Strict(Box::new(std::mem::take(m)));
187 *target = StrictNorm::Strip;
188 }
189 _ => {
190 *target = this_norm;
191 }
192 }
193 }
194 xs.retain(|m| !matches!(m, Meta::Skip));
195
196 *norm = final_norm;
197
198 match xs.len() {
199 0 => Some(Meta::Skip),
200 1 => Some(xs.remove(0)),
201 _ => None,
202 }
203 }
204
205 match self {
206 Meta::And(xs) => {
207 if let Some(replacement) = normalize_vec(xs, for_usage, norm, false) {
208 *self = replacement;
209 }
210 }
211 Meta::Or(xs) => {
213 if let Some(replacement) = normalize_vec(xs, for_usage, norm, true) {
214 *self = replacement;
215 } else {
216 let mut saw_cmd = false;
217 xs.retain(|m| {
219 let is_cmd = m.is_command();
220 let keep = !(is_cmd && saw_cmd);
221 saw_cmd |= is_cmd;
222 keep
223 });
224 match xs.len() {
225 0 => *self = Meta::Skip,
226 1 => *self = xs.remove(0),
227 _ => *self = Meta::Required(Box::new(std::mem::take(self))),
228 }
229 }
230 }
231 Meta::Optional(m) => {
232 m.normalize(for_usage, norm);
233 if matches!(**m, Meta::Skip) {
234 *self = Meta::Skip;
236 } else if let Meta::Required(mm) | Meta::Optional(mm) = m.as_mut() {
237 *m = std::mem::take(mm);
240 } else if let Meta::Many(many) = m.as_mut() {
241 if let Meta::Required(x) = many.as_mut() {
243 *self = Meta::Many(Box::new(Meta::Optional(std::mem::take(x))));
244 }
245 }
246 }
247 Meta::Required(m) => {
248 m.normalize(for_usage, norm);
249 if matches!(**m, Meta::Skip) {
250 *self = Meta::Skip;
252 } else if matches!(**m, Meta::And(_) | Meta::Or(_)) {
253 } else {
255 *self = std::mem::take(m);
257 }
258 }
259 Meta::Many(m) => {
260 m.normalize(for_usage, norm);
261 if matches!(**m, Meta::Skip) {
262 *self = Meta::Skip;
263 }
264 }
265 Meta::Adjacent(m) | Meta::Subsection(m, _) | Meta::Suffix(m, _) => {
266 m.normalize(for_usage, norm);
267 *self = std::mem::take(m);
268 }
269 Meta::Item(i) => i.normalize(for_usage),
270 Meta::Skip => {
271 }
273 Meta::CustomUsage(m, u) => {
274 m.normalize(for_usage, norm);
275 if for_usage {
277 if u.is_empty() {
278 *self = Meta::Skip;
279 }
280 } else {
281 *self = std::mem::take(m);
282 }
283 }
284 Meta::Strict(m) => {
285 m.normalize(for_usage, norm);
286 norm.push();
287 *self = std::mem::take(m);
288 }
289 }
290 }
291}
292
293impl From<Item> for Meta {
294 fn from(value: Item) -> Self {
295 Meta::Item(Box::new(value))
296 }
297}
298
299impl Meta {
300 fn alts(self, to: &mut Vec<Meta>) {
301 match self {
302 Meta::Or(mut xs) => to.append(&mut xs),
303 Meta::Skip => {}
304 meta => to.push(meta),
305 }
306 }
307
308 pub(crate) fn or(self, other: Meta) -> Self {
309 let mut res = Vec::new();
310 self.alts(&mut res);
311 other.alts(&mut res);
312 match res.len() {
313 0 => Meta::Skip,
314 1 => res.remove(0),
315 _ => Meta::Or(res),
316 }
317 }
318
319 pub(crate) fn collect_shorts(&self, flags: &mut Vec<char>, args: &mut Vec<char>) {
321 match self {
322 Meta::And(xs) | Meta::Or(xs) => {
323 for x in xs {
324 x.collect_shorts(flags, args);
325 }
326 }
327 Meta::Item(m) => match &**m {
328 Item::Any { .. } | Item::Positional { .. } => {}
329 Item::Command { meta, .. } => {
330 meta.collect_shorts(flags, args);
331 }
332 Item::Flag { shorts, .. } => flags.extend(shorts),
333 Item::Argument { shorts, .. } => args.extend(shorts),
334 },
335 Meta::CustomUsage(m, _)
336 | Meta::Required(m)
337 | Meta::Optional(m)
338 | Meta::Adjacent(m)
339 | Meta::Subsection(m, _)
340 | Meta::Suffix(m, _)
341 | Meta::Many(m) => {
342 m.collect_shorts(flags, args);
343 }
344 Meta::Skip | Meta::Strict(_) => {}
345 }
346 }
347}