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