cliargs/help/
mod.rs

1// Copyright (C) 2024 Takayuki Sato. All Rights Reserved.
2// This program is free software under MIT License.
3// See the file LICENSE in this distribution for more details.
4
5mod opts;
6
7use crate::OptCfg;
8
9use linebreak;
10use std::cmp;
11
12/// Holds help text blocks and help options blocks.
13///
14/// And this struct creates a [HelpIter] instance which outputs a help text line by line.
15pub struct Help {
16    margin_left: usize,
17    margin_right: usize,
18    blocks: Vec<Block>,
19}
20
21struct Block {
22    indent: usize,
23    margin_left: usize,
24    margin_right: usize,
25    bodies: Vec<(usize, String)>, // The vector of tuples of (first indent, text)
26
27    _spaces: String,
28}
29
30impl Help {
31    /// Constructs an [Help] instance with 0 margins.
32    pub fn new() -> Self {
33        Self {
34            margin_left: 0,
35            margin_right: 0,
36            blocks: Vec::<Block>::with_capacity(2),
37        }
38    }
39
40    /// Constructs an [Help] instance with left and right margins.
41    pub fn with_margins(margin_left: usize, margin_right: usize) -> Self {
42        Self {
43            margin_left: margin_left,
44            margin_right: margin_right,
45            blocks: Vec::<Block>::with_capacity(2),
46        }
47    }
48
49    /// Adds a text for a help text block to this help instance.
50    ///
51    /// The indent width of this help text block is set to *auto indentation*.
52    /// The margins of a help text generated by this instance equals them which specified at a
53    /// constructor.
54    pub fn add_text(&mut self, text: String) {
55        self.add_text_with_indent_and_margins(text, 0, 0, 0)
56    }
57
58    /// Adds a text and an indent width for a help text block to this help instance.
59    ///
60    /// The indent width is the number of spaces inserted at the beginning of each line from the
61    /// second line.
62    /// The margins of a help text generated by this instance equals them which specified at a
63    /// constructor.
64    pub fn add_text_with_indent(&mut self, text: String, indent: usize) {
65        self.add_text_with_indent_and_margins(text, indent, 0, 0)
66    }
67
68    /// Adds a text and margins for a help text block to this help instance.
69    ///
70    /// The margins of this help text block generated by this instance equals the sum of them
71    /// specified as parameters of this method and them which specified at a constructor.
72    /// The indent width of this help text block is set to *auto indentation*.
73    pub fn add_text_with_margins(&mut self, text: String, margin_left: usize, margin_right: usize) {
74        self.add_text_with_indent_and_margins(text, 0, margin_left, margin_right)
75    }
76
77    /// Adds a text and an indent width and margins for a help text block to this help instance.
78    ///
79    /// The indent width is the number of spaces inserted at the beginning of each line from the
80    /// second line.
81    /// The margins of this help text block generated by this instance equals the sum of them
82    /// specified as parameters of this method and them which specified at a constructor.
83    pub fn add_text_with_indent_and_margins(
84        &mut self,
85        text: String,
86        indent: usize,
87        margin_left: usize,
88        margin_right: usize,
89    ) {
90        let margin_left = self.margin_left + margin_left;
91        let margin_right = self.margin_right + margin_right;
92        let max_len = cmp::max(margin_left, indent);
93        let block = Block {
94            indent: indent,
95            margin_left: margin_left,
96            margin_right: margin_right,
97            bodies: vec![(0, text)],
98            _spaces: " ".repeat(max_len),
99        };
100        self.blocks.push(block);
101    }
102
103    /// Adds texts for a help text block to this help instance.
104    ///
105    /// The indent width of this help text block is set to *auto indentation*.
106    /// The margins of a help text generated by this instance equals them which specified at a
107    /// constructor.
108    pub fn add_texts(&mut self, texts: Vec<String>) {
109        self.add_texts_with_indent_and_margins(texts, 0, 0, 0);
110    }
111
112    /// Adds texts and an indent width for a help text block to this help instance.
113    ///
114    /// The indent width is the number of spaces inserted at the beginning of each line from the
115    /// second line.
116    /// The margins of a help text generated by this instance equals them which specified at the
117    /// `new` function.
118    pub fn add_texts_with_indent(&mut self, texts: Vec<String>, indent: usize) {
119        self.add_texts_with_indent_and_margins(texts, indent, 0, 0);
120    }
121
122    /// Adds texts and an indent width and margins for a help text block to this help instance.
123    ///
124    /// The margins of this help text block generated by this instance equals the sum of them
125    /// specified as parameters of this method and them which specified at a constructor.
126    /// The indent width of this help text block is set to *auto indentation*.
127    pub fn add_texts_with_margins(
128        &mut self,
129        texts: Vec<String>,
130        margin_left: usize,
131        margin_right: usize,
132    ) {
133        self.add_texts_with_indent_and_margins(texts, 0, margin_left, margin_right);
134    }
135
136    /// Adds texts and an indent width and margins for a help text block to this help instance.
137    ///
138    /// The indent width is the number of spaces inserted at the beginning of each line from the
139    /// second line.
140    /// The margins of this help text block generated by this instance equals the sum of them
141    /// specified as parameters of this method and them which specified at a constructor.
142    pub fn add_texts_with_indent_and_margins(
143        &mut self,
144        texts: Vec<String>,
145        indent: usize,
146        margin_left: usize,
147        margin_right: usize,
148    ) {
149        let margin_left = self.margin_left + margin_left;
150        let margin_right = self.margin_right + margin_right;
151        let max_len = cmp::max(margin_left, indent);
152        let block = Block {
153            indent: indent,
154            margin_left: margin_left,
155            margin_right: margin_right,
156            bodies: texts.into_iter().map(|s| (0, s)).collect(),
157            _spaces: " ".repeat(max_len),
158        };
159        self.blocks.push(block);
160    }
161
162    /// Adds OptCfg(s) for a help option block to this help instance.
163    ///
164    /// The indent width of this help text block is set to *auto indentation*.
165    /// The margins of a help text generated by this instance equals them which specified at a
166    /// constructor.
167    pub fn add_opts(&mut self, cfgs: &[OptCfg]) {
168        self.add_opts_with_indent_and_margins(cfgs, 0, 0, 0);
169    }
170
171    /// Adds OptCfg(s) and an indent width for a help option block to this help instance.
172    ///
173    /// The indent width is the number of spaces inserted at the beginning of each line from the
174    /// second line.
175    /// The margins of a help text generated by this instance equals them which specified at a
176    /// constructor.
177    pub fn add_opts_with_indent(&mut self, cfgs: &[OptCfg], indent: usize) {
178        self.add_opts_with_indent_and_margins(cfgs, indent, 0, 0);
179    }
180
181    /// Adds OptCfg(s) and an indent width and margins for a help option block to this help
182    /// instance.
183    ///
184    /// The margins of this help text block generated by this instance equals the sum of them
185    /// specified as parameters of this method and them which specified at a constructor.
186    /// The indent width of this help text block is set to *auto indentation*.
187    pub fn add_opts_with_margins(
188        &mut self,
189        cfgs: &[OptCfg],
190        margin_left: usize,
191        margin_right: usize,
192    ) {
193        self.add_opts_with_indent_and_margins(cfgs, 0, margin_left, margin_right);
194    }
195
196    /// Adds OptCfg(s) and an indent width and margins for a help option block to this help
197    /// instance.
198    ///
199    /// The indent width is the number of spaces inserted at the beginning of each line from the
200    /// second line.
201    /// The margins of this help text block generated by this instance equals the sum of them
202    /// specified as parameters of this method and them which specified at a constructor.
203    pub fn add_opts_with_indent_and_margins(
204        &mut self,
205        cfgs: &[OptCfg],
206        indent: usize,
207        margin_left: usize,
208        margin_right: usize,
209    ) {
210        let mut indent = indent;
211        let bodies = opts::create_opts_help(cfgs, &mut indent);
212
213        let mut max_len = 0;
214        for (i, _) in &bodies {
215            max_len = cmp::max(max_len, *i);
216        }
217
218        let margin_left = self.margin_left + margin_left;
219        let margin_right = self.margin_right + margin_right;
220        let max_len = cmp::max(max_len, cmp::max(margin_left, indent));
221
222        let block = Block {
223            indent: indent,
224            margin_left: margin_left,
225            margin_right: margin_right,
226            bodies: bodies,
227            _spaces: " ".repeat(max_len),
228        };
229        self.blocks.push(block);
230    }
231
232    /// Creates a [HelpIter] instance which is an iterator that outputs a help text line by line.
233    pub fn iter(&self) -> HelpIter {
234        if self.blocks.is_empty() {
235            return HelpIter {
236                line_width: 0,
237                blocks: &self.blocks,
238                block_iter: BlockIter::empty(),
239            };
240        }
241
242        let line_width = linebreak::term_cols();
243
244        HelpIter {
245            line_width: line_width,
246            blocks: &self.blocks,
247            block_iter: BlockIter::new(&self.blocks[0], line_width),
248        }
249    }
250
251    /// Outputs a help text to the standard output.
252    pub fn print(&self) {
253        let mut iter = self.iter();
254        while let Some(line) = iter.next() {
255            println!("{}", line);
256        }
257    }
258}
259
260/// Is an iterator that outputs a help text line by line.
261///
262/// The width of line uses the current terminal column number.
263pub struct HelpIter<'a> {
264    line_width: usize,
265    blocks: &'a [Block],
266    block_iter: BlockIter<'a>,
267}
268
269struct BlockIter<'a> {
270    bodies: &'a [(usize, String)],
271    index: usize,
272    indent: &'a str,
273    margin: &'a str,
274    line_iter: linebreak::LineIter<'a>,
275}
276
277impl Iterator for HelpIter<'_> {
278    type Item = String;
279
280    fn next(&mut self) -> Option<String> {
281        loop {
282            if let Some(line) = self.block_iter.next() {
283                return Some(line);
284            }
285            if self.blocks.len() <= 1 {
286                break;
287            }
288            self.blocks = &self.blocks[1..];
289            self.block_iter = BlockIter::new(&self.blocks[0], self.line_width);
290        }
291        None
292    }
293}
294
295impl<'a> BlockIter<'a> {
296    fn new(block: &'a Block, line_width: usize) -> Self {
297        let print_width = line_width - block.margin_left - block.margin_right;
298        if block.bodies.is_empty() || print_width <= block.indent {
299            return Self::empty();
300        }
301        let (indent, text) = &block.bodies[0];
302        let mut line_iter = linebreak::LineIter::new(&text, print_width);
303        line_iter.set_indent(&block._spaces[0..(*indent)]);
304        Self {
305            bodies: &block.bodies,
306            index: 0,
307            indent: &(&block._spaces)[0..block.indent],
308            margin: &(&block._spaces)[0..block.margin_left],
309            line_iter: line_iter,
310        }
311    }
312
313    fn empty() -> Self {
314        Self {
315            bodies: &[] as &'a [(usize, String)],
316            index: 0,
317            indent: "",
318            margin: "",
319            line_iter: linebreak::LineIter::new("", 0),
320        }
321    }
322}
323
324impl<'a> Iterator for BlockIter<'a> {
325    type Item = String;
326
327    fn next(&mut self) -> Option<String> {
328        if self.bodies.is_empty() {
329            return None;
330        }
331
332        loop {
333            if let Some(mut line) = self.line_iter.next() {
334                line.insert_str(0, self.margin);
335                self.line_iter.set_indent(&self.indent);
336                return Some(line);
337            }
338
339            self.index += 1;
340            if self.index >= self.bodies.len() {
341                break;
342            }
343
344            let (indent, text) = &self.bodies[self.index];
345            self.line_iter.init(&text);
346            self.line_iter.set_indent(&self.indent[0..(*indent)]);
347        }
348
349        None
350    }
351}
352
353#[cfg(test)]
354mod tests_of_help {
355    use super::*;
356
357    #[test]
358    fn new() {
359        let help = Help::new();
360
361        let mut iter = help.iter();
362
363        let line = iter.next();
364        assert_eq!(line, None);
365
366        let line = iter.next();
367        assert_eq!(line, None);
368    }
369
370    #[test]
371    fn new_and_add_text_if_one_line_with_zero_wrapping() {
372        let mut help = Help::new();
373        help.add_text("abc".to_string());
374
375        let mut iter = help.iter();
376
377        let line = iter.next();
378        assert_eq!(line, Some("abc".to_string()));
379
380        let line = iter.next();
381        assert_eq!(line, None);
382
383        let line = iter.next();
384        assert_eq!(line, None);
385    }
386
387    #[test]
388    fn new_and_add_text_if_one_line_with_wrapping() {
389        let term_cols = linebreak::term_cols();
390        let mut text = "a".repeat(term_cols);
391        text.push_str("123456");
392
393        let mut help = Help::new();
394        help.add_text(text);
395
396        let mut iter = help.iter();
397
398        let line = iter.next();
399        assert_eq!(line, Some("a".repeat(term_cols)));
400
401        let line = iter.next();
402        assert_eq!(line, Some("123456".to_string()));
403
404        let line = iter.next();
405        assert_eq!(line, None);
406    }
407
408    #[test]
409    fn new_add_text_if_multi_lines_with_wrapping() {
410        let term_cols = linebreak::term_cols();
411        let mut text = "a".repeat(term_cols);
412        text.push_str("123456\n");
413        text.push_str(&"b".repeat(term_cols));
414        text.push_str("789");
415
416        let mut help = Help::new();
417        help.add_text(text);
418        let mut iter = help.iter();
419
420        let line = iter.next();
421        assert_eq!(line, Some("a".repeat(term_cols)));
422
423        let line = iter.next();
424        assert_eq!(line, Some("123456".to_string()));
425
426        let line = iter.next();
427        assert_eq!(line, Some("b".repeat(term_cols)));
428
429        let line = iter.next();
430        assert_eq!(line, Some("789".to_string()));
431
432        let line = iter.next();
433        assert_eq!(line, None);
434    }
435
436    #[test]
437    fn with_margins_and_add_text() {
438        let term_cols = linebreak::term_cols();
439        let mut text = "a".repeat(term_cols - 5 - 3);
440        text.push_str("12345\n");
441        text.push_str(&"b".repeat(term_cols - 5 - 3));
442        text.push_str("6789");
443
444        let mut help = Help::with_margins(5, 3);
445        help.add_text(text);
446        let mut iter = help.iter();
447
448        let line = iter.next();
449        let mut expected = "a".repeat(term_cols - 5 - 3);
450        expected.insert_str(0, "     ");
451        assert_eq!(line, Some(expected));
452
453        let line = iter.next();
454        let expected = "     12345".to_string();
455        assert_eq!(line, Some(expected));
456
457        let line = iter.next();
458        let mut expected = "b".repeat(term_cols - 5 - 3);
459        expected.insert_str(0, "     ");
460        assert_eq!(line, Some(expected));
461
462        let line = iter.next();
463        let expected = "     6789".to_string();
464        assert_eq!(line, Some(expected));
465
466        let line = iter.next();
467        assert_eq!(line, None);
468    }
469
470    #[test]
471    fn add_text_with_margins() {
472        let term_cols = linebreak::term_cols();
473        let mut text = "a".repeat(term_cols - 5 - 3);
474        text.push_str("12345\n");
475        text.push_str(&"b".repeat(term_cols - 5 - 3));
476        text.push_str("6789");
477
478        let mut help = Help::new();
479        help.add_text_with_margins(text, 5, 3);
480        let mut iter = help.iter();
481
482        let line = iter.next();
483        let mut expected = "a".repeat(term_cols - 5 - 3);
484        expected.insert_str(0, "     ");
485        assert_eq!(line, Some(expected));
486
487        let line = iter.next();
488        let expected = "     12345".to_string();
489        assert_eq!(line, Some(expected));
490
491        let line = iter.next();
492        let mut expected = "b".repeat(term_cols - 5 - 3);
493        expected.insert_str(0, "     ");
494        assert_eq!(line, Some(expected));
495
496        let line = iter.next();
497        let expected = "     6789".to_string();
498        assert_eq!(line, Some(expected));
499
500        let line = iter.next();
501        assert_eq!(line, None);
502    }
503
504    #[test]
505    fn add_text_with_indent() {
506        let term_cols = linebreak::term_cols();
507        let mut text = "a".repeat(term_cols);
508        text.push_str("12345\n");
509        text.push_str(&"b".repeat(term_cols - 8));
510        text.push_str("6789");
511
512        let mut help = Help::new();
513        help.add_text_with_indent(text, 8);
514        let mut iter = help.iter();
515
516        let line = iter.next();
517        let expected = "a".repeat(term_cols);
518        assert_eq!(line, Some(expected));
519
520        let line = iter.next();
521        let expected = "        12345".to_string();
522        assert_eq!(line, Some(expected));
523
524        let line = iter.next();
525        let mut expected = "b".repeat(term_cols - 8);
526        expected.insert_str(0, "        ");
527        assert_eq!(line, Some(expected));
528
529        let line = iter.next();
530        let expected = "        6789".to_string();
531        assert_eq!(line, Some(expected));
532
533        let line = iter.next();
534        assert_eq!(line, None);
535    }
536
537    #[test]
538    fn add_text_with_indent_and_margins() {
539        let term_cols = linebreak::term_cols();
540        let mut text = "a".repeat(term_cols - 1 - 2);
541        text.push_str("12345\n");
542        text.push_str(&"b".repeat(term_cols - 8 - 1 - 2));
543        text.push_str("6789");
544
545        let mut help = Help::new();
546        help.add_text_with_indent_and_margins(text, 8, 1, 2);
547        let mut iter = help.iter();
548
549        let line = iter.next();
550        let mut expected = "a".repeat(term_cols - 1 - 2);
551        expected.insert_str(0, " ");
552        assert_eq!(line, Some(expected));
553
554        let line = iter.next();
555        let expected = "         12345".to_string();
556        assert_eq!(line, Some(expected));
557
558        let line = iter.next();
559        let mut expected = "b".repeat(term_cols - 8 - 1 - 2);
560        expected.insert_str(0, "         ");
561        assert_eq!(line, Some(expected));
562
563        let line = iter.next();
564        let expected = "         6789".to_string();
565        assert_eq!(line, Some(expected));
566
567        let line = iter.next();
568        assert_eq!(line, None);
569    }
570
571    #[test]
572    fn add_text_if_text_is_empty() {
573        let mut help = Help::new();
574        help.add_text("".to_string());
575        let mut iter = help.iter();
576
577        let line = iter.next();
578        assert_eq!(line, Some("".to_string()));
579
580        let line = iter.next();
581        assert_eq!(line, None);
582    }
583
584    #[test]
585    fn add_text_multiple_times() {
586        let term_cols = linebreak::term_cols();
587
588        let mut text = "a".repeat(term_cols - 4 - 3);
589        text.push_str(&"b".repeat(term_cols - 4 - 5 - 3));
590        text.push_str(&"c".repeat(term_cols - 4 - 5 - 3));
591
592        let mut help = Help::with_margins(1, 1);
593        help.add_text_with_indent_and_margins(text, 5, 3, 2);
594
595        let mut text = "d".repeat(term_cols - 2 - 2);
596        text.push_str(&"e".repeat(term_cols - 2 - 5 - 2));
597        text.push_str(&"f".repeat(term_cols - 2 - 5 - 2));
598
599        help.add_text_with_indent_and_margins(text, 5, 1, 1);
600
601        let mut iter = help.iter();
602
603        let line = iter.next();
604        assert_eq!(line, Some("    ".to_string() + &"a".repeat(term_cols - 7)));
605
606        let line = iter.next();
607        assert_eq!(
608            line,
609            Some("         ".to_string() + &"b".repeat(term_cols - 12))
610        );
611
612        let line = iter.next();
613        assert_eq!(
614            line,
615            Some("         ".to_string() + &"c".repeat(term_cols - 12))
616        );
617
618        let line = iter.next();
619        assert_eq!(line, Some("  ".to_string() + &"d".repeat(term_cols - 4)));
620
621        let line = iter.next();
622        assert_eq!(
623            line,
624            Some("       ".to_string() + &"e".repeat(term_cols - 9))
625        );
626
627        let line = iter.next();
628        assert_eq!(
629            line,
630            Some("       ".to_string() + &"f".repeat(term_cols - 9))
631        );
632    }
633
634    #[test]
635    fn add_texts_if_array_is_empty() {
636        let mut help = Help::new();
637        help.add_texts(Vec::<String>::new());
638
639        let mut iter = help.iter();
640
641        let line = iter.next();
642        assert_eq!(line, None);
643
644        let line = iter.next();
645        assert_eq!(line, None);
646    }
647
648    #[test]
649    fn add_texts_if_array_has_a_text() {
650        let term_cols = linebreak::term_cols();
651
652        let mut texts = Vec::<String>::new();
653
654        let mut text = "a".repeat(term_cols);
655        text.push_str(&"b".repeat(term_cols));
656        text.push_str(&"c".repeat(term_cols));
657        texts.push(text);
658
659        let mut help = Help::new();
660        help.add_texts(texts);
661
662        let mut iter = help.iter();
663
664        let line = iter.next();
665        assert_eq!(line, Some("a".repeat(term_cols)));
666
667        let line = iter.next();
668        assert_eq!(line, Some("b".repeat(term_cols)));
669
670        let line = iter.next();
671        assert_eq!(line, Some("c".repeat(term_cols)));
672
673        let line = iter.next();
674        assert_eq!(line, None);
675    }
676
677    #[test]
678    fn add_texts_if_array_has_multiple_texts() {
679        let term_cols = linebreak::term_cols();
680
681        let mut texts = Vec::<String>::new();
682
683        let mut text = "a".repeat(term_cols);
684        text.push_str(&"b".repeat(term_cols));
685        text.push_str(&"c".repeat(term_cols));
686        texts.push(text);
687
688        let mut text = "d".repeat(term_cols);
689        text.push_str(&"e".repeat(term_cols));
690        text.push_str(&"f".repeat(term_cols));
691        texts.push(text);
692
693        let mut help = Help::new();
694        help.add_texts(texts);
695
696        let mut iter = help.iter();
697
698        let line = iter.next();
699        assert_eq!(line, Some("a".repeat(term_cols)));
700
701        let line = iter.next();
702        assert_eq!(line, Some("b".repeat(term_cols)));
703
704        let line = iter.next();
705        assert_eq!(line, Some("c".repeat(term_cols)));
706
707        let line = iter.next();
708        assert_eq!(line, Some("d".repeat(term_cols)));
709
710        let line = iter.next();
711        assert_eq!(line, Some("e".repeat(term_cols)));
712
713        let line = iter.next();
714        assert_eq!(line, Some("f".repeat(term_cols)));
715
716        let line = iter.next();
717        assert_eq!(line, None);
718    }
719
720    #[test]
721    fn add_texts_with_indent() {
722        let term_cols = linebreak::term_cols();
723
724        let mut texts = Vec::<String>::new();
725
726        let mut text = "a".repeat(term_cols);
727        text.push_str(&"b".repeat(term_cols - 5));
728        text.push_str(&"c".repeat(term_cols - 5));
729        texts.push(text);
730
731        let mut text = "d".repeat(term_cols);
732        text.push_str(&"e".repeat(term_cols - 5));
733        text.push_str(&"f".repeat(term_cols - 5));
734        texts.push(text);
735
736        let mut help = Help::new();
737        help.add_texts_with_indent(texts, 5);
738
739        let mut iter = help.iter();
740
741        let line = iter.next();
742        assert_eq!(line, Some("a".repeat(term_cols)));
743
744        let line = iter.next();
745        assert_eq!(line, Some(" ".repeat(5) + &"b".repeat(term_cols - 5)));
746
747        let line = iter.next();
748        assert_eq!(line, Some(" ".repeat(5) + &"c".repeat(term_cols - 5)));
749
750        let line = iter.next();
751        assert_eq!(line, Some("d".repeat(term_cols)));
752
753        let line = iter.next();
754        assert_eq!(line, Some(" ".repeat(5) + &"e".repeat(term_cols - 5)));
755
756        let line = iter.next();
757        assert_eq!(line, Some(" ".repeat(5) + &"f".repeat(term_cols - 5)));
758
759        let line = iter.next();
760        assert_eq!(line, None);
761    }
762
763    #[test]
764    fn add_texts_with_margins() {
765        let term_cols = linebreak::term_cols();
766
767        let mut texts = Vec::<String>::new();
768
769        let mut text = "a".repeat(term_cols - 3 - 3);
770        text.push_str(&"b".repeat(term_cols - 3 - 3));
771        text.push_str(&"c".repeat(term_cols - 3 - 3));
772        texts.push(text);
773
774        let mut text = "d".repeat(term_cols - 3 - 3);
775        text.push_str(&"e".repeat(term_cols - 3 - 3));
776        text.push_str(&"f".repeat(term_cols - 3 - 3));
777        texts.push(text);
778
779        let mut help = Help::with_margins(1, 1);
780        help.add_texts_with_margins(texts, 2, 2);
781
782        let mut iter = help.iter();
783
784        let line = iter.next();
785        assert_eq!(line, Some(" ".repeat(3) + &"a".repeat(term_cols - 6)));
786
787        let line = iter.next();
788        assert_eq!(line, Some(" ".repeat(3) + &"b".repeat(term_cols - 6)));
789
790        let line = iter.next();
791        assert_eq!(line, Some(" ".repeat(3) + &"c".repeat(term_cols - 6)));
792
793        let line = iter.next();
794        assert_eq!(line, Some(" ".repeat(3) + &"d".repeat(term_cols - 6)));
795
796        let line = iter.next();
797        assert_eq!(line, Some(" ".repeat(3) + &"e".repeat(term_cols - 6)));
798
799        let line = iter.next();
800        assert_eq!(line, Some(" ".repeat(3) + &"f".repeat(term_cols - 6)));
801
802        let line = iter.next();
803        assert_eq!(line, None);
804    }
805
806    #[test]
807    fn add_texts_with_indent_and_margins() {
808        let term_cols = linebreak::term_cols();
809
810        let mut texts = Vec::<String>::new();
811
812        let mut text = "a".repeat(term_cols - 3 - 3);
813        text.push_str(&"b".repeat(term_cols - 3 - 5 - 3));
814        text.push_str(&"c".repeat(term_cols - 3 - 5 - 3));
815        texts.push(text);
816
817        let mut text = "d".repeat(term_cols - 3 - 3);
818        text.push_str(&"e".repeat(term_cols - 3 - 5 - 3));
819        text.push_str(&"f".repeat(term_cols - 3 - 5 - 3));
820        texts.push(text);
821
822        let mut help = Help::with_margins(1, 1);
823        help.add_texts_with_indent_and_margins(texts, 5, 2, 2);
824
825        let mut iter = help.iter();
826
827        let line = iter.next();
828        assert_eq!(line, Some(" ".repeat(3) + &"a".repeat(term_cols - 6)));
829
830        let line = iter.next();
831        assert_eq!(line, Some(" ".repeat(8) + &"b".repeat(term_cols - 11)));
832
833        let line = iter.next();
834        assert_eq!(line, Some(" ".repeat(8) + &"c".repeat(term_cols - 11)));
835
836        let line = iter.next();
837        assert_eq!(line, Some(" ".repeat(3) + &"d".repeat(term_cols - 6)));
838
839        let line = iter.next();
840        assert_eq!(line, Some(" ".repeat(8) + &"e".repeat(term_cols - 11)));
841
842        let line = iter.next();
843        assert_eq!(line, Some(" ".repeat(8) + &"f".repeat(term_cols - 11)));
844
845        let line = iter.next();
846        assert_eq!(line, None);
847    }
848
849    #[test]
850    fn add_opts_with_no_wrapping() {
851        use crate::OptCfgParam::*;
852
853        let mut help = Help::new();
854        help.add_opts(&[OptCfg::with([
855            names(&["foo-bar"]),
856            desc("This is a description of option."),
857        ])]);
858
859        let mut iter = help.iter();
860
861        let line = iter.next();
862        assert_eq!(
863            line,
864            Some("--foo-bar  This is a description of option.".to_string())
865        );
866
867        let line = iter.next();
868        assert_eq!(line, None);
869    }
870
871    #[test]
872    fn add_opts_with_wrapping() {
873        use crate::OptCfgParam::*;
874
875        let cols = linebreak::term_cols();
876
877        let mut help = Help::new();
878        help.add_opts(&[OptCfg::with([
879            names(&["foo-bar"]),
880            desc(&("a".repeat(cols - 11) + " bcdef")),
881        ])]);
882
883        let mut iter = help.iter();
884
885        let line = iter.next();
886        assert_eq!(
887            line,
888            Some("--foo-bar  ".to_string() + &"a".repeat(cols - 11))
889        );
890
891        let line = iter.next();
892        assert_eq!(line, Some("           ".to_string() + "bcdef"));
893
894        let line = iter.next();
895        assert_eq!(line, None);
896    }
897
898    #[test]
899    fn with_margins_and_add_opts() {
900        use crate::OptCfgParam::*;
901
902        let cols = linebreak::term_cols();
903
904        let mut help = Help::with_margins(4, 2);
905
906        help.add_opts(&[OptCfg::with([
907            names(&["foo-bar"]),
908            desc(&("a".repeat(cols - 11 - 4 - 2) + " " + &"b".repeat(cols - 11 - 4 - 2) + "ccc")),
909        ])]);
910
911        let mut iter = help.iter();
912
913        let line = iter.next();
914        assert_eq!(
915            line,
916            Some("    --foo-bar  ".to_string() + &"a".repeat(cols - 11 - 4 - 2))
917        );
918
919        let line = iter.next();
920        assert_eq!(
921            line,
922            Some(" ".repeat(11 + 4) + &"b".repeat(cols - 11 - 4 - 2))
923        );
924
925        let line = iter.next();
926        assert_eq!(line, Some(" ".repeat(11 + 4) + "ccc"));
927
928        let line = iter.next();
929        assert_eq!(line, None);
930    }
931
932    #[test]
933    fn add_opts_with_margins() {
934        use crate::OptCfgParam::*;
935
936        let cols = linebreak::term_cols();
937
938        let mut help = Help::new();
939
940        help.add_opts_with_margins(
941            &[OptCfg::with([
942                names(&["foo-bar"]),
943                desc(
944                    &("a".repeat(cols - 11 - 5 - 4) + " " + &"b".repeat(cols - 11 - 5 - 4) + "ccc"),
945                ),
946            ])],
947            5,
948            4,
949        );
950
951        let mut iter = help.iter();
952
953        let line = iter.next();
954        assert_eq!(
955            line,
956            Some("     --foo-bar  ".to_string() + &"a".repeat(cols - 11 - 5 - 4))
957        );
958
959        let line = iter.next();
960        assert_eq!(
961            line,
962            Some(" ".repeat(11 + 5) + &"b".repeat(cols - 11 - 5 - 4))
963        );
964
965        let line = iter.next();
966        assert_eq!(line, Some(" ".repeat(11 + 5) + "ccc"));
967
968        let line = iter.next();
969        assert_eq!(line, None);
970    }
971
972    #[test]
973    fn add_opts_with_margins_by_constructor_and_add_text_with_margins() {
974        use crate::OptCfgParam::*;
975
976        let cols = linebreak::term_cols();
977
978        let mut help = Help::with_margins(4, 2);
979
980        help.add_opts_with_margins(
981            &[OptCfg::with([
982                names(&["foo-bar"]),
983                desc(
984                    &("a".repeat(cols - 11 - 5 - 4) + " " + &"b".repeat(cols - 11 - 5 - 4) + "ccc"),
985                ),
986            ])],
987            1,
988            2,
989        );
990
991        let mut iter = help.iter();
992
993        let line = iter.next();
994        assert_eq!(
995            line,
996            Some("     --foo-bar  ".to_string() + &"a".repeat(cols - 11 - 5 - 4))
997        );
998
999        let line = iter.next();
1000        assert_eq!(
1001            line,
1002            Some(" ".repeat(11 + 5) + &"b".repeat(cols - 11 - 5 - 4))
1003        );
1004
1005        let line = iter.next();
1006        assert_eq!(line, Some(" ".repeat(11 + 5) + "ccc"));
1007
1008        let line = iter.next();
1009        assert_eq!(line, None);
1010    }
1011
1012    #[test]
1013    fn add_opts_with_indent_if_indent_is_longer_than_title() {
1014        use crate::OptCfgParam::*;
1015
1016        let cols = linebreak::term_cols();
1017
1018        let mut help = Help::new();
1019        help.add_opts_with_indent(
1020            &[OptCfg::with([
1021                names(&["foo-bar"]),
1022                desc(&("a".repeat(cols - 12) + " " + &"b".repeat(cols - 12) + "ccc")),
1023            ])],
1024            12,
1025        );
1026
1027        let mut iter = help.iter();
1028
1029        let line = iter.next();
1030        assert_eq!(
1031            line,
1032            Some("--foo-bar".to_string() + "   " + &"a".repeat(cols - 12))
1033        );
1034
1035        let line = iter.next();
1036        assert_eq!(line, Some(" ".repeat(12) + &"b".repeat(cols - 12)));
1037
1038        let line = iter.next();
1039        assert_eq!(line, Some(" ".repeat(12) + &"ccc"));
1040
1041        let line = iter.next();
1042        assert_eq!(line, None);
1043    }
1044
1045    #[test]
1046    fn add_opts_with_indent_if_indent_is_shorter_than_title() {
1047        use crate::OptCfgParam::*;
1048
1049        let cols = linebreak::term_cols();
1050
1051        let mut help = Help::new();
1052        help.add_opts_with_indent(
1053            &[OptCfg::with([
1054                names(&["foo-bar"]),
1055                desc(&("a".repeat(cols))),
1056            ])],
1057            10,
1058        );
1059
1060        let mut iter = help.iter();
1061
1062        let line = iter.next();
1063        assert_eq!(line, Some("--foo-bar".to_string()));
1064
1065        let line = iter.next();
1066        assert_eq!(line, Some(" ".repeat(10) + &"a".repeat(cols - 10)));
1067
1068        let line = iter.next();
1069        assert_eq!(line, Some(" ".repeat(10) + &"a".repeat(10)));
1070
1071        let line = iter.next();
1072        assert_eq!(line, None);
1073    }
1074
1075    #[test]
1076    fn add_opts_with_indent_and_margins() {
1077        use crate::OptCfgParam::*;
1078
1079        let cols = linebreak::term_cols();
1080
1081        let mut help = Help::new();
1082        help.add_opts_with_indent_and_margins(
1083            &[OptCfg::with([
1084                names(&["foo-bar"]),
1085                desc(&("a".repeat(cols))),
1086            ])],
1087            6,
1088            4,
1089            2,
1090        );
1091
1092        let mut iter = help.iter();
1093
1094        let line = iter.next();
1095        assert_eq!(line, Some("    --foo-bar".to_string()));
1096
1097        let line = iter.next();
1098        assert_eq!(line, Some(" ".repeat(10) + &"a".repeat(cols - 6 - 4 - 2)));
1099
1100        let line = iter.next();
1101        assert_eq!(line, Some(" ".repeat(10) + &"a".repeat(6 + 4 + 2)));
1102
1103        let line = iter.next();
1104        assert_eq!(line, None);
1105    }
1106
1107    #[test]
1108    fn add_opts_if_opts_are_multiple() {
1109        use crate::OptCfgParam::*;
1110
1111        let cols = linebreak::term_cols();
1112
1113        let mut help = Help::new();
1114        help.add_opts(&[
1115            OptCfg::with([
1116                names(&["foo-bar", "f"]),
1117                has_arg(true),
1118                desc(&("a".repeat(cols - 22) + " " + &"b".repeat(cols - 22) + "ccc")),
1119                arg_in_help("<text>"),
1120            ]),
1121            OptCfg::with([
1122                names(&["baz", "b"]),
1123                desc(&("d".repeat(cols - 22) + " " + &"e".repeat(cols - 22) + "fff")),
1124            ]),
1125        ]);
1126
1127        let mut iter = help.iter();
1128
1129        let line = iter.next();
1130        assert_eq!(
1131            line,
1132            Some("--foo-bar, -f <text>  ".to_string() + &"a".repeat(cols - 22)),
1133        );
1134
1135        let line = iter.next();
1136        assert_eq!(line, Some(" ".repeat(22) + &"b".repeat(cols - 22)),);
1137
1138        let line = iter.next();
1139        assert_eq!(line, Some(" ".repeat(22) + "ccc"),);
1140
1141        let line = iter.next();
1142        assert_eq!(
1143            line,
1144            Some("--baz, -b             ".to_string() + &"d".repeat(cols - 22)),
1145        );
1146
1147        let line = iter.next();
1148        assert_eq!(line, Some(" ".repeat(22) + &"e".repeat(cols - 22)),);
1149
1150        let line = iter.next();
1151        assert_eq!(line, Some(" ".repeat(22) + "fff"),);
1152
1153        let line = iter.next();
1154        assert_eq!(line, None);
1155    }
1156
1157    #[test]
1158    fn add_opts_if_names_are_empty_and_store_key_is_specified() {
1159        use crate::OptCfgParam::*;
1160
1161        let mut help = Help::new();
1162        help.add_opts(&[
1163            OptCfg::with([store_key("foo"), desc("description")]),
1164            OptCfg::with([store_key("bar"), names(&["", ""]), desc("description")]),
1165        ]);
1166
1167        let mut iter = help.iter();
1168
1169        let line = iter.next();
1170        assert_eq!(line, Some("--foo          description".to_string()));
1171
1172        let line = iter.next();
1173        assert_eq!(line, Some("        --bar  description".to_string()));
1174
1175        let line = iter.next();
1176        assert_eq!(line, None);
1177    }
1178
1179    #[test]
1180    fn add_opts_if_store_key_is_any_option() {
1181        use crate::OptCfgParam::*;
1182
1183        let mut help = Help::new();
1184        help.add_opts(&[
1185            OptCfg::with([store_key("foo"), desc("description")]),
1186            OptCfg::with([store_key("*"), desc("any option")]),
1187        ]);
1188
1189        let mut iter = help.iter();
1190
1191        let line = iter.next();
1192        assert_eq!(line, Some("--foo  description".to_string()));
1193
1194        let line = iter.next();
1195        assert_eq!(line, None);
1196    }
1197
1198    #[test]
1199    fn add_opts_if_first_element_of_names_is_any_option() {
1200        use crate::OptCfgParam::*;
1201
1202        let mut help = Help::new();
1203        help.add_opts(&[
1204            OptCfg::with([names(&["foo-bar"]), desc("description")]),
1205            OptCfg::with([names(&["*"]), desc("any option")]),
1206        ]);
1207
1208        let mut iter = help.iter();
1209
1210        let line = iter.next();
1211        assert_eq!(line, Some("--foo-bar  description".to_string()));
1212
1213        let line = iter.next();
1214        assert_eq!(line, None);
1215    }
1216
1217    #[test]
1218    fn add_opts_with_indent_if_indent_is_longer_than_line_width() {
1219        use crate::OptCfgParam::*;
1220
1221        let cols = linebreak::term_cols();
1222
1223        let mut help = Help::new();
1224        help.add_opts_with_indent(
1225            &[
1226                OptCfg::with([names(&["foo-bar"]), desc("description")]),
1227                OptCfg::with([names(&["baz"]), desc("description")]),
1228            ],
1229            cols + 1,
1230        );
1231
1232        let mut iter = help.iter();
1233
1234        let line = iter.next();
1235        assert_eq!(line, None);
1236
1237        let line = iter.next();
1238        assert_eq!(line, None);
1239    }
1240
1241    #[test]
1242    fn add_opts_with_margins_if_sum_of_margins_are_equal_to_line_width() {
1243        use crate::OptCfgParam::*;
1244
1245        let cols = linebreak::term_cols();
1246
1247        let mut help = Help::new();
1248        help.add_opts_with_margins(
1249            &[
1250                OptCfg::with([names(&["foo-bar"]), desc("description")]),
1251                OptCfg::with([names(&["baz"]), desc("description")]),
1252            ],
1253            cols - 1,
1254            1,
1255        );
1256
1257        let mut iter = help.iter();
1258
1259        let line = iter.next();
1260        assert_eq!(line, None);
1261
1262        let line = iter.next();
1263        assert_eq!(line, None);
1264    }
1265
1266    #[test]
1267    fn add_opts_if_names_contains_empty_strings() {
1268        use crate::OptCfgParam::*;
1269
1270        let mut help = Help::new();
1271        help.add_opts(&[
1272            OptCfg::with([names(&["", "f", "foo-bar", "", ""]), desc("description")]),
1273            OptCfg::with([names(&["b", "", "z", "baz"]), desc("description")]),
1274        ]);
1275
1276        let mut iter = help.iter();
1277
1278        let line = iter.next();
1279        assert_eq!(line, Some("    -f, --foo-bar  description".to_string()));
1280
1281        let line = iter.next();
1282        assert_eq!(line, Some("-b,     -z, --baz  description".to_string()));
1283
1284        let line = iter.next();
1285        assert_eq!(line, None);
1286    }
1287}