1 | use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not}; |
---|---|
2 | |
3 | macro_rules! auto_impl_ref_binop_trait { |
4 | (impl $trait_name:ident, $method:ident for $t:ty, $u:ty) => { |
5 | impl $trait_name<&$u> for $t { |
6 | type Output = <$t as $trait_name<$t>>::Output; |
7 | |
8 | #[inline] |
9 | fn $method(self, rhs: &$u) -> Self::Output { |
10 | $trait_name::$method(self, *rhs) |
11 | } |
12 | } |
13 | |
14 | impl $trait_name<$u> for &$t { |
15 | type Output = <$t as $trait_name<$t>>::Output; |
16 | |
17 | #[inline] |
18 | fn $method(self, rhs: $u) -> Self::Output { |
19 | $trait_name::$method(*self, rhs) |
20 | } |
21 | } |
22 | |
23 | impl $trait_name<&$u> for &$t { |
24 | type Output = <$t as $trait_name<$t>>::Output; |
25 | |
26 | #[inline] |
27 | fn $method(self, rhs: &$u) -> Self::Output { |
28 | $trait_name::$method(*self, *rhs) |
29 | } |
30 | } |
31 | }; |
32 | } |
33 | |
34 | macro_rules! impl_assign_op_trait { |
35 | ( |
36 | $trait:ident, $method:ident for $t:ty, $u:ty, using $used_trait:ident::$used_method:ident |
37 | ) => { |
38 | impl $trait<$u> for $t { |
39 | #[inline] |
40 | fn $method(&mut self, other: $u) { |
41 | *self = $used_trait::$used_method(&*self, other); |
42 | } |
43 | } |
44 | |
45 | impl $trait<&$u> for $t { |
46 | #[inline] |
47 | fn $method(&mut self, other: &$u) { |
48 | *self = $used_trait::$used_method(&*self, other); |
49 | } |
50 | } |
51 | }; |
52 | } |
53 | |
54 | const CLEARV: u8 = 0b0000_0000; |
55 | const BOLD: u8 = 0b0000_0001; |
56 | const UNDERLINE: u8 = 0b0000_0010; |
57 | const REVERSED: u8 = 0b0000_0100; |
58 | const ITALIC: u8 = 0b0000_1000; |
59 | const BLINK: u8 = 0b0001_0000; |
60 | const HIDDEN: u8 = 0b0010_0000; |
61 | const DIMMED: u8 = 0b0100_0000; |
62 | const STRIKETHROUGH: u8 = 0b1000_0000; |
63 | |
64 | static STYLES: [(u8, Styles); 8] = [ |
65 | (BOLD, Styles::Bold), |
66 | (DIMMED, Styles::Dimmed), |
67 | (UNDERLINE, Styles::Underline), |
68 | (REVERSED, Styles::Reversed), |
69 | (ITALIC, Styles::Italic), |
70 | (BLINK, Styles::Blink), |
71 | (HIDDEN, Styles::Hidden), |
72 | (STRIKETHROUGH, Styles::Strikethrough), |
73 | ]; |
74 | |
75 | pub static CLEAR: Style = Style(CLEARV); |
76 | |
77 | /// A combinatorial style such as bold, italics, dimmed, etc. |
78 | /// |
79 | /// ## Creation |
80 | /// |
81 | /// `Style::default()` returns a `Style` with no style switches |
82 | /// activated and is the default method of creating a plain `Style`. |
83 | /// |
84 | /// ## `Style` from a set of `Styles`s / `Styles` iterator |
85 | /// |
86 | /// `Style` implements `FromIter<Styles>` which means that it is |
87 | /// possible to do the following: |
88 | /// |
89 | /// ```rust |
90 | /// # use colored::*; |
91 | /// let style = Style::from_iter([Styles::Bold, Styles::Italic, Styles::Strikethrough]); |
92 | /// for styles in [Styles::Bold, Styles::Italic, Styles::Strikethrough] { |
93 | /// assert!(style.contains(styles)); |
94 | /// } |
95 | /// ``` |
96 | /// |
97 | /// As you can see, this is a good thing to keep in mind, although for |
98 | /// most cases, where you're not setting styles dynamically and are |
99 | /// simply creating a pre-defined set of styles, using [`Default`] and |
100 | /// then using the builder-style methods is likely prettier. |
101 | /// |
102 | /// ```rust |
103 | /// # use colored::*; |
104 | /// let many_styles = Style::default() |
105 | /// .bold() |
106 | /// .underline() |
107 | /// .italic() |
108 | /// .blink(); |
109 | /// ``` |
110 | /// |
111 | /// ## Implementation of logical bitwise operators |
112 | /// |
113 | /// `Style` implements bitwise logical operations that operate on |
114 | /// the held style switches collectively. By far the most common |
115 | /// and useful is the bitwise 'or' operator `|` which combines two |
116 | /// styles, merging their combined styles into one. Example: |
117 | /// |
118 | /// ```rust |
119 | /// # use colored::*; |
120 | /// let only_bold = Style::from(Styles::Bold); |
121 | /// // This line is actually an example of `Styles`'s bitwise logic impls but still. |
122 | /// let underline_and_italic = Styles::Underline | Styles::Italic; |
123 | /// let all_three = only_bold | underline_and_italic; |
124 | /// |
125 | /// assert!(all_three.contains(Styles::Bold) |
126 | /// && all_three.contains(Styles::Underline) |
127 | /// && all_three.contains(Styles::Italic)); |
128 | /// ``` |
129 | /// |
130 | /// This functionality also allows for easily turning off styles |
131 | /// of one `Styles` using another by combining the `&` and `!` |
132 | /// operators. |
133 | /// |
134 | /// ```rust |
135 | /// # use colored::*; |
136 | /// let mut very_loud_style = Style::default() |
137 | /// .bold() |
138 | /// .underline() |
139 | /// .italic() |
140 | /// .strikethrough() |
141 | /// .hidden(); |
142 | /// |
143 | /// // Oops! Some of those should not be in there! |
144 | /// // This Style now has all styles _except_ the two we don't want |
145 | /// // (hidden and strikethough). |
146 | /// let remove_mask = |
147 | /// !Style::from_iter([Styles::Hidden, Styles::Strikethrough]); |
148 | /// very_loud_style &= remove_mask; |
149 | /// |
150 | /// // `very_loud_style` no longer contains the undesired style |
151 | /// // switches... |
152 | /// assert!(!very_loud_style.contains(Styles::Hidden) |
153 | /// && !very_loud_style.contains(Styles::Strikethrough)); |
154 | /// // ...but it retains everything else! |
155 | /// assert!(very_loud_style.contains(Styles::Bold)); |
156 | /// ``` |
157 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] |
158 | pub struct Style(u8); |
159 | |
160 | /// Enum containing all of the available style settings that can be |
161 | /// applied to a [`Styles`] and by extension, a colrized type. |
162 | /// |
163 | /// ## Implementation of bitwise logical operators |
164 | /// |
165 | /// The implementations of [`BitAnd`], [`BitOr`], [`BitXor`], and |
166 | /// [`Not`] are really extensions of [`Style`]'s implementations of |
167 | /// the same. [`BitOr`] is great for starting chains of `Styles`'s |
168 | /// for creating [`Style`]'s. |
169 | /// |
170 | /// ``` |
171 | /// # use colored::*; |
172 | /// let my_styles = |
173 | /// // BitOr<Styles> for Styles (Styles | Styles) = Style |
174 | /// Styles::Bold | Styles::Underline |
175 | /// // BitOr<Styles> for Style (Style | Styles) = Style |
176 | /// | Styles::Italic; |
177 | /// |
178 | /// for s in [Styles::Bold, Styles::Underline, Styles::Italic] { |
179 | /// assert!(my_styles.contains(s)); |
180 | /// } |
181 | /// ``` |
182 | /// |
183 | /// [`Not`] has far fewer use cases but can still find use in |
184 | /// turning a `Styles` into a [`Style`] with all styles activated |
185 | /// except that `Styles`. |
186 | /// |
187 | /// ``` |
188 | /// # use colored::*; |
189 | /// let everything_but_bold = !Styles::Bold; |
190 | /// |
191 | /// assert!(everything_but_bold.contains(Styles::Underline)); |
192 | /// assert!(everything_but_bold.contains(Styles::Strikethrough)); |
193 | /// assert!(!everything_but_bold.contains(Styles::Bold)); |
194 | /// ``` |
195 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] |
196 | #[allow(missing_docs)] |
197 | pub enum Styles { |
198 | Clear, |
199 | Bold, |
200 | Dimmed, |
201 | Underline, |
202 | Reversed, |
203 | Italic, |
204 | Blink, |
205 | Hidden, |
206 | Strikethrough, |
207 | } |
208 | |
209 | impl Styles { |
210 | fn to_str<'a>(self) -> &'a str { |
211 | match self { |
212 | Styles::Clear => "", // unreachable, but we don't want to panic |
213 | Styles::Bold => "1", |
214 | Styles::Dimmed => "2", |
215 | Styles::Italic => "3", |
216 | Styles::Underline => "4", |
217 | Styles::Blink => "5", |
218 | Styles::Reversed => "7", |
219 | Styles::Hidden => "8", |
220 | Styles::Strikethrough => "9", |
221 | } |
222 | } |
223 | |
224 | fn to_u8(self) -> u8 { |
225 | match self { |
226 | Styles::Clear => CLEARV, |
227 | Styles::Bold => BOLD, |
228 | Styles::Dimmed => DIMMED, |
229 | Styles::Italic => ITALIC, |
230 | Styles::Underline => UNDERLINE, |
231 | Styles::Blink => BLINK, |
232 | Styles::Reversed => REVERSED, |
233 | Styles::Hidden => HIDDEN, |
234 | Styles::Strikethrough => STRIKETHROUGH, |
235 | } |
236 | } |
237 | |
238 | fn from_u8(u: u8) -> Option<Vec<Styles>> { |
239 | if u == CLEARV { |
240 | return None; |
241 | } |
242 | |
243 | let res: Vec<Styles> = STYLES |
244 | .iter() |
245 | .filter(|&(mask, _)| (0 != (u & mask))) |
246 | .map(|&(_, value)| value) |
247 | .collect(); |
248 | if res.is_empty() { |
249 | None |
250 | } else { |
251 | Some(res) |
252 | } |
253 | } |
254 | } |
255 | |
256 | impl BitAnd<Styles> for Styles { |
257 | type Output = Style; |
258 | |
259 | fn bitand(self, rhs: Styles) -> Self::Output { |
260 | Style(self.to_u8() & rhs.to_u8()) |
261 | } |
262 | } |
263 | |
264 | auto_impl_ref_binop_trait!(impl BitAnd, bitand for Styles, Styles); |
265 | |
266 | impl BitAnd<Style> for Styles { |
267 | type Output = Style; |
268 | |
269 | fn bitand(self, rhs: Style) -> Self::Output { |
270 | Style(self.to_u8() & rhs.0) |
271 | } |
272 | } |
273 | |
274 | auto_impl_ref_binop_trait!(impl BitAnd, bitand for Styles, Style); |
275 | |
276 | impl BitOr<Styles> for Styles { |
277 | type Output = Style; |
278 | |
279 | fn bitor(self, rhs: Styles) -> Self::Output { |
280 | Style(self.to_u8() | rhs.to_u8()) |
281 | } |
282 | } |
283 | |
284 | auto_impl_ref_binop_trait!(impl BitOr, bitor for Styles, Styles); |
285 | |
286 | impl BitOr<Style> for Styles { |
287 | type Output = Style; |
288 | |
289 | fn bitor(self, rhs: Style) -> Self::Output { |
290 | Style(self.to_u8() | rhs.0) |
291 | } |
292 | } |
293 | |
294 | auto_impl_ref_binop_trait!(impl BitOr, bitor for Styles, Style); |
295 | |
296 | impl BitXor<Styles> for Styles { |
297 | type Output = Style; |
298 | |
299 | fn bitxor(self, rhs: Styles) -> Self::Output { |
300 | Style(self.to_u8() ^ rhs.to_u8()) |
301 | } |
302 | } |
303 | |
304 | auto_impl_ref_binop_trait!(impl BitXor, bitxor for Styles, Styles); |
305 | |
306 | impl BitXor<Style> for Styles { |
307 | type Output = Style; |
308 | |
309 | fn bitxor(self, rhs: Style) -> Self::Output { |
310 | Style(self.to_u8() ^ rhs.0) |
311 | } |
312 | } |
313 | |
314 | auto_impl_ref_binop_trait!(impl BitXor, bitxor for Styles, Style); |
315 | |
316 | impl Not for Styles { |
317 | type Output = Style; |
318 | |
319 | fn not(self) -> Self::Output { |
320 | Style(!self.to_u8()) |
321 | } |
322 | } |
323 | |
324 | impl Not for &Styles { |
325 | type Output = Style; |
326 | |
327 | fn not(self) -> Self::Output { |
328 | Style(!self.to_u8()) |
329 | } |
330 | } |
331 | |
332 | impl Style { |
333 | /// Check if the current style has one of [`Styles`](Styles) switched on. |
334 | /// |
335 | /// ```rust |
336 | /// # use colored::*; |
337 | /// let colored = "".bold().italic(); |
338 | /// assert_eq!(colored.style.contains(Styles::Bold), true); |
339 | /// assert_eq!(colored.style.contains(Styles::Italic), true); |
340 | /// assert_eq!(colored.style.contains(Styles::Dimmed), false); |
341 | /// ``` |
342 | pub fn contains(self, style: Styles) -> bool { |
343 | let s = style.to_u8(); |
344 | self.0 & s == s |
345 | } |
346 | |
347 | pub(crate) fn to_str(self) -> String { |
348 | let styles = Styles::from_u8(self.0).unwrap_or_default(); |
349 | styles |
350 | .iter() |
351 | .map(|s| s.to_str()) |
352 | .collect::<Vec<&str>>() |
353 | .join(";") |
354 | } |
355 | |
356 | /// Adds the `two` style switch to this Style. |
357 | /// |
358 | /// ```rust |
359 | /// # use colored::*; |
360 | /// let cstr = "".red().bold(); |
361 | /// let mut style = cstr.style; |
362 | /// style.add(Styles::Italic); |
363 | /// let mut cstr2 = "".blue(); |
364 | /// cstr2.style = style; |
365 | /// |
366 | /// assert!(cstr2.style.contains(Styles::Bold)); |
367 | /// assert!(cstr2.style.contains(Styles::Italic)); |
368 | /// assert_eq!(cstr2.fgcolor, Some(Color::Blue)); |
369 | /// ``` |
370 | pub fn add(&mut self, two: Styles) { |
371 | self.0 |= two.to_u8(); |
372 | } |
373 | |
374 | /// Turns off a style switch. |
375 | /// |
376 | /// ```rust |
377 | /// use colored::*; |
378 | /// let cstr = "".red().bold().italic(); |
379 | /// let mut style = cstr.style; |
380 | /// style.remove(Styles::Italic); |
381 | /// let mut cstr2 = "".blue(); |
382 | /// cstr2.style = style; |
383 | /// assert!(cstr2.style.contains(Styles::Bold)); |
384 | /// assert!(!cstr2.style.contains(Styles::Italic)); |
385 | /// assert_eq!(cstr2.fgcolor, Some(Color::Blue)); |
386 | /// ``` |
387 | pub fn remove(&mut self, two: Styles) { |
388 | self.0 &= !two.to_u8(); |
389 | } |
390 | |
391 | /// Makes this `Style` include Bold. |
392 | pub fn bold(mut self) -> Self { |
393 | self.add(Styles::Bold); |
394 | self |
395 | } |
396 | |
397 | /// Makes this `Style` include Dimmed. |
398 | pub fn dimmed(mut self) -> Self { |
399 | self.add(Styles::Dimmed); |
400 | self |
401 | } |
402 | |
403 | /// Makes this `Style` include Underline. |
404 | pub fn underline(mut self) -> Self { |
405 | self.add(Styles::Underline); |
406 | self |
407 | } |
408 | |
409 | /// Makes this `Style` include Reversed. |
410 | pub fn reversed(mut self) -> Self { |
411 | self.add(Styles::Reversed); |
412 | self |
413 | } |
414 | |
415 | /// Makes this `Style` include Italic. |
416 | pub fn italic(mut self) -> Self { |
417 | self.add(Styles::Italic); |
418 | self |
419 | } |
420 | |
421 | /// Makes this `Style` include Blink. |
422 | pub fn blink(mut self) -> Self { |
423 | self.add(Styles::Blink); |
424 | self |
425 | } |
426 | |
427 | /// Makes this `Style` include Hidden. |
428 | pub fn hidden(mut self) -> Self { |
429 | self.add(Styles::Hidden); |
430 | self |
431 | } |
432 | |
433 | /// Makes this `Style` include Strikethrough. |
434 | pub fn strikethrough(mut self) -> Self { |
435 | self.add(Styles::Strikethrough); |
436 | self |
437 | } |
438 | } |
439 | |
440 | impl BitAnd<Style> for Style { |
441 | type Output = Style; |
442 | |
443 | fn bitand(self, rhs: Style) -> Self::Output { |
444 | Style(self.0 & rhs.0) |
445 | } |
446 | } |
447 | |
448 | auto_impl_ref_binop_trait!(impl BitAnd, bitand for Style, Style); |
449 | |
450 | impl BitAnd<Styles> for Style { |
451 | type Output = Style; |
452 | |
453 | fn bitand(self, rhs: Styles) -> Self::Output { |
454 | Style(self.0 & rhs.to_u8()) |
455 | } |
456 | } |
457 | |
458 | auto_impl_ref_binop_trait!(impl BitAnd, bitand for Style, Styles); |
459 | |
460 | impl BitOr<Style> for Style { |
461 | type Output = Style; |
462 | |
463 | fn bitor(self, rhs: Style) -> Self::Output { |
464 | Style(self.0 | rhs.0) |
465 | } |
466 | } |
467 | |
468 | auto_impl_ref_binop_trait!(impl BitOr, bitor for Style, Style); |
469 | |
470 | impl BitOr<Styles> for Style { |
471 | type Output = Style; |
472 | |
473 | fn bitor(self, rhs: Styles) -> Self::Output { |
474 | Style(self.0 | rhs.to_u8()) |
475 | } |
476 | } |
477 | |
478 | auto_impl_ref_binop_trait!(impl BitOr, bitor for Style, Styles); |
479 | |
480 | impl BitXor<Style> for Style { |
481 | type Output = Style; |
482 | |
483 | fn bitxor(self, rhs: Style) -> Self::Output { |
484 | Style(self.0 ^ rhs.0) |
485 | } |
486 | } |
487 | |
488 | auto_impl_ref_binop_trait!(impl BitXor, bitxor for Style, Style); |
489 | |
490 | impl BitXor<Styles> for Style { |
491 | type Output = Style; |
492 | |
493 | fn bitxor(self, rhs: Styles) -> Self::Output { |
494 | Style(self.0 ^ rhs.to_u8()) |
495 | } |
496 | } |
497 | |
498 | auto_impl_ref_binop_trait!(impl BitXor, bitxor for Style, Styles); |
499 | |
500 | impl Not for Style { |
501 | type Output = Style; |
502 | |
503 | fn not(self) -> Self::Output { |
504 | Style(!self.0) |
505 | } |
506 | } |
507 | |
508 | impl Not for &Style { |
509 | type Output = Style; |
510 | |
511 | fn not(self) -> Self::Output { |
512 | Style(!self.0) |
513 | } |
514 | } |
515 | |
516 | impl_assign_op_trait!(BitAndAssign, bitand_assign for Style, Style, using BitAnd::bitand); |
517 | |
518 | impl_assign_op_trait!(BitAndAssign, bitand_assign for Style, Styles, using BitAnd::bitand); |
519 | |
520 | impl_assign_op_trait!(BitOrAssign, bitor_assign for Style, Style, using BitOr::bitor); |
521 | |
522 | impl_assign_op_trait!(BitOrAssign, bitor_assign for Style, Styles, using BitOr::bitor); |
523 | |
524 | impl_assign_op_trait!(BitXorAssign, bitxor_assign for Style, Style, using BitXor::bitxor); |
525 | |
526 | impl_assign_op_trait!(BitXorAssign, bitxor_assign for Style, Styles, using BitXor::bitxor); |
527 | |
528 | impl Default for Style { |
529 | fn default() -> Self { |
530 | CLEAR |
531 | } |
532 | } |
533 | |
534 | impl From<Styles> for Style { |
535 | fn from(value: Styles) -> Self { |
536 | Style(value.to_u8()) |
537 | } |
538 | } |
539 | |
540 | impl From<&Styles> for Style { |
541 | fn from(value: &Styles) -> Self { |
542 | Style(value.to_u8()) |
543 | } |
544 | } |
545 | |
546 | impl FromIterator<Styles> for Style { |
547 | fn from_iter<T: IntoIterator<Item = Styles>>(iter: T) -> Self { |
548 | let mut style: Style = Style::default(); |
549 | for styles: Styles in iter.into_iter() { |
550 | style.add(two:styles); |
551 | } |
552 | style |
553 | } |
554 | } |
555 | |
556 | #[cfg(test)] |
557 | mod tests { |
558 | use super::*; |
559 | |
560 | mod u8_to_styles_invalid_is_none { |
561 | use super::super::Styles; |
562 | use super::super::CLEARV; |
563 | |
564 | #[test] |
565 | fn empty_is_none() { |
566 | assert_eq!(None, Styles::from_u8(CLEARV)); |
567 | } |
568 | } |
569 | |
570 | mod u8_to_styles_isomorphism { |
571 | use super::super::Styles; |
572 | use super::super::{ |
573 | BLINK, BOLD, DIMMED, HIDDEN, ITALIC, REVERSED, STRIKETHROUGH, UNDERLINE, |
574 | }; |
575 | |
576 | macro_rules! value_isomorph { |
577 | ($name:ident, $value:expr) => { |
578 | #[test] |
579 | fn $name() { |
580 | let u = Styles::from_u8($value); |
581 | assert!( |
582 | u.is_some(), |
583 | "{}: Styles::from_u8 -> None", |
584 | stringify!($value) |
585 | ); |
586 | let u = u.unwrap(); |
587 | assert!( |
588 | u.len() == 1, |
589 | "{}: Styles::from_u8 found {} styles (expected 1)", |
590 | stringify!($value), |
591 | u.len() |
592 | ); |
593 | assert!( |
594 | u[0].to_u8() == $value, |
595 | "{}: to_u8() doesn't match its const value", |
596 | stringify!($value) |
597 | ); |
598 | } |
599 | }; |
600 | } |
601 | |
602 | value_isomorph!(bold, BOLD); |
603 | value_isomorph!(underline, UNDERLINE); |
604 | value_isomorph!(reversed, REVERSED); |
605 | value_isomorph!(italic, ITALIC); |
606 | value_isomorph!(blink, BLINK); |
607 | value_isomorph!(hidden, HIDDEN); |
608 | value_isomorph!(dimmed, DIMMED); |
609 | value_isomorph!(strikethrough, STRIKETHROUGH); |
610 | } |
611 | |
612 | mod styles_combine_complex { |
613 | use super::super::Styles::*; |
614 | use super::super::{Style, Styles}; |
615 | |
616 | fn style_from_multiples(styles: &[Styles]) -> Style { |
617 | let mut res = Style(styles[0].to_u8()); |
618 | for s in &styles[1..] { |
619 | res = Style(res.0 | s.to_u8()); |
620 | } |
621 | res |
622 | } |
623 | |
624 | macro_rules! test_aggreg { |
625 | ($styles:expr, $expect:expr) => {{ |
626 | let v = style_from_multiples($styles); |
627 | let r = Styles::from_u8(v.0).expect("should find styles"); |
628 | assert_eq!(&$expect as &[Styles], &r[..]) |
629 | }}; |
630 | } |
631 | |
632 | #[test] |
633 | fn aggreg1() { |
634 | let styles: &[Styles] = &[Bold, Bold, Bold]; |
635 | test_aggreg!(styles, [Bold]); |
636 | } |
637 | |
638 | #[test] |
639 | fn aggreg2() { |
640 | let styles: &[Styles] = &[Italic, Italic, Bold, Bold]; |
641 | test_aggreg!(styles, [Bold, Italic]); |
642 | } |
643 | |
644 | #[test] |
645 | fn aggreg3() { |
646 | let styles: &[Styles] = &[Bold, Italic, Bold]; |
647 | test_aggreg!(styles, [Bold, Italic]); |
648 | } |
649 | |
650 | macro_rules! test_combine { |
651 | ($styles:expr) => {{ |
652 | let v = style_from_multiples($styles); |
653 | let r = Styles::from_u8(v.0).expect("should find styles"); |
654 | assert_eq!($styles, &r[..]) |
655 | }}; |
656 | } |
657 | |
658 | #[test] |
659 | fn two1() { |
660 | let s: &[Styles] = &[Bold, Underline]; |
661 | test_combine!(s); |
662 | } |
663 | |
664 | #[test] |
665 | fn two2() { |
666 | let s: &[Styles] = &[Underline, Italic]; |
667 | test_combine!(s); |
668 | } |
669 | |
670 | #[test] |
671 | fn two3() { |
672 | let s: &[Styles] = &[Bold, Italic]; |
673 | test_combine!(s); |
674 | } |
675 | |
676 | #[test] |
677 | fn three1() { |
678 | let s: &[Styles] = &[Bold, Underline, Italic]; |
679 | test_combine!(s); |
680 | } |
681 | |
682 | #[test] |
683 | fn three2() { |
684 | let s: &[Styles] = &[Dimmed, Underline, Italic]; |
685 | test_combine!(s); |
686 | } |
687 | |
688 | #[test] |
689 | fn four() { |
690 | let s: &[Styles] = &[Dimmed, Underline, Italic, Hidden]; |
691 | test_combine!(s); |
692 | } |
693 | |
694 | #[test] |
695 | fn five() { |
696 | let s: &[Styles] = &[Dimmed, Underline, Italic, Blink, Hidden]; |
697 | test_combine!(s); |
698 | } |
699 | |
700 | #[test] |
701 | fn six() { |
702 | let s: &[Styles] = &[Bold, Dimmed, Underline, Italic, Blink, Hidden]; |
703 | test_combine!(s); |
704 | } |
705 | |
706 | #[test] |
707 | fn all() { |
708 | let s: &[Styles] = &[ |
709 | Bold, |
710 | Dimmed, |
711 | Underline, |
712 | Reversed, |
713 | Italic, |
714 | Blink, |
715 | Hidden, |
716 | Strikethrough, |
717 | ]; |
718 | test_combine!(s); |
719 | } |
720 | } |
721 | |
722 | #[test] |
723 | fn test_style_contains() { |
724 | let mut style = Style(Styles::Bold.to_u8()); |
725 | style.add(Styles::Italic); |
726 | |
727 | assert!(style.contains(Styles::Bold)); |
728 | assert!(style.contains(Styles::Italic)); |
729 | assert!(!style.contains(Styles::Dimmed)); |
730 | } |
731 | |
732 | mod style_bitwise_logic { |
733 | use super::*; |
734 | |
735 | macro_rules! check_impl { |
736 | ($lh:expr, $method:path, $rh:expr => $res:expr) => { |
737 | assert_eq!($method($lh, $rh), $res); |
738 | assert_eq!($method(&$lh, $rh), $res); |
739 | assert_eq!($method($lh, &$rh), $res); |
740 | assert_eq!($method(&$lh, &$rh), $res); |
741 | }; |
742 | } |
743 | |
744 | macro_rules! check_impl_reflexive { |
745 | ($lh:expr, $method:path, $rh:expr => $res:expr) => { |
746 | check_impl!($lh, $method, $rh => $res); |
747 | check_impl!($rh, $method, $lh => $res); |
748 | } |
749 | } |
750 | |
751 | /// TTABLE = TRUTH_TABLE |
752 | const TTABLE: (u8, u8) = (0b0101, 0b0011); |
753 | |
754 | #[test] |
755 | fn binops() { |
756 | let tstyle_l = Style(TTABLE.0); |
757 | let tstyle_r = Style(TTABLE.1); |
758 | let and_res = Style(TTABLE.0 & TTABLE.1); |
759 | let or_res = Style(TTABLE.0 | TTABLE.1); |
760 | let xor_res = Style(TTABLE.0 ^ TTABLE.1); |
761 | |
762 | check_impl!(tstyle_l, BitAnd::bitand, tstyle_r => and_res); |
763 | check_impl!(tstyle_l, BitOr::bitor, tstyle_r => or_res); |
764 | check_impl!(tstyle_l, BitXor::bitxor, tstyle_r => xor_res); |
765 | } |
766 | |
767 | #[test] |
768 | fn binops_with_styles() { |
769 | let bold_underline = Style(0b0011); |
770 | |
771 | check_impl_reflexive!( |
772 | bold_underline, |
773 | BitAnd::bitand, |
774 | Styles::Bold |
775 | => Style(0b0000_0001) |
776 | ); |
777 | check_impl_reflexive!( |
778 | bold_underline, |
779 | BitOr::bitor, |
780 | Styles::Reversed |
781 | => Style(0b0000_0111) |
782 | ); |
783 | check_impl_reflexive!( |
784 | bold_underline, |
785 | BitXor::bitxor, |
786 | Styles::Underline |
787 | => Style(0b0000_0001) |
788 | ); |
789 | } |
790 | |
791 | #[test] |
792 | fn not() { |
793 | let not_bold = !Style(BOLD); |
794 | assert!(!not_bold.contains(Styles::Bold)); |
795 | assert!(not_bold.contains(Styles::Strikethrough)); |
796 | assert_eq!(!Style(0b0011_0101), Style(0b1100_1010)); |
797 | } |
798 | |
799 | #[test] |
800 | fn assign_ops() { |
801 | let original_style = Style(0b0011); |
802 | let op_style = Style(0b0101); |
803 | |
804 | let mut style = original_style; |
805 | style &= op_style; |
806 | assert_eq!(style, Style(0b0001)); |
807 | |
808 | style = original_style; |
809 | style |= op_style; |
810 | assert_eq!(style, Style(0b0111)); |
811 | |
812 | style = original_style; |
813 | style ^= op_style; |
814 | assert_eq!(style, Style(0b0110)); |
815 | } |
816 | |
817 | #[test] |
818 | fn assign_ops_with_styles() { |
819 | let original_style = Style(0b0011); |
820 | |
821 | let mut style = original_style; |
822 | style &= Styles::Bold; |
823 | assert_eq!(style, Style(0b0001)); |
824 | |
825 | style = original_style; |
826 | style |= Styles::Reversed; |
827 | assert_eq!(style, Style(0b0111)); |
828 | |
829 | style = original_style; |
830 | style ^= Styles::Bold; |
831 | assert_eq!(style, Style(0b0010)); |
832 | } |
833 | |
834 | #[test] |
835 | fn styles_binops() { |
836 | check_impl!( |
837 | Styles::Bold, |
838 | BitAnd::bitand, |
839 | Styles::Bold |
840 | => Style(0b0000_0001) |
841 | ); |
842 | // The check_impl is only to verify it works with all the combos |
843 | // of refs. We already know it compines so let's spare ourselves |
844 | // the extra assertions. |
845 | assert_eq!(Styles::Bold & Styles::Underline, Style(0b0000_0000)); |
846 | |
847 | check_impl!( |
848 | Styles::Bold, |
849 | BitOr::bitor, |
850 | Styles::Underline |
851 | => Style(0b0000_0011) |
852 | ); |
853 | assert_eq!(Styles::Bold | Styles::Bold, Style(0b0000_0001)); |
854 | |
855 | check_impl!( |
856 | Styles::Bold, |
857 | BitXor::bitxor, |
858 | Styles::Underline |
859 | => Style(0b0000_0011) |
860 | ); |
861 | assert_eq!(Styles::Bold ^ Styles::Bold, Style(0b0000_0000)); |
862 | } |
863 | |
864 | #[test] |
865 | fn styles_not() { |
866 | let not_bold = !Styles::Bold; |
867 | assert_eq!(not_bold, Style(!BOLD)); |
868 | } |
869 | } |
870 | } |
871 |
Definitions
- auto_impl_ref_binop_trait
- impl_assign_op_trait
- Style
- Styles
- Clear
- Bold
- Dimmed
- Underline
- Reversed
- Italic
- Blink
- Hidden
- Strikethrough
- to_str
- to_u8
- from_u8
- Output
- bitand
- bitand
- Output
- bitand
- bitand
- Output
- bitor
- bitor
- Output
- bitor
- bitor
- Output
- bitxor
- bitxor
- Output
- bitxor
- bitxor
- Output
- not
- Output
- not
- contains
- to_str
- add
- remove
- bold
- dimmed
- underline
- reversed
- italic
- blink
- hidden
- strikethrough
- Output
- bitand
- bitand
- Output
- bitand
- bitand
- Output
- bitor
- bitor
- Output
- bitor
- bitor
- Output
- bitxor
- bitxor
- Output
- bitxor
- bitxor
- Output
- not
- Output
- not
- bitand_assign
- bitand_assign
- bitor_assign
- bitor_assign
- bitxor_assign
- bitxor_assign
- default
- from
- from
Learn Rust with the experts
Find out more