1use std::fmt::{Debug, Display};
2
3use crate::{Color, SGRBuilder, SGRWriter, Style};
4
5#[derive(Default, Debug, Clone)]
25pub struct SGRString {
26 pub text: String,
28 pub clean: CleanKind,
32
33 pub custom_places: Vec<u8>,
38 pub custom_cleans: Vec<u8>,
43
44 pub foreground: ColorKind,
50 pub background: ColorKind,
56
57 pub reset: bool,
61 pub bold: StyleKind,
63 pub dim: StyleKind,
65 pub italic: StyleKind,
67 pub underline: StyleKind,
69 pub blinking: StyleKind,
71 pub inverse: StyleKind,
73 pub hidden: StyleKind,
75 pub strikethrough: StyleKind,
77}
78impl SGRString {
79 pub fn place_all(&self, builder: &mut SGRBuilder) {
83 if self.reset {
84 builder.write_code(0);
85 }
86 self.place_colors(builder);
87 self.place_styles(builder);
88 self.place_custom(builder);
89 }
90 pub fn place_colors(&self, builder: &mut SGRBuilder) {
94 use ColorKind::*;
95 match self.foreground {
96 Black => builder.write_code(30),
97 Red => builder.write_code(31),
98 Green => builder.write_code(32),
99 Yellow => builder.write_code(33),
100 Blue => builder.write_code(34),
101 Magenta => builder.write_code(35),
102 Cyan => builder.write_code(36),
103 White => builder.write_code(37),
104 Byte(n) => builder.write_codes(&[38, 5, n]),
105 Rgb(r, g, b) => builder.write_codes(&[38, 2, r, g, b]),
106 Default => builder.write_code(39),
107 ColorKind::None => (),
108 };
109 match self.background {
110 Black => builder.write_code(40),
111 Red => builder.write_code(41),
112 Green => builder.write_code(42),
113 Yellow => builder.write_code(43),
114 Blue => builder.write_code(44),
115 Magenta => builder.write_code(45),
116 Cyan => builder.write_code(46),
117 White => builder.write_code(47),
118 Byte(n) => builder.write_codes(&[48, 5, n]),
119 Rgb(r, g, b) => builder.write_codes(&[48, 2, r, g, b]),
120 Default => builder.write_code(49),
121 ColorKind::None => (),
122 };
123 }
124 pub fn place_styles(&self, builder: &mut SGRBuilder) {
128 use StyleKind::*;
129 for (kind, place, not) in [
130 (&self.bold, 1, 22),
131 (&self.dim, 2, 22),
132 (&self.italic, 3, 23),
133 (&self.underline, 4, 24),
134 (&self.blinking, 5, 25),
135 (&self.inverse, 7, 27),
136 (&self.hidden, 8, 28),
137 (&self.strikethrough, 9, 29),
138 ] {
139 match kind {
140 None => (),
141 Place => builder.write_code(place),
142 Clean => builder.write_code(not),
143 }
144 }
145 }
146 pub fn place_custom(&self, builder: &mut SGRBuilder) {
150 builder.write_codes(&self.custom_places);
151 }
152 pub fn clean_all(&self, builder: &mut SGRBuilder) {
158 match self.clean {
159 CleanKind::Reset => builder.write_code(0),
160 CleanKind::Reverse => {
161 self.clean_colors(builder);
162 self.clean_styles(builder);
163 }
164 CleanKind::None => (),
165 }
166 self.clean_custom(builder);
167 }
168 pub fn clean_colors(&self, builder: &mut SGRBuilder) {
174 if self.foreground != ColorKind::None {
175 builder.write_code(39);
176 }
177 if self.background != ColorKind::None {
178 builder.write_code(49);
179 }
180 }
181 pub fn clean_styles(&self, builder: &mut SGRBuilder) {
187 for (kind, place, not) in [
188 (&self.bold, 22, 1),
189 (&self.dim, 22, 2),
190 (&self.italic, 23, 3),
191 (&self.underline, 24, 4),
192 (&self.blinking, 25, 5),
193 (&self.inverse, 27, 7),
194 (&self.hidden, 28, 8),
195 (&self.strikethrough, 29, 9),
196 ] {
197 match kind {
198 StyleKind::None => (),
199 StyleKind::Place => builder.write_code(place),
200 StyleKind::Clean => builder.write_code(not),
201 }
202 }
203 }
204 pub fn clean_custom(&self, builder: &mut SGRBuilder) {
210 builder.write_codes(&self.custom_cleans);
211 }
212}
213impl From<Color> for SGRString {
214 fn from(value: Color) -> Self {
215 Self::default().color(value)
216 }
217}
218impl From<Style> for SGRString {
219 fn from(value: Style) -> Self {
220 Self::default().style(value)
221 }
222}
223impl From<&str> for SGRString {
224 fn from(value: &str) -> Self {
225 Self {
226 text: String::from(value),
227 ..Default::default()
228 }
229 }
230}
231impl From<String> for SGRString {
232 fn from(value: String) -> Self {
233 Self {
234 text: value,
235 ..Default::default()
236 }
237 }
238}
239impl From<&String> for SGRString {
240 fn from(value: &String) -> Self {
241 Self {
242 text: String::from(value),
243 ..Default::default()
244 }
245 }
246}
247impl Display for SGRString {
248 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249 let mut fmt = SGRWriter::from(f);
250 fmt.place_sgr(self)?;
251 fmt.write_inner(&self.text)?;
252 fmt.clean_sgr(self)
253 }
254}
255#[derive(Debug, Clone, Default, PartialEq, Eq)]
257pub enum CleanKind {
258 #[default]
260 None,
261 Reset,
263 Reverse,
265}
266#[derive(Debug, Clone, Default, PartialEq, Eq)]
268pub enum StyleKind {
269 #[default]
271 None,
272 Place,
274 Clean,
278}
279#[derive(Debug, Clone, Default, PartialEq, Eq)]
283#[allow(missing_docs)]
284pub enum ColorKind {
285 #[default]
287 None,
288 Black,
289 Red,
290 Green,
291 Yellow,
292 Blue,
293 Magenta,
294 Cyan,
295 White,
296 Byte(u8),
297 Rgb(u8, u8, u8),
298 Default,
300}
301impl<I: Into<SGRString>> EasySGR for I {}
302pub trait EasySGR: Into<SGRString> {
306 #[must_use]
318 #[inline]
319 fn to_sgr(self) -> SGRString {
320 self.into()
321 }
322 #[must_use]
324 #[inline]
325 fn text(self, text: impl Into<String>) -> SGRString {
326 SGRString {
327 text: text.into(),
328 ..self.into()
329 }
330 }
331 #[must_use]
333 #[inline]
334 fn style(self, style: impl Into<Style>) -> SGRString {
335 use Style::*;
336 use StyleKind::*;
337
338 let mut this = self.into();
339 match style.into() {
340 Reset => this.reset = true,
341 Bold => this.bold = Place,
342 Dim => this.dim = Place,
343 Italic => this.italic = Place,
344 Underline => this.underline = Place,
345 Blinking => this.blinking = Place,
346 Inverse => this.inverse = Place,
347 Hidden => this.hidden = Place,
348 Strikethrough => this.strikethrough = Place,
349
350 NotBold => this.bold = Clean,
351 NotDim => this.dim = Clean,
352 NotItalic => this.italic = Clean,
353 NotUnderline => this.underline = Clean,
354 NotBlinking => this.blinking = Clean,
355 NotInverse => this.inverse = Clean,
356 NotHidden => this.hidden = Clean,
357 NotStrikethrough => this.strikethrough = Clean,
358 }
359 this
360 }
361 #[must_use]
363 #[inline]
364 fn color(self, color: impl Into<Color>) -> SGRString {
365 use {Color::*, ColorKind::*};
366
367 let mut this = self.into();
368
369 (this.foreground, this.background) = match color.into() {
370 BlackFg => (Black, this.background),
371 RedFg => (Red, this.background),
372 GreenFg => (Green, this.background),
373 YellowFg => (Yellow, this.background),
374 BlueFg => (Blue, this.background),
375 MagentaFg => (Magenta, this.background),
376 CyanFg => (Cyan, this.background),
377 WhiteFg => (White, this.background),
378 ByteFg(n) => (Byte(n), this.background),
379 RgbFg(r, g, b) => (Rgb(r, g, b), this.background),
380 DefaultFg => (Default, this.background),
381
382 BlackBg => (this.foreground, Black),
383 RedBg => (this.foreground, Red),
384 GreenBg => (this.foreground, Green),
385 YellowBg => (this.foreground, Yellow),
386 BlueBg => (this.foreground, Blue),
387 MagentaBg => (this.foreground, Magenta),
388 CyanBg => (this.foreground, Cyan),
389 WhiteBg => (this.foreground, White),
390 ByteBg(n) => (this.foreground, Byte(n)),
391 RgbBg(r, g, b) => (this.foreground, Rgb(r, g, b)),
392 DefaultBg => (this.foreground, Default),
393 };
394 this
395 }
396 #[must_use]
400 #[inline]
401 fn custom(self, code: impl Into<u8>) -> SGRString {
402 let mut this = self.into();
403 this.custom_places.push(code.into());
404 this
405 }
406 #[must_use]
408 #[inline]
409 fn clean(self, clean: impl Into<CleanKind>) -> SGRString {
410 let mut this = self.into();
411 this.clean = clean.into();
412 this
413 }
414 #[must_use]
416 #[inline]
417 fn custom_place(self, code: impl Into<u8>) -> SGRString {
418 let mut this = self.into();
419 this.custom_places.push(code.into());
420 this
421 }
422 #[must_use]
424 #[inline]
425 fn custom_clean(self, code: impl Into<u8>) -> SGRString {
426 let mut this = self.into();
427 this.custom_cleans.push(code.into());
428 this
429 }
430}