1use serde::{Deserialize, Serialize};
6use std::str::FromStr;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub enum State {
11 Hover,
13 Focus,
15 Active,
17 Visited,
19 Disabled,
21 Checked,
23 Indeterminate,
25 Required,
27 Valid,
29 Invalid,
31 InRange,
33 OutOfRange,
35 ReadOnly,
37 ReadWrite,
39 Optional,
41 PlaceholderShown,
43 Autofill,
45 Default,
47 IndeterminateAlt,
49 InRangeAlt,
51 OutOfRangeAlt,
53 ReadOnlyAlt,
55 ReadWriteAlt,
57 OptionalAlt,
59 PlaceholderShownAlt,
61 AutofillAlt,
63}
64
65impl State {
66 pub fn prefix(&self) -> &'static str {
68 match self {
69 State::Hover => "hover:",
70 State::Focus => "focus:",
71 State::Active => "active:",
72 State::Visited => "visited:",
73 State::Disabled => "disabled:",
74 State::Checked => "checked:",
75 State::Indeterminate => "indeterminate:",
76 State::Required => "required:",
77 State::Valid => "valid:",
78 State::Invalid => "invalid:",
79 State::InRange => "in-range:",
80 State::OutOfRange => "out-of-range:",
81 State::ReadOnly => "read-only:",
82 State::ReadWrite => "read-write:",
83 State::Optional => "optional:",
84 State::PlaceholderShown => "placeholder-shown:",
85 State::Autofill => "autofill:",
86 State::Default => "default:",
87 State::IndeterminateAlt => "indeterminate:",
88 State::InRangeAlt => "in-range:",
89 State::OutOfRangeAlt => "out-of-range:",
90 State::ReadOnlyAlt => "read-only:",
91 State::ReadWriteAlt => "read-write:",
92 State::OptionalAlt => "optional:",
93 State::PlaceholderShownAlt => "placeholder-shown:",
94 State::AutofillAlt => "autofill:",
95 }
96 }
97
98 pub fn css_name(&self) -> &'static str {
100 match self {
101 State::Hover => ":hover",
102 State::Focus => ":focus",
103 State::Active => ":active",
104 State::Visited => ":visited",
105 State::Disabled => ":disabled",
106 State::Checked => ":checked",
107 State::Indeterminate => ":indeterminate",
108 State::Required => ":required",
109 State::Valid => ":valid",
110 State::Invalid => ":invalid",
111 State::InRange => ":in-range",
112 State::OutOfRange => ":out-of-range",
113 State::ReadOnly => ":read-only",
114 State::ReadWrite => ":read-write",
115 State::Optional => ":optional",
116 State::PlaceholderShown => ":placeholder-shown",
117 State::Autofill => ":autofill",
118 State::Default => ":default",
119 State::IndeterminateAlt => ":indeterminate",
120 State::InRangeAlt => ":in-range",
121 State::OutOfRangeAlt => ":out-of-range",
122 State::ReadOnlyAlt => ":read-only",
123 State::ReadWriteAlt => ":read-write",
124 State::OptionalAlt => ":optional",
125 State::PlaceholderShownAlt => ":placeholder-shown",
126 State::AutofillAlt => ":autofill",
127 }
128 }
129
130 pub fn all() -> Vec<State> {
132 vec![
133 State::Hover,
134 State::Focus,
135 State::Active,
136 State::Visited,
137 State::Disabled,
138 State::Checked,
139 State::Indeterminate,
140 State::Required,
141 State::Valid,
142 State::Invalid,
143 State::InRange,
144 State::OutOfRange,
145 State::ReadOnly,
146 State::ReadWrite,
147 State::Optional,
148 State::PlaceholderShown,
149 State::Autofill,
150 State::Default,
151 ]
152 }
153
154 pub fn is_interactive(&self) -> bool {
156 matches!(self, State::Hover | State::Focus | State::Active)
157 }
158
159 pub fn is_form_related(&self) -> bool {
161 matches!(
162 self,
163 State::Checked
164 | State::Indeterminate
165 | State::Required
166 | State::Valid
167 | State::Invalid
168 | State::InRange
169 | State::OutOfRange
170 | State::ReadOnly
171 | State::ReadWrite
172 | State::Optional
173 | State::PlaceholderShown
174 | State::Autofill
175 | State::Default
176 )
177 }
178}
179
180impl FromStr for State {
181 type Err = String;
182
183 fn from_str(s: &str) -> Result<Self, Self::Err> {
184 match s.to_lowercase().as_str() {
185 "hover" => Ok(State::Hover),
186 "focus" => Ok(State::Focus),
187 "active" => Ok(State::Active),
188 "visited" => Ok(State::Visited),
189 "disabled" => Ok(State::Disabled),
190 "checked" => Ok(State::Checked),
191 "indeterminate" => Ok(State::Indeterminate),
192 "required" => Ok(State::Required),
193 "valid" => Ok(State::Valid),
194 "invalid" => Ok(State::Invalid),
195 "in-range" => Ok(State::InRange),
196 "out-of-range" => Ok(State::OutOfRange),
197 "read-only" => Ok(State::ReadOnly),
198 "read-write" => Ok(State::ReadWrite),
199 "optional" => Ok(State::Optional),
200 "placeholder-shown" => Ok(State::PlaceholderShown),
201 "autofill" => Ok(State::Autofill),
202 "default" => Ok(State::Default),
203 _ => Err(format!("Invalid state: {}", s)),
204 }
205 }
206}
207
208impl std::fmt::Display for State {
209 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210 match self {
211 State::Hover => write!(f, "hover"),
212 State::Focus => write!(f, "focus"),
213 State::Active => write!(f, "active"),
214 State::Visited => write!(f, "visited"),
215 State::Disabled => write!(f, "disabled"),
216 State::Checked => write!(f, "checked"),
217 State::Indeterminate => write!(f, "indeterminate"),
218 State::Required => write!(f, "required"),
219 State::Valid => write!(f, "valid"),
220 State::Invalid => write!(f, "invalid"),
221 State::InRange => write!(f, "in-range"),
222 State::OutOfRange => write!(f, "out-of-range"),
223 State::ReadOnly => write!(f, "read-only"),
224 State::ReadWrite => write!(f, "read-write"),
225 State::Optional => write!(f, "optional"),
226 State::PlaceholderShown => write!(f, "placeholder-shown"),
227 State::Autofill => write!(f, "autofill"),
228 State::Default => write!(f, "default"),
229 State::IndeterminateAlt => write!(f, "indeterminate"),
230 State::InRangeAlt => write!(f, "in-range"),
231 State::OutOfRangeAlt => write!(f, "out-of-range"),
232 State::ReadOnlyAlt => write!(f, "read-only"),
233 State::ReadWriteAlt => write!(f, "read-write"),
234 State::OptionalAlt => write!(f, "optional"),
235 State::PlaceholderShownAlt => write!(f, "placeholder-shown"),
236 State::AutofillAlt => write!(f, "autofill"),
237 }
238 }
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244
245 #[test]
246 fn test_state_prefix() {
247 assert_eq!(State::Hover.prefix(), "hover:");
248 assert_eq!(State::Focus.prefix(), "focus:");
249 assert_eq!(State::Active.prefix(), "active:");
250 assert_eq!(State::Disabled.prefix(), "disabled:");
251 assert_eq!(State::Checked.prefix(), "checked:");
252 }
253
254 #[test]
255 fn test_state_css_name() {
256 assert_eq!(State::Hover.css_name(), ":hover");
257 assert_eq!(State::Focus.css_name(), ":focus");
258 assert_eq!(State::Active.css_name(), ":active");
259 assert_eq!(State::Disabled.css_name(), ":disabled");
260 assert_eq!(State::Checked.css_name(), ":checked");
261 }
262
263 #[test]
264 fn test_state_from_str() {
265 assert_eq!(State::from_str("hover").unwrap(), State::Hover);
266 assert_eq!(State::from_str("focus").unwrap(), State::Focus);
267 assert_eq!(State::from_str("active").unwrap(), State::Active);
268 assert_eq!(State::from_str("disabled").unwrap(), State::Disabled);
269 assert_eq!(State::from_str("checked").unwrap(), State::Checked);
270 }
271
272 #[test]
273 fn test_state_display() {
274 assert_eq!(format!("{}", State::Hover), "hover");
275 assert_eq!(format!("{}", State::Focus), "focus");
276 assert_eq!(format!("{}", State::Active), "active");
277 assert_eq!(format!("{}", State::Disabled), "disabled");
278 assert_eq!(format!("{}", State::Checked), "checked");
279 }
280
281 #[test]
282 fn test_state_all() {
283 let all = State::all();
284 assert_eq!(all.len(), 18);
285 assert!(all.contains(&State::Hover));
286 assert!(all.contains(&State::Focus));
287 assert!(all.contains(&State::Active));
288 }
289
290 #[test]
291 fn test_state_is_interactive() {
292 assert!(State::Hover.is_interactive());
293 assert!(State::Focus.is_interactive());
294 assert!(State::Active.is_interactive());
295 assert!(!State::Disabled.is_interactive());
296 assert!(!State::Checked.is_interactive());
297 }
298
299 #[test]
300 fn test_state_is_form_related() {
301 assert!(!State::Hover.is_form_related());
302 assert!(!State::Focus.is_form_related());
303 assert!(!State::Active.is_form_related());
304 assert!(State::Checked.is_form_related());
305 assert!(State::Required.is_form_related());
306 assert!(State::Valid.is_form_related());
307 }
308}