radicle_term/ansi/
style.rs1use std::fmt::{self, Display};
2use std::hash::{Hash, Hasher};
3use std::ops::BitOr;
4
5use super::{Color, Paint};
6
7#[derive(Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone)]
8pub struct Property(u8);
9
10impl Property {
11 pub const BOLD: Self = Property(1 << 0);
12 pub const DIM: Self = Property(1 << 1);
13 pub const ITALIC: Self = Property(1 << 2);
14 pub const UNDERLINE: Self = Property(1 << 3);
15 pub const BLINK: Self = Property(1 << 4);
16 pub const INVERT: Self = Property(1 << 5);
17 pub const HIDDEN: Self = Property(1 << 6);
18 pub const STRIKETHROUGH: Self = Property(1 << 7);
19
20 pub const fn new() -> Self {
21 Property(0)
22 }
23
24 #[inline(always)]
25 pub const fn contains(self, other: Property) -> bool {
26 (other.0 & self.0) == other.0
27 }
28
29 #[inline(always)]
30 pub fn set(&mut self, other: Property) {
31 self.0 |= other.0;
32 }
33
34 #[inline(always)]
35 pub fn iter(self) -> Iter {
36 Iter {
37 index: 0,
38 properties: self,
39 }
40 }
41}
42
43impl BitOr for Property {
44 type Output = Self;
45
46 #[inline(always)]
47 fn bitor(self, rhs: Self) -> Self {
48 Property(self.0 | rhs.0)
49 }
50}
51
52pub struct Iter {
53 index: u8,
54 properties: Property,
55}
56
57impl Iterator for Iter {
58 type Item = usize;
59
60 fn next(&mut self) -> Option<Self::Item> {
61 while self.index < 8 {
62 let index = self.index;
63 self.index += 1;
64
65 if self.properties.contains(Property(1 << index)) {
66 return Some(index as usize);
67 }
68 }
69
70 None
71 }
72}
73
74#[repr(C, packed)]
76#[derive(Default, Debug, Eq, Ord, PartialOrd, Copy, Clone)]
77pub struct Style {
78 pub(crate) foreground: Color,
79 pub(crate) background: Color,
80 pub(crate) properties: Property,
81 pub(crate) wrap: bool,
82}
83
84impl PartialEq for Style {
85 fn eq(&self, other: &Style) -> bool {
86 self.foreground == other.foreground
87 && self.background == other.background
88 && self.properties == other.properties
89 }
90}
91
92impl Hash for Style {
93 fn hash<H: Hasher>(&self, state: &mut H) {
94 self.foreground.hash(state);
95 self.background.hash(state);
96 self.properties.hash(state);
97 }
98}
99
100#[inline]
101fn write_spliced<T: Display>(c: &mut bool, f: &mut dyn fmt::Write, t: T) -> fmt::Result {
102 if *c {
103 write!(f, ";{t}")
104 } else {
105 *c = true;
106 write!(f, "{t}")
107 }
108}
109
110impl Style {
111 #[inline]
114 pub const fn new(color: Color) -> Style {
115 Self {
117 foreground: color,
118 background: Color::Unset,
119 properties: Property::new(),
120 wrap: false,
121 }
122 }
123
124 #[inline]
126 pub const fn fg(mut self, color: Color) -> Style {
127 self.foreground = color;
128 self
129 }
130
131 #[inline]
133 pub const fn bg(mut self, color: Color) -> Style {
134 self.background = color;
135 self
136 }
137
138 pub fn merge(mut self, other: Style) -> Style {
141 if self.foreground == Color::Unset {
142 self.foreground = other.foreground;
143 }
144 if self.background == Color::Unset {
145 self.background = other.background;
146 }
147 self.properties.set(other.properties);
148 self
149 }
150
151 #[inline]
163 pub const fn wrap(mut self) -> Style {
164 self.wrap = true;
165 self
166 }
167
168 pub fn bold(mut self) -> Self {
169 self.properties.set(Property::BOLD);
170 self
171 }
172
173 pub fn dim(mut self) -> Self {
174 self.properties.set(Property::DIM);
175 self
176 }
177
178 pub fn italic(mut self) -> Self {
179 self.properties.set(Property::ITALIC);
180 self
181 }
182
183 pub fn underline(mut self) -> Self {
184 self.properties.set(Property::UNDERLINE);
185 self
186 }
187
188 pub fn invert(mut self) -> Self {
189 self.properties.set(Property::INVERT);
190 self
191 }
192
193 pub fn strikethrough(mut self) -> Self {
194 self.properties.set(Property::STRIKETHROUGH);
195 self
196 }
197
198 #[inline]
201 pub fn paint<T>(self, item: T) -> Paint<T> {
202 Paint::new(item).with_style(self)
203 }
204
205 #[inline]
207 pub const fn fg_color(&self) -> Color {
208 self.foreground
209 }
210
211 #[inline]
213 pub const fn bg_color(&self) -> Color {
214 self.background
215 }
216
217 #[inline]
219 pub const fn is_wrapping(&self) -> bool {
220 self.wrap
221 }
222
223 #[inline(always)]
224 fn is_plain(&self) -> bool {
225 self == &Style::default()
226 }
227
228 pub fn fmt_prefix(&self, f: &mut dyn fmt::Write) -> fmt::Result {
238 if self.is_plain() {
240 return Ok(());
241 }
242
243 let mut splice = false;
244 write!(f, "\x1B[")?;
245
246 for i in self.properties.iter() {
247 let k = if i >= 5 { i + 2 } else { i + 1 };
248 write_spliced(&mut splice, f, k)?;
249 }
250
251 if self.background != Color::Unset {
252 write_spliced(&mut splice, f, "4")?;
253 self.background.ansi_fmt(f)?;
254 }
255
256 if self.foreground != Color::Unset {
257 write_spliced(&mut splice, f, "3")?;
258 self.foreground.ansi_fmt(f)?;
259 }
260
261 write!(f, "m")
263 }
264
265 pub fn fmt_suffix(&self, f: &mut dyn fmt::Write) -> fmt::Result {
275 if self.is_plain() {
276 return Ok(());
277 }
278 write!(f, "\x1B[0m")
279 }
280}