1use std::{fmt, str::FromStr};
2
3use crossterm::style::Attributes;
4use serde::Deserialize;
5
6macro_rules! Attribute {
7 (
8 $(
9 $(#[$inner:ident $($args:tt)*])*
10 $name:ident = $sgr:expr,
11 )*
12 ) => {
13 #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Deserialize)]
14 #[non_exhaustive]
15 #[serde(rename_all = "snake_case")]
16 pub enum Attribute {
17 $(
18 $(#[$inner $($args)*])*
19 $name,
20 )*
21 }
22
23 impl Attribute {
24 pub fn iterator() -> impl Iterator<Item = Attribute> {
25 use self::Attribute::*;
26 [ $($name,)* ].into_iter()
27 }
28 }
29 }
30}
31
32Attribute! {
33 Reset = 0,
35 Bold = 1,
37 Dim = 2,
39 Italic = 3,
41 Underlined = 4,
43
44 DoubleUnderlined = 2,
47 Undercurled = 3,
49 Underdotted = 4,
51 Underdashed = 5,
53
54 SlowBlink = 5,
56 RapidBlink = 6,
58 Reverse = 7,
60 Hidden = 8,
62 CrossedOut = 9,
64 Fraktur = 20,
68 NoBold = 21,
70 NormalIntensity = 22,
72 NoItalic = 23,
74 NoUnderline = 24,
76 NoBlink = 25,
78 NoReverse = 27,
80 NoHidden = 28,
82 NotCrossedOut = 29,
84 Framed = 51,
86 Encircled = 52,
88 OverLined = 53,
90 NotFramedOrEncircled = 54,
92 NotOverLined = 55,
94}
95
96impl From<Attribute> for crossterm::style::Attribute {
97 fn from(value: Attribute) -> Self {
98 use crossterm::style;
99 use Attribute::*;
100
101 match value {
102 Reset => style::Attribute::Reset,
103 Bold => style::Attribute::Bold,
104 Dim => style::Attribute::Dim,
105 Italic => style::Attribute::Italic,
106 Underlined => style::Attribute::Underlined,
107 DoubleUnderlined => style::Attribute::DoubleUnderlined,
108 Undercurled => style::Attribute::Undercurled,
109 Underdotted => style::Attribute::Underdotted,
110 Underdashed => style::Attribute::Underdashed,
111 SlowBlink => style::Attribute::SlowBlink,
112 RapidBlink => style::Attribute::RapidBlink,
113 Reverse => style::Attribute::Reverse,
114 Hidden => style::Attribute::Hidden,
115 CrossedOut => style::Attribute::CrossedOut,
116 Fraktur => style::Attribute::Fraktur,
117 NoBold => style::Attribute::NoBold,
118 NormalIntensity => style::Attribute::NormalIntensity,
119 NoItalic => style::Attribute::NoItalic,
120 NoUnderline => style::Attribute::NoUnderline,
121 NoBlink => style::Attribute::NoBlink,
122 NoReverse => style::Attribute::NoReverse,
123 NoHidden => style::Attribute::NoHidden,
124 NotCrossedOut => style::Attribute::NotCrossedOut,
125 Framed => style::Attribute::Framed,
126 Encircled => style::Attribute::Encircled,
127 OverLined => style::Attribute::OverLined,
128 NotFramedOrEncircled => style::Attribute::NotFramedOrEncircled,
129 NotOverLined => style::Attribute::NotOverLined,
130 }
131 }
132}
133
134impl FromStr for Attribute {
135 type Err = ();
136
137 fn from_str(s: &str) -> Result<Self, Self::Err> {
138 match s {
139 "reset" => Ok(Attribute::Reset),
140 "bold" => Ok(Attribute::Bold),
141 "dim" => Ok(Attribute::Dim),
142 "italic" => Ok(Attribute::Italic),
143 "underlined" => Ok(Attribute::Underlined),
144 "double_underlined" => Ok(Attribute::DoubleUnderlined),
145 "undercurled" => Ok(Attribute::Undercurled),
146 "underdotted" => Ok(Attribute::Underdotted),
147 "underdashed" => Ok(Attribute::Underdashed),
148 "slow_blink" => Ok(Attribute::SlowBlink),
149 "rapid_blink" => Ok(Attribute::RapidBlink),
150 "reverse" => Ok(Attribute::Reverse),
151 "hidden" => Ok(Attribute::Hidden),
152 "crossed_out" => Ok(Attribute::CrossedOut),
153 "fraktur" => Ok(Attribute::Fraktur),
154 "no_bold" => Ok(Attribute::NoBold),
155 "normal_intensity" => Ok(Attribute::NormalIntensity),
156 "no_italic" => Ok(Attribute::NoItalic),
157 "no_underline" => Ok(Attribute::NoUnderline),
158 "no_blink" => Ok(Attribute::NoBlink),
159 "no_reverse" => Ok(Attribute::NoReverse),
160 "no_hidden" => Ok(Attribute::NoHidden),
161 "not_crossed_out" => Ok(Attribute::NotCrossedOut),
162 "framed" => Ok(Attribute::Framed),
163 "encircled" => Ok(Attribute::Encircled),
164 "overlined" => Ok(Attribute::OverLined),
165 "not_framed_or_encircled" => Ok(Attribute::NotFramedOrEncircled),
166 "not_overlined" => Ok(Attribute::NotOverLined),
167 _ => Err(()),
168 }
169 }
170}
171
172impl fmt::Display for Attribute {
173 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174 match self {
175 Attribute::Reset => write!(f, "reset"),
176 Attribute::Bold => write!(f, "bold"),
177 Attribute::Dim => write!(f, "dim"),
178 Attribute::Italic => write!(f, "italic"),
179 Attribute::Underlined => write!(f, "underlined"),
180 Attribute::DoubleUnderlined => write!(f, "double_underlined"),
181 Attribute::Undercurled => write!(f, "undercurled"),
182 Attribute::Underdotted => write!(f, "underdotted"),
183 Attribute::Underdashed => write!(f, "underdashed"),
184 Attribute::SlowBlink => write!(f, "slow_blink"),
185 Attribute::RapidBlink => write!(f, "rapid_blink"),
186 Attribute::Reverse => write!(f, "reverse"),
187 Attribute::Hidden => write!(f, "hidden"),
188 Attribute::CrossedOut => write!(f, "crossed_out"),
189 Attribute::Fraktur => write!(f, "fraktur"),
190 Attribute::NoBold => write!(f, "no_bold"),
191 Attribute::NormalIntensity => write!(f, "normal_intensity"),
192 Attribute::NoItalic => write!(f, "no_italic"),
193 Attribute::NoUnderline => write!(f, "no_underline"),
194 Attribute::NoBlink => write!(f, "no_blink"),
195 Attribute::NoReverse => write!(f, "no_reverse"),
196 Attribute::NoHidden => write!(f, "no_hidden"),
197 Attribute::NotCrossedOut => write!(f, "not_crossed_out"),
198 Attribute::Framed => write!(f, "framed"),
199 Attribute::Encircled => write!(f, "encircled"),
200 Attribute::OverLined => write!(f, "overlined"),
201 Attribute::NotFramedOrEncircled => write!(f, "not_framed_or_encircled"),
202 Attribute::NotOverLined => write!(f, "not_overlined"),
203 }
204 }
205}
206
207pub fn deserialize_attributes<'de, D>(deserializer: D) -> Result<Attributes, D::Error>
208where
209 D: serde::Deserializer<'de>,
210{
211 Vec::<String>::deserialize(deserializer).map(|vec| {
212 let mut attributes = Attributes::default();
213 for attr in vec {
214 attributes.set(
215 match Attribute::from_str(&attr) {
216 Ok(t) => t,
217 Err(_) => panic!(
218 "could not decode attribute: {}, valid attributes: {:?}",
219 attr,
220 Attribute::iterator()
221 .map(|a| a.to_string())
222 .collect::<Vec<_>>() ),
224 }
225 .into(),
226 );
227 }
228 attributes
229 })
230}