1 use {
2     crate::*,
3     crossterm::{style::Print, QueueableCommand},
4     std::borrow::Cow,
5     unicode_width::UnicodeWidthChar,
6 };
7 
8 /// wrap a writer to ensure that at most `allowed` columns are
9 /// written.
10 pub struct CropWriter<'w, W>
11 where
12     W: std::io::Write,
13 {
14     pub w: &'w mut W,
15 
16     /// number of screen columns which may be covered
17     pub allowed: usize,
18 
19     /// the string replacing a tabulation
20     pub tab_replacement: &'static str,
21 }
22 
23 impl<'w, W> CropWriter<'w, W>
24 where
25     W: std::io::Write,
26 {
new(w: &'w mut W, limit: usize) -> Self27     pub fn new(w: &'w mut W, limit: usize) -> Self {
28         Self {
29             w,
30             allowed: limit,
31             tab_replacement: DEFAULT_TAB_REPLACEMENT,
32         }
33     }
is_full(&self) -> bool34     pub fn is_full(&self) -> bool {
35         self.allowed == 0
36     }
37     /// return a tuple containing a string containing either the given &str
38     /// or the part fitting the remaining width, and the width of this string)
cropped_str<'a>(&self, s: &'a str) -> (Cow<'a, str>, usize)39     pub fn cropped_str<'a>(&self, s: &'a str) -> (Cow<'a, str>, usize) {
40         StrFit::make_cow(s, self.allowed)
41     }
queue_unstyled_str(&mut self, s: &str) -> Result<(), Error>42     pub fn queue_unstyled_str(&mut self, s: &str) -> Result<(), Error> {
43         if self.is_full() {
44             return Ok(());
45         }
46         let (string, len) = self.cropped_str(s);
47         self.allowed -= len;
48         self.w.queue(Print(string))?;
49         Ok(())
50     }
queue_str(&mut self, cs: &CompoundStyle, s: &str) -> Result<(), Error>51     pub fn queue_str(&mut self, cs: &CompoundStyle, s: &str) -> Result<(), Error> {
52         if self.is_full() {
53             return Ok(());
54         }
55         let (string, len) = self.cropped_str(s);
56         self.allowed -= len;
57         cs.queue(self.w, string)
58     }
queue_char(&mut self, cs: &CompoundStyle, c: char) -> Result<(), Error>59     pub fn queue_char(&mut self, cs: &CompoundStyle, c: char) -> Result<(), Error> {
60         let width = UnicodeWidthChar::width(c).unwrap_or(0);
61         if width < self.allowed {
62             self.allowed -= width;
63             cs.queue(self.w, c)?;
64         }
65         Ok(())
66     }
queue_unstyled_char(&mut self, c: char) -> Result<(), Error>67     pub fn queue_unstyled_char(&mut self, c: char) -> Result<(), Error> {
68         if c == '\t' {
69             return self.queue_unstyled_str(self.tab_replacement);
70         }
71         let width = UnicodeWidthChar::width(c).unwrap_or(0);
72         if width < self.allowed {
73             self.allowed -= width;
74             self.w.queue(Print(c))?;
75         }
76         Ok(())
77     }
78     /// a "g_string" is a "gentle" one: each char takes one column on screen.
79     /// This function must thus not be used for unknown strings.
queue_unstyled_g_string(&mut self, mut s: String) -> Result<(), Error>80     pub fn queue_unstyled_g_string(&mut self, mut s: String) -> Result<(), Error> {
81         if self.is_full() {
82             return Ok(());
83         }
84         let mut len = 0;
85         for (idx, _) in s.char_indices() {
86             len += 1;
87             if len > self.allowed {
88                 s.truncate(idx);
89                 self.allowed = 0;
90                 self.w.queue(Print(s))?;
91                 return Ok(());
92             }
93         }
94         self.allowed -= len;
95         self.w.queue(Print(s))?;
96         Ok(())
97     }
98     /// a "g_string" is a "gentle" one: each char takes one column on screen.
99     /// This function must thus not be used for unknown strings.
queue_g_string(&mut self, cs: &CompoundStyle, mut s: String) -> Result<(), Error>100     pub fn queue_g_string(&mut self, cs: &CompoundStyle, mut s: String) -> Result<(), Error> {
101         if self.is_full() {
102             return Ok(());
103         }
104         let mut len = 0;
105         for (idx, _) in s.char_indices() {
106             len += 1;
107             if len > self.allowed {
108                 s.truncate(idx);
109                 self.allowed = 0;
110                 return cs.queue(self.w, s);
111             }
112         }
113         self.allowed -= len;
114         cs.queue(self.w, s)
115     }
queue_fg(&mut self, cs: &CompoundStyle) -> Result<(), Error>116     pub fn queue_fg(&mut self, cs: &CompoundStyle) -> Result<(), Error> {
117         cs.queue_fg(self.w)
118     }
queue_bg(&mut self, cs: &CompoundStyle) -> Result<(), Error>119     pub fn queue_bg(&mut self, cs: &CompoundStyle) -> Result<(), Error> {
120         cs.queue_bg(self.w)
121     }
fill(&mut self, cs: &CompoundStyle, filling: &'static Filling) -> Result<(), Error>122     pub fn fill(&mut self, cs: &CompoundStyle, filling: &'static Filling) -> Result<(), Error> {
123         self.repeat(cs, filling, self.allowed)
124     }
fill_unstyled(&mut self, filling: &'static Filling) -> Result<(), Error>125     pub fn fill_unstyled(&mut self, filling: &'static Filling) -> Result<(), Error> {
126         self.repeat_unstyled(filling, self.allowed)
127     }
fill_with_space(&mut self, cs: &CompoundStyle) -> Result<(), Error>128     pub fn fill_with_space(&mut self, cs: &CompoundStyle) -> Result<(), Error> {
129         self.repeat(cs, &SPACE_FILLING, self.allowed)
130     }
repeat( &mut self, cs: &CompoundStyle, filling: &'static Filling, mut len: usize, ) -> Result<(), Error>131     pub fn repeat(
132         &mut self,
133         cs: &CompoundStyle,
134         filling: &'static Filling,
135         mut len: usize,
136     ) -> Result<(), Error> {
137         len = len.min(self.allowed);
138         self.allowed -= len;
139         filling.queue_styled(self.w, cs, len)
140     }
repeat_unstyled(&mut self, filling: &'static Filling, mut len: usize) -> Result<(), Error>141     pub fn repeat_unstyled(&mut self, filling: &'static Filling, mut len: usize) -> Result<(), Error> {
142         len = len.min(self.allowed);
143         self.allowed -= len;
144         filling.queue_unstyled(self.w, len)
145     }
146 }
147