spec_ai/spec_ai_tui/style/
modifier.rs1use crossterm::style::Attribute;
4use std::ops::{BitOr, BitOrAssign};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
8pub struct Modifier(u16);
9
10impl Modifier {
11 pub const NONE: Self = Self(0);
13 pub const BOLD: Self = Self(1 << 0);
15 pub const DIM: Self = Self(1 << 1);
17 pub const ITALIC: Self = Self(1 << 2);
19 pub const UNDERLINED: Self = Self(1 << 3);
21 pub const SLOW_BLINK: Self = Self(1 << 4);
23 pub const RAPID_BLINK: Self = Self(1 << 5);
25 pub const REVERSED: Self = Self(1 << 6);
27 pub const HIDDEN: Self = Self(1 << 7);
29 pub const CROSSED_OUT: Self = Self(1 << 8);
31
32 pub const fn empty() -> Self {
34 Self::NONE
35 }
36
37 pub const fn is_empty(&self) -> bool {
39 self.0 == 0
40 }
41
42 pub const fn contains(&self, other: Self) -> bool {
44 (self.0 & other.0) == other.0
45 }
46
47 pub const fn union(self, other: Self) -> Self {
49 Self(self.0 | other.0)
50 }
51
52 pub const fn intersection(self, other: Self) -> Self {
54 Self(self.0 & other.0)
55 }
56
57 pub const fn difference(self, other: Self) -> Self {
59 Self(self.0 & !other.0)
60 }
61
62 pub fn insert(&mut self, other: Self) {
64 self.0 |= other.0;
65 }
66
67 pub fn remove(&mut self, other: Self) {
69 self.0 &= !other.0;
70 }
71
72 pub fn attributes(&self) -> Vec<Attribute> {
74 let mut attrs = Vec::new();
75 if self.contains(Self::BOLD) {
76 attrs.push(Attribute::Bold);
77 }
78 if self.contains(Self::DIM) {
79 attrs.push(Attribute::Dim);
80 }
81 if self.contains(Self::ITALIC) {
82 attrs.push(Attribute::Italic);
83 }
84 if self.contains(Self::UNDERLINED) {
85 attrs.push(Attribute::Underlined);
86 }
87 if self.contains(Self::SLOW_BLINK) {
88 attrs.push(Attribute::SlowBlink);
89 }
90 if self.contains(Self::RAPID_BLINK) {
91 attrs.push(Attribute::RapidBlink);
92 }
93 if self.contains(Self::REVERSED) {
94 attrs.push(Attribute::Reverse);
95 }
96 if self.contains(Self::HIDDEN) {
97 attrs.push(Attribute::Hidden);
98 }
99 if self.contains(Self::CROSSED_OUT) {
100 attrs.push(Attribute::CrossedOut);
101 }
102 attrs
103 }
104}
105
106impl BitOr for Modifier {
107 type Output = Self;
108
109 fn bitor(self, rhs: Self) -> Self::Output {
110 self.union(rhs)
111 }
112}
113
114impl BitOrAssign for Modifier {
115 fn bitor_assign(&mut self, rhs: Self) {
116 self.insert(rhs);
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn test_modifier_empty() {
126 let m = Modifier::empty();
127 assert!(m.is_empty());
128 assert!(!m.contains(Modifier::BOLD));
129 }
130
131 #[test]
132 fn test_modifier_union() {
133 let m = Modifier::BOLD | Modifier::ITALIC;
134 assert!(m.contains(Modifier::BOLD));
135 assert!(m.contains(Modifier::ITALIC));
136 assert!(!m.contains(Modifier::UNDERLINED));
137 }
138
139 #[test]
140 fn test_modifier_insert_remove() {
141 let mut m = Modifier::BOLD;
142 m.insert(Modifier::ITALIC);
143 assert!(m.contains(Modifier::BOLD));
144 assert!(m.contains(Modifier::ITALIC));
145
146 m.remove(Modifier::BOLD);
147 assert!(!m.contains(Modifier::BOLD));
148 assert!(m.contains(Modifier::ITALIC));
149 }
150
151 #[test]
152 fn test_modifier_attributes() {
153 let m = Modifier::BOLD | Modifier::UNDERLINED;
154 let attrs = m.attributes();
155 assert_eq!(attrs.len(), 2);
156 assert!(attrs.contains(&Attribute::Bold));
157 assert!(attrs.contains(&Attribute::Underlined));
158 }
159}