1 use std::fmt::{self, Write}; 2 3 /// Left, Center, Right or Unspecified 4 #[derive(Debug, PartialEq, Eq, Clone, Copy)] 5 pub enum Alignment { 6 Unspecified, 7 Left, 8 Center, 9 Right, 10 } 11 12 impl Default for Alignment { default() -> Self13 fn default() -> Self { 14 Alignment::Unspecified 15 } 16 } 17 18 /// a Compound is a part of a line with a consistent styling. 19 /// It can be part of word, several words, some inline code, or even the whole line. 20 #[derive(Clone, PartialEq, Eq, Hash)] 21 pub struct Compound<'s> { 22 pub src: &'s str, 23 pub bold: bool, 24 pub italic: bool, 25 pub code: bool, 26 pub strikeout: bool, 27 } 28 29 impl<'s> Compound<'s> { 30 /// make a raw unstyled compound 31 /// Involves no parsing 32 #[inline(always)] raw_str(src: &'s str) -> Compound<'s>33 pub fn raw_str(src: &'s str) -> Compound<'s> { 34 Compound { 35 src, 36 bold: false, 37 italic: false, 38 code: false, 39 strikeout: false, 40 } 41 } 42 /// change the content but keeps the style arguments set_str(&mut self, src: &'s str)43 pub fn set_str(&mut self, src: &'s str) { 44 self.src = src; 45 } 46 /// return a sub part of the compound, with the same styling 47 /// r_start is relative, that is 0 is the index of the first 48 /// byte of this compound. 49 #[inline(always)] sub(&self, r_start: usize, r_end: usize) -> Compound<'s>50 pub fn sub(&self, r_start: usize, r_end: usize) -> Compound<'s> { 51 Compound { 52 src: &self.src[r_start..r_end], 53 bold: self.bold, 54 italic: self.italic, 55 code: self.code, 56 strikeout: self.strikeout, 57 } 58 } 59 /// return a sub part of the compound, with the same styling 60 /// r_start is relative, that is 0 is the index of the first 61 /// char of this compound. 62 /// 63 /// The difference with `sub` is that this method is unicode 64 /// aware and counts the chars instead of asking for the bytes 65 #[inline(always)] sub_chars(&self, r_start: usize, r_end: usize) -> Compound<'s>66 pub fn sub_chars(&self, r_start: usize, r_end: usize) -> Compound<'s> { 67 let mut rb_start = 0; 68 let mut rb_end = 0; 69 for (char_idx, (byte_idx, _)) in self.as_str().char_indices().enumerate() { 70 if char_idx == r_start { 71 rb_start = byte_idx; 72 } else if char_idx == r_end { 73 rb_end = byte_idx; 74 break; 75 } 76 } 77 if rb_end == 0 && rb_start != 0 { 78 self.tail(rb_start) 79 } else { 80 self.sub(rb_start, rb_end) 81 } 82 } 83 /// return a sub part at end of the compound, with the same styling 84 /// r_start is relative, that is if you give 0 you get a clone of 85 /// this compound 86 #[inline(always)] tail(&self, r_start: usize) -> Compound<'s>87 pub fn tail(&self, r_start: usize) -> Compound<'s> { 88 Compound { 89 src: &self.src[r_start..], 90 bold: self.bold, 91 italic: self.italic, 92 code: self.code, 93 strikeout: self.strikeout, 94 } 95 } 96 /// return a sub part at end of the compound, with the same styling 97 /// r_start is relative, that is if you give 0 you get a clone of 98 /// this compound 99 /// 100 /// The difference with `tail` is that this method is unicode 101 /// aware and counts the chars instead of asking for the bytes 102 #[inline(always)] tail_chars(&self, r_start: usize) -> Compound<'s>103 pub fn tail_chars(&self, r_start: usize) -> Compound<'s> { 104 let mut rb_start = 0; 105 for (char_idx, (byte_idx, _)) in self.as_str().char_indices().enumerate() { 106 rb_start = byte_idx; 107 if char_idx == r_start { 108 break; 109 } 110 } 111 self.tail(rb_start) 112 } 113 114 // shortens this compound by `tail_size` bytes and returns the tail 115 // as another compound cut_tail(&mut self, tail_size: usize) -> Compound<'s>116 pub fn cut_tail(&mut self, tail_size: usize) -> Compound<'s> { 117 let cut = self.src.len() - tail_size; 118 let tail = Compound { 119 src: &self.src[cut..], 120 bold: self.bold, 121 italic: self.italic, 122 code: self.code, 123 strikeout: self.strikeout, 124 }; 125 self.src = &self.src[0..cut]; 126 tail 127 } 128 129 // make a raw unstyled compound from part of a string 130 // Involves no parsing 131 #[inline(always)] raw_part(src: &'s str, start: usize, end: usize) -> Compound<'s>132 pub fn raw_part(src: &'s str, start: usize, end: usize) -> Compound<'s> { 133 Compound { 134 src: &src[start..end], 135 bold: false, 136 italic: false, 137 code: false, 138 strikeout: false, 139 } 140 } 141 #[inline(always)] new( src: &'s str, start: usize, end: usize, bold: bool, italic: bool, code: bool, strikeout: bool, ) -> Compound<'s>142 pub fn new( 143 src: &'s str, // the source string from which the compound is a part 144 start: usize, // start index in bytes 145 end: usize, 146 bold: bool, 147 italic: bool, 148 code: bool, 149 strikeout: bool, 150 ) -> Compound<'s> { 151 Compound { 152 src: &src[start..end], 153 italic, 154 bold, 155 code, 156 strikeout, 157 } 158 } 159 #[inline(always)] bold(mut self) -> Compound<'s>160 pub fn bold(mut self) -> Compound<'s> { 161 self.bold = true; 162 self 163 } 164 #[inline(always)] italic(mut self) -> Compound<'s>165 pub fn italic(mut self) -> Compound<'s> { 166 self.italic = true; 167 self 168 } 169 #[inline(always)] code(mut self) -> Compound<'s>170 pub fn code(mut self) -> Compound<'s> { 171 self.code = true; 172 self 173 } 174 #[inline(always)] strikeout(mut self) -> Compound<'s>175 pub fn strikeout(mut self) -> Compound<'s> { 176 self.strikeout = true; 177 self 178 } 179 #[inline(always)] set_bold(&mut self, bold: bool)180 pub fn set_bold(&mut self, bold: bool) { 181 self.bold = bold; 182 } 183 #[inline(always)] set_italic(&mut self, italic: bool)184 pub fn set_italic(&mut self, italic: bool) { 185 self.italic = italic; 186 } 187 #[inline(always)] set_code(&mut self, code: bool)188 pub fn set_code(&mut self, code: bool) { 189 self.code = code; 190 } 191 #[inline(always)] set_strikeout(&mut self, strikeout: bool)192 pub fn set_strikeout(&mut self, strikeout: bool) { 193 self.strikeout = strikeout; 194 } 195 #[inline(always)] as_str(&self) -> &'s str196 pub fn as_str(&self) -> &'s str { 197 self.src 198 } 199 #[inline(always)] char_length(&self) -> usize200 pub fn char_length(&self) -> usize { 201 self.as_str().chars().count() 202 } 203 #[inline(always)] is_empty(&self) -> bool204 pub fn is_empty(&self) -> bool { 205 self.src.is_empty() 206 } 207 } 208 209 impl fmt::Display for Compound<'_> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 211 f.write_str(self.as_str())?; 212 Ok(()) 213 } 214 } 215 216 impl fmt::Debug for Compound<'_> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result217 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 218 if self.bold { 219 f.write_char('B')?; 220 } 221 if self.italic { 222 f.write_char('I')?; 223 } 224 if self.code { 225 f.write_char('C')?; 226 } 227 if self.strikeout { 228 f.write_char('S')?; 229 } 230 f.write_char('"')?; 231 f.write_str(self.as_str())?; 232 f.write_char('"')?; 233 Ok(()) 234 } 235 } 236