1use std::borrow::Cow;
2use std::io::Write;
3use std::marker::PhantomData;
4
5use crate::block::Block;
6use crate::cmd::Command;
7use crate::store::Store;
8use crate::style::Style;
9use crate::wrapper::Wrapper;
10use crate::AppHelp;
11use crate::HelpPolicy;
12
13pub struct DefaultPolicy<'a, I> {
20 name: Cow<'a, str>,
21
22 style: Style,
23
24 styles: Vec<Style>,
25
26 max_width: usize,
27
28 hiding_pos: bool,
29
30 usage_new_line: usize,
31
32 marker: PhantomData<&'a I>,
33}
34
35impl<I> Default for DefaultPolicy<'_, I> {
36 fn default() -> Self {
37 Self {
38 name: Default::default(),
39 style: Default::default(),
40 styles: Default::default(),
41 max_width: 0,
42 hiding_pos: true,
43 usage_new_line: 0,
44 marker: Default::default(),
45 }
46 }
47}
48
49impl<'a, I> DefaultPolicy<'a, I> {
50 pub fn new<S: Into<Cow<'a, str>>>(
51 name: S,
52 style: Style,
53 block: Vec<Style>,
54 max_width: usize,
55 hiding_pos: bool,
56 usage_new_line: usize,
57 ) -> Self {
58 Self {
59 name: name.into(),
60 style,
61 styles: block,
62 max_width,
63 hiding_pos,
64 usage_new_line,
65 marker: PhantomData,
66 }
67 }
68}
69
70impl<'a> DefaultPolicy<'a, Command<'a>> {
71 pub fn get_block_usage(
72 &self,
73 item: &Block<'a, Cow<'a, str>>,
74 stores: &[Store<'a>],
75 ) -> (Vec<String>, Vec<String>) {
76 let mut usages = vec![];
77 let mut args = vec![];
78
79 for store in item.iter() {
80 if let Some(store) = stores.iter().find(|v| &v.name() == store) {
81 let hint = store.hint();
82
83 if !hint.is_empty() {
84 if store.position() {
85 if store.optional() {
86 args.push(format!("[{}]", hint));
87 } else {
88 args.push(format!("<{}>", hint));
89 }
90 } else if store.optional() {
91 usages.push(format!("[{}]", hint));
92 } else {
93 usages.push(format!("<{}>", hint));
94 }
95 }
96 }
97 }
98 (usages, args)
99 }
100
101 pub fn get_command_usage(&self, item: &Command<'a>) -> Cow<'a, str> {
102 let mut usages = vec![];
103 let mut args = vec![];
104 let mut block_hint = vec![];
105
106 for block in item.block() {
107 let (block_usages, mut block_args) = self.get_block_usage(block, item);
108
109 if !block_usages.is_empty() {
110 for mut usage in block_usages {
111 if self.usage_new_line > 0 && (usages.len() + 1) % self.usage_new_line == 0 {
112 usage.push_str("\n ");
115 }
116 usages.push(usage);
117 }
118 }
119 if !block_args.is_empty() {
121 args.append(&mut block_args);
122 }
123 }
124 for block in item.block() {
125 if !block.is_empty() {
126 let arg = block.hint();
127
128 if !arg.is_empty() {
129 block_hint.push(arg);
130 }
131 }
132 }
133
134 let mut ret = String::from("Usage: ");
135 let usage = usages.join(" ");
136 let block_hint = block_hint.join(" ");
137 let args = args.join(" ");
138
139 if !self.name.is_empty() {
140 ret += &self.name;
141 ret += " ";
142 }
143 if !item.name().is_empty() {
144 ret += &item.name();
145 ret += " ";
146 }
147 if !usage.is_empty() {
148 ret += &usage;
149 ret += " ";
150 }
151 if self.hiding_pos {
152 if !block_hint.is_empty() {
153 ret += &block_hint;
154 ret += " ";
155 }
156 } else if !args.is_empty() {
157 ret += &args;
158 ret += " ";
159 }
160 ret.into()
161 }
162
163 pub fn get_block_help(
164 &self,
165 item: &Block<'a, Cow<'a, str>>,
166 stores: &Vec<Store<'a>>,
167 ) -> Cow<'a, str> {
168 let style = &self.style;
169 let count = item.len();
170 let head = item.head();
171 let foot = item.foot();
172 let line_spacing = &"\n".repeat(1 + style.line_spacing);
173 let mut output = if head.is_empty() { vec![] } else { vec![head] };
174 let mut data: Vec<Vec<Cow<'a, str>>> = vec![vec![]; count];
175 let blocks = item.as_slice();
176 let styles = &self.styles;
177 let mut any_filled = false;
178
179 for idx in 0..count {
180 for store in stores {
181 if store.name() == blocks[idx] {
182 let hint = store.hint();
183 let help = store.help();
184
185 if !hint.is_empty() {
186 data[idx].push(hint);
187 any_filled = true;
188 }
189 if !help.is_empty() {
190 data[idx].push(help);
191 any_filled = true;
192 }
193 }
194 }
195 }
196 if !any_filled {
197 return "".into();
198 }
199 let mut wrapper = Wrapper::new(&data);
200
201 if !styles.is_empty() {
202 wrapper.wrap_with(styles, self.max_width);
203 } else {
204 wrapper.wrap(self.max_width);
205 }
206 let wrapped = wrapper.get_output();
207 let mut wrapped_lines = vec![];
208
209 for wrapped_line in wrapped {
210 let max_len = wrapped_line.iter().map(|v| v.len()).max().unwrap_or(1);
211 let first_style = wrapped_line[0].get_style();
212 let first_row_spacing = &" ".repeat(first_style.row_spacing);
213 let first_line_spacing = &"\n".repeat(1 + first_style.line_spacing);
214
215 for i in 0..max_len {
216 let rows = &wrapped_line
217 .iter()
218 .map(|v| v.get_line(i))
219 .collect::<Vec<String>>();
220 let mut line = rows.join(first_row_spacing);
221
222 line.push_str(first_line_spacing);
223 wrapped_lines.push(line);
224 }
225 }
226 let wrapped_output = wrapped_lines.join("");
227
228 if !wrapped_output.is_empty() {
229 output.push(wrapped_output.into());
230 }
231 if !foot.is_empty() {
232 output.push(foot);
233 }
234 let output = output.join(line_spacing);
235 let output = output.trim_end().to_owned();
236
237 output.into()
238 }
239}
240
241impl<'a> HelpPolicy<'a, Command<'a>> for DefaultPolicy<'a, Command<'a>> {
242 fn format(&self, item: &Command<'a>) -> Option<Cow<'a, str>> {
243 let usage = self.get_command_usage(item);
244 let mut blocks = vec![usage];
245 let head = item.head();
246 let foot = item.foot();
247 let block_spacing = "\n".repeat(1 + self.style.block_spacing);
248
249 if !head.is_empty() {
250 blocks.push(head);
251 }
252 for block in item.block() {
253 if !block.is_empty() {
254 let help = self.get_block_help(block, item);
255
256 if !help.is_empty() {
257 blocks.push(help);
258 }
259 }
260 }
261 if !foot.is_empty() {
262 blocks.push(foot);
263 }
264 Some(blocks.join(&block_spacing).into())
265 }
266}
267
268pub struct DefaultAppPolicy<'a, I> {
269 styles: Vec<Style>, max_width: usize,
272
273 show_global: bool,
274
275 hiding_pos: bool,
276
277 usage_new_line: usize,
278
279 marker: PhantomData<&'a I>,
280}
281
282impl<I> Default for DefaultAppPolicy<'_, I> {
283 fn default() -> Self {
284 Self {
285 styles: Default::default(),
286 max_width: 0,
287 show_global: true,
288 hiding_pos: true,
289 usage_new_line: 0,
290 marker: Default::default(),
291 }
292 }
293}
294
295impl<I> DefaultAppPolicy<'_, I> {
296 pub fn new(
297 styles: Vec<Style>,
298 max_width: usize,
299 show_global: bool,
300 usage_new_line: usize,
301 ) -> Self {
302 Self {
303 styles,
304 max_width,
305 show_global,
306 hiding_pos: true,
307 usage_new_line,
308 marker: PhantomData,
309 }
310 }
311}
312
313impl<'a, W: Write> DefaultAppPolicy<'a, AppHelp<'a, W>> {
314 pub fn get_block_usage(
315 &self,
316 item: &Block<'a, Cow<'a, str>>,
317 stores: &[Store<'a>],
318 ) -> (Vec<String>, Vec<String>) {
319 let mut usages = vec![];
320 let mut args = vec![];
321
322 for store in item.iter() {
323 if let Some(store) = stores.iter().find(|v| &v.name() == store) {
324 let hint = store.hint();
325
326 if !hint.is_empty() {
327 if store.position() {
328 if store.optional() {
329 args.push(format!("[{}]", hint));
330 } else {
331 args.push(format!("<{}>", hint));
332 }
333 } else if store.optional() {
334 usages.push(format!("[{}]", hint));
335 } else {
336 usages.push(format!("<{}>", hint));
337 }
338 }
339 }
340 }
341 (usages, args)
342 }
343
344 pub fn get_app_usage(&self, app: &AppHelp<'a, W>) -> Cow<'a, str> {
345 let global = app.global();
346 let mut usages = vec![];
347 let mut args = vec![];
348 let mut block_hint = vec![];
349
350 for block in global.block() {
351 let (block_usages, mut block_args) = self.get_block_usage(block, global);
352
353 if !block_usages.is_empty() {
354 for mut usage in block_usages {
355 if self.usage_new_line > 0 && (usages.len() + 1) % self.usage_new_line == 0 {
356 usage.push_str("\n ");
359 }
360 usages.push(usage);
361 }
362 }
363 if !block_args.is_empty() {
365 args.append(&mut block_args);
366 }
367 }
368 for block in global.block() {
369 if !block.is_empty() {
370 let arg = block.hint();
371
372 if !arg.is_empty() {
373 block_hint.push(arg);
374 }
375 }
376 }
377
378 let mut ret = String::from("Usage: ");
379 let global_usage = usages.join(" ");
381 let block_hint = block_hint.join(" ");
382 let command_usage = if app.has_cmd() { "<COMMAND>" } else { "" };
383 let args = args.join(" ");
384
385 if !global.name().is_empty() {
386 ret += &global.name();
387 ret += " ";
388 }
389 if !global_usage.is_empty() {
390 ret += &global_usage;
391 ret += " ";
392 }
393 if !command_usage.is_empty() {
394 ret += command_usage;
395 ret += " ";
396 }
397 if self.hiding_pos {
398 if !block_hint.is_empty() {
399 ret += &block_hint;
400 ret += " ";
401 }
402 } else if !args.is_empty() {
403 ret += &args;
404 ret += " ";
405 }
406 ret.into()
407 }
408
409 pub fn get_block_help(
410 &self,
411 block: &Block<'a, Cow<'a, str>>,
412 app: &AppHelp<'a, W>,
413 ) -> Cow<'a, str> {
414 let head = block.head();
415 let foot = block.foot();
416 let count = block.len();
417 let mut data = vec![vec![]; count];
418 let styles = &self.styles;
419 let line_spacing = &"\n".repeat(1 + app.style().line_spacing);
420 let mut any_filled = false;
421
422 if block.is_empty() {
423 return "".into();
424 }
425 for (name, data_mut) in block.iter().zip(data.iter_mut()) {
426 if let Some(command) = app.find_cmd(name.clone()) {
427 let hint = command.hint();
428 let help = command.help();
429
430 if !hint.is_empty() {
431 data_mut.push(hint);
432 any_filled = true;
433 }
434 if !help.is_empty() {
435 data_mut.push(help);
436 any_filled = true;
437 }
438 } else {
439 panic!("Unknow command {} in block {}", name, block.name());
440 }
441 }
442 if !any_filled {
443 return "".into();
444 }
445 let mut wrapper = Wrapper::new(&data);
446
447 if !styles.is_empty() {
448 wrapper.wrap_with(styles, self.max_width);
449 } else {
450 wrapper.wrap(self.max_width);
451 }
452 let wrapped = wrapper.get_output();
453 let mut wrapped_lines = vec![];
454
455 for wrapped_line in wrapped {
456 let max_len = wrapped_line.iter().map(|v| v.len()).max().unwrap_or(1);
457 let first_style = wrapped_line[0].get_style();
458 let first_row_spacing = &" ".repeat(first_style.row_spacing);
459 let first_line_spacing = &"\n".repeat(1 + first_style.line_spacing);
460
461 for i in 0..max_len {
462 let rows = &wrapped_line
463 .iter()
464 .map(|v| v.get_line(i))
465 .collect::<Vec<String>>();
466 let mut line = rows.join(first_row_spacing);
467
468 line.push_str(first_line_spacing);
469 wrapped_lines.push(line);
470 }
471 }
472
473 if !wrapped_lines.is_empty() {
474 wrapped_lines.last_mut().unwrap().pop();
476 }
477
478 let mut usages = vec![];
479 let wrapped_output = wrapped_lines.join("");
480
481 if !head.is_empty() {
482 usages.push(head);
483 }
484 if !foot.is_empty() {
485 usages.push(foot);
486 }
487 if !wrapped_output.is_empty() {
488 usages.push(wrapped_output.into());
489 }
490
491 usages.join(line_spacing).into()
492 }
493
494 pub fn get_global_help(
495 &self,
496 item: &Block<'a, Cow<'a, str>>,
497 stores: &Vec<Store<'a>>,
498 app: &AppHelp<'a, W>,
499 ) -> Cow<'a, str> {
500 let style = &app.style;
501 let count = item.len();
502 let head = item.head();
503 let foot = item.foot();
504 let line_spacing = &"\n".repeat(1 + style.line_spacing);
505 let mut output = if head.is_empty() { vec![] } else { vec![head] };
506 let mut data: Vec<Vec<Cow<'a, str>>> = vec![vec![]; count];
507 let blocks = item.as_slice();
508 let styles = &self.styles;
509 let mut any_filled = false;
510
511 if item.is_empty() {
512 return "".into();
513 }
514 for idx in 0..count {
515 for store in stores {
516 if store.name() == blocks[idx] {
517 let hint = store.hint();
518 let help = store.help();
519
520 if !hint.is_empty() {
521 data[idx].push(hint);
522 any_filled = true;
523 }
524 if !help.is_empty() {
525 data[idx].push(help);
526 any_filled = true;
527 }
528 }
529 }
530 }
531 if !any_filled {
532 return "".into();
533 }
534 let mut wrapper = Wrapper::new(&data);
535
536 if !styles.is_empty() {
537 wrapper.wrap_with(styles, self.max_width);
538 } else {
539 wrapper.wrap(self.max_width);
540 }
541 let wrapped = wrapper.get_output();
542 let mut wrapped_lines = vec![];
543
544 for wrapped_line in wrapped {
545 let max_len = wrapped_line.iter().map(|v| v.len()).max().unwrap_or(1);
546 let first_style = wrapped_line[0].get_style();
547 let first_row_spacing = &" ".repeat(first_style.row_spacing);
548 let first_line_spacing = &"\n".repeat(1 + first_style.line_spacing);
549
550 for i in 0..max_len {
551 let rows = &wrapped_line
552 .iter()
553 .map(|v| v.get_line(i))
554 .collect::<Vec<String>>();
555 let mut line = rows.join(first_row_spacing);
556
557 line.push_str(first_line_spacing);
558 wrapped_lines.push(line);
559 }
560 }
561 let wrapped_output = wrapped_lines.join("");
562
563 if !wrapped_output.is_empty() {
564 output.push(wrapped_output.into());
565 }
566 if !foot.is_empty() {
567 output.push(foot);
568 }
569 let output = output.join(line_spacing);
570 let output = output.trim_end().to_owned();
571
572 output.into()
573 }
574}
575
576impl<'a, W: Write> HelpPolicy<'a, AppHelp<'a, W>> for DefaultAppPolicy<'a, AppHelp<'a, W>> {
577 fn format(&self, app: &AppHelp<'a, W>) -> Option<Cow<'a, str>> {
578 let usage = self.get_app_usage(app);
579 let head = app.head();
580 let foot = app.foot();
581 let block_spacing = "\n".repeat(1 + app.style().block_spacing);
582 let mut usages = if usage.is_empty() {
583 vec![]
584 } else {
585 vec![usage]
586 };
587
588 if !head.is_empty() {
589 usages.push(head);
590 }
591 for block in app.block().iter() {
592 let block_help = self.get_block_help(block, app);
593
594 if !block_help.is_empty() {
595 usages.push(block_help);
596 }
597 }
598 if self.show_global {
599 for block in app.global().block() {
600 let global_help = self.get_global_help(block, app.global(), app);
601
602 if !global_help.is_empty() {
603 usages.push(global_help);
604 }
605 }
606 }
607 if !foot.is_empty() {
608 usages.push(foot);
609 }
610 Some(usages.join(&block_spacing).into())
611 }
612}