1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//! Extensions and utilities for the [`textwrap`] crate.

use std::borrow::Cow;

use textwrap::Options;
use textwrap::WordSeparator;
use textwrap::WordSplitter;

/// Get [`textwrap`] options with our settings.
pub fn options<'a>() -> Options<'a> {
    let opts = Options::with_termwidth()
        .break_words(false)
        .word_separator(WordSeparator::AsciiSpace)
        .word_splitter(WordSplitter::NoHyphenation);

    // In tests, the terminal is always 80 characters wide.
    if cfg!(test) {
        opts.with_width(80)
    } else {
        opts
    }
}

/// Extension trait adding methods to [`textwrap::Options`]
pub trait TextWrapOptionsExt {
    /// Subtract from the current `width`.
    fn subtract_width(self, decrease: usize) -> Self;

    /// Set the `width` to wrap the text to.
    fn with_width(self, width: usize) -> Self;

    /// Wrap the given text into lines.
    fn wrap<'s>(&self, text: &'s str) -> Vec<Cow<'s, str>>;

    /// Wrap the given text into lines and return a `String`.
    ///
    /// Like [`Self::wrap`] but with the lines pre-joined.
    fn fill(&self, text: &str) -> String;
}

impl<'a> TextWrapOptionsExt for Options<'a> {
    fn subtract_width(mut self, decrease: usize) -> Self {
        self.width -= decrease;
        self
    }

    fn with_width(mut self, width: usize) -> Self {
        self.width = width;
        self
    }

    fn wrap<'s>(&self, text: &'s str) -> Vec<Cow<'s, str>> {
        textwrap::wrap(text, self)
    }

    fn fill(&self, text: &str) -> String {
        textwrap::fill(text, self)
    }
}