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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
use {
    crate::*,
    crate::crossterm::{style::Print, QueueableCommand},
    std::borrow::Cow,
    unicode_width::UnicodeWidthChar,
};

/// wrap a writer to ensure that at most `allowed` columns are
/// written.
pub struct CropWriter<'w, W>
where
    W: std::io::Write,
{
    pub w: &'w mut W,

    /// number of screen columns which may be covered
    pub allowed: usize,

    /// the string replacing a tabulation
    pub tab_replacement: &'static str,
}

impl<'w, W> CropWriter<'w, W>
where
    W: std::io::Write,
{
    pub fn new(w: &'w mut W, limit: usize) -> Self {
        Self {
            w,
            allowed: limit,
            tab_replacement: DEFAULT_TAB_REPLACEMENT,
        }
    }
    pub const fn is_full(&self) -> bool {
        self.allowed == 0
    }
    /// return a tuple containing a string containing either the given &str
    /// or the part fitting the remaining width, and the width of this string)
    pub fn cropped_str<'a>(&self, s: &'a str) -> (Cow<'a, str>, usize) {
        StrFit::make_cow(s, self.allowed)
    }
    pub fn queue_unstyled_str(&mut self, s: &str) -> Result<(), Error> {
        if self.is_full() {
            return Ok(());
        }
        let (string, len) = self.cropped_str(s);
        self.allowed -= len;
        self.w.queue(Print(string))?;
        Ok(())
    }
    pub fn queue_str(&mut self, cs: &CompoundStyle, s: &str) -> Result<(), Error> {
        if self.is_full() {
            return Ok(());
        }
        let (string, len) = self.cropped_str(s);
        self.allowed -= len;
        cs.queue(self.w, string)
    }
    pub fn queue_char(&mut self, cs: &CompoundStyle, c: char) -> Result<(), Error> {
        let width = UnicodeWidthChar::width(c).unwrap_or(0);
        if width < self.allowed {
            self.allowed -= width;
            cs.queue(self.w, c)?;
        }
        Ok(())
    }
    pub fn queue_unstyled_char(&mut self, c: char) -> Result<(), Error> {
        if c == '\t' {
            return self.queue_unstyled_str(self.tab_replacement);
        }
        let width = UnicodeWidthChar::width(c).unwrap_or(0);
        if width < self.allowed {
            self.allowed -= width;
            self.w.queue(Print(c))?;
        }
        Ok(())
    }
    /// a "g_string" is a "gentle" one: each char takes one column on screen.
    /// This function must thus not be used for unknown strings.
    pub fn queue_unstyled_g_string(&mut self, mut s: String) -> Result<(), Error> {
        if self.is_full() {
            return Ok(());
        }
        let mut len = 0;
        for (idx, _) in s.char_indices() {
            len += 1;
            if len > self.allowed {
                s.truncate(idx);
                self.allowed = 0;
                self.w.queue(Print(s))?;
                return Ok(());
            }
        }
        self.allowed -= len;
        self.w.queue(Print(s))?;
        Ok(())
    }
    /// a "g_string" is a "gentle" one: each char takes one column on screen.
    /// This function must thus not be used for unknown strings.
    pub fn queue_g_string(&mut self, cs: &CompoundStyle, mut s: String) -> Result<(), Error> {
        if self.is_full() {
            return Ok(());
        }
        let mut len = 0;
        for (idx, _) in s.char_indices() {
            len += 1;
            if len > self.allowed {
                s.truncate(idx);
                self.allowed = 0;
                return cs.queue(self.w, s);
            }
        }
        self.allowed -= len;
        cs.queue(self.w, s)
    }
    pub fn queue_fg(&mut self, cs: &CompoundStyle) -> Result<(), Error> {
        cs.queue_fg(self.w)
    }
    pub fn queue_bg(&mut self, cs: &CompoundStyle) -> Result<(), Error> {
        cs.queue_bg(self.w)
    }
    pub fn fill(&mut self, cs: &CompoundStyle, filling: &'static Filling) -> Result<(), Error> {
        self.repeat(cs, filling, self.allowed)
    }
    pub fn fill_unstyled(&mut self, filling: &'static Filling) -> Result<(), Error> {
        self.repeat_unstyled(filling, self.allowed)
    }
    pub fn fill_with_space(&mut self, cs: &CompoundStyle) -> Result<(), Error> {
        self.repeat(cs, &SPACE_FILLING, self.allowed)
    }
    pub fn repeat(
        &mut self,
        cs: &CompoundStyle,
        filling: &'static Filling,
        mut len: usize,
    ) -> Result<(), Error> {
        len = len.min(self.allowed);
        self.allowed -= len;
        filling.queue_styled(self.w, cs, len)
    }
    pub fn repeat_unstyled(&mut self, filling: &'static Filling, mut len: usize) -> Result<(), Error> {
        len = len.min(self.allowed);
        self.allowed -= len;
        filling.queue_unstyled(self.w, len)
    }
}