css_lexer/
atom_set.rs

1/// Object-safe version of AtomSet for use with trait objects. This trait mirrors the functionality of AtomSet but is
2/// compatible with `dyn` trait objects.
3pub trait DynAtomSet: std::fmt::Debug {
4	/// Converts a string keyword to the corresponding atom variant, returning its bit representation.
5	fn str_to_bits(&self, keyword: &str) -> u32;
6
7	/// Converts this atom's bit representation back to its string representation.
8	fn bits_to_str(&self, bits: u32) -> &'static str;
9
10	/// Get the current bits of this Atom.
11	fn bits(&self) -> u32;
12}
13
14/// # Usage with `#[derive(AtomSet)]`
15///
16/// The easiest way to implement this trait is using the `AtomSet` derive macro:
17///
18/// ```rust
19/// use derive_atom_set::AtomSet;
20/// use css_lexer::AtomSet;
21///
22/// #[derive(Debug, Default, Copy, Clone, PartialEq, AtomSet)]
23/// pub enum Units {
24///   // AtomSet must derive default, ideally with an empty atom, or equivalent!
25///   #[default]
26///   _None,
27///
28///   // Automatically converts to "px"
29///   Px,
30///
31///   // Automatically converts to "rem"
32///   Rem,
33///
34///   // Custom string mapping
35///   #[atom("%")]
36///   Percent,
37/// }
38/// ```
39pub trait AtomSet: Default + std::fmt::Debug {
40	/// Converts a string keyword to the corresponding atom variant.
41	///
42	/// This performs case-insensitive matching and returns the `Empty` variant for unrecognized strings.
43	///
44	/// # Examples
45	///
46	/// ```rust
47	/// # use css_lexer::{AtomSet};
48	/// use derive_atom_set::*;
49	///
50	/// #[derive(Debug, Default, Copy, Clone, PartialEq, AtomSet)]
51	/// enum MyAtomSet {
52	///   #[default]
53	///   _None,
54	///   Url
55	/// }
56	/// assert_eq!(MyAtomSet::from_str("url"), MyAtomSet::Url);
57	/// assert_eq!(MyAtomSet::from_str("URL"), MyAtomSet::Url);  // Case insensitive
58	/// assert_eq!(MyAtomSet::from_str("unknown"), MyAtomSet::_None);
59	/// ```
60	fn from_str(keyword: &str) -> Self;
61
62	/// Converts this atom back to its string representation.
63	///
64	/// Returns a static string slice that represents this atom's canonical form.
65	///
66	/// The variant marked `#[default]` will always return the empty string.
67	///
68	/// # Examples
69	///
70	/// ```rust
71	/// # use css_lexer::AtomSet;
72	/// use derive_atom_set::*;
73	///
74	/// #[derive(Debug, Default, Copy, Clone, PartialEq, AtomSet)]
75	/// enum MyAtomSet {
76	///   #[default]
77	///   _None,
78	///   Url
79	/// }
80	/// assert_eq!(MyAtomSet::Url.to_str(), "url");
81	/// assert_eq!(MyAtomSet::_None.to_str(), "");
82	///
83	/// // Round-trip conversion
84	/// let atom = MyAtomSet::from_str("url");
85	/// assert_eq!(atom.to_str(), "url");
86	/// ```
87	fn to_str(self) -> &'static str;
88
89	/// Returns the length in characters of this atom's string representation.
90	///
91	/// This is equivalent to `self.to_str().len()` but may be more efficient depending on the implementation.
92	///
93	/// # Examples
94	///
95	/// ```rust
96	/// # use css_lexer::AtomSet;
97	/// use derive_atom_set::*;
98	///
99	/// #[derive(Debug, Default, Copy, Clone, PartialEq, AtomSet)]
100	/// enum MyAtomSet {
101	///   #[default]
102	///   _None,
103	///   Url
104	/// }
105	/// assert_eq!(MyAtomSet::Url.len(), 3);
106	/// assert_eq!(MyAtomSet::_None.len(), 0);
107	/// ```
108	fn len(&self) -> u32;
109
110	/// Returns true if the length of this atom is 0.
111	///
112	/// This is equivalent to `self.to_str().is_empty()` but may be more efficient depending on the implementation.
113	///
114	/// # Examples
115	///
116	/// ```rust
117	/// # use css_lexer::AtomSet;
118	/// use derive_atom_set::*;
119	///
120	/// #[derive(Debug, Default, Copy, Clone, PartialEq, AtomSet)]
121	/// enum MyAtomSet {
122	///   #[default]
123	///   _None,
124	///   Url
125	/// }
126	/// assert!(!MyAtomSet::Url.is_empty());
127	/// assert!(MyAtomSet::_None.is_empty());
128	/// ```
129	fn is_empty(&self) -> bool {
130		self.len() == 0
131	}
132
133	/// Converts a numeric bit representation back to an atom variant.
134	///
135	/// This is used internally for efficient storage and retrieval. Returns the `Empty` variant for unrecognized bit
136	/// values.
137	///
138	/// # Examples
139	///
140	/// ```rust
141	/// # use css_lexer::AtomSet;
142	/// use derive_atom_set::*;
143	///
144	/// #[derive(Debug, Default, Copy, Clone, PartialEq, AtomSet)]
145	/// enum MyAtomSet {
146	///   #[default]
147	///   _None,
148	///   Url
149	/// }
150	/// let atom = MyAtomSet::Url;
151	/// let bits = atom.as_bits();
152	/// let restored = MyAtomSet::from_bits(bits);
153	/// assert_eq!(atom, restored);
154	/// ```
155	fn from_bits(bits: u32) -> Self;
156
157	/// Converts this atom to its numeric bit representation.
158	///
159	/// This is used internally for efficient storage. The bit value corresponds to the enum discriminant.
160	///
161	/// # Examples
162	///
163	/// ```rust
164	/// # use css_lexer::AtomSet;
165	/// use derive_atom_set::*;
166	///
167	/// #[derive(Debug, Default, Copy, Clone, PartialEq, AtomSet)]
168	/// enum MyAtomSet {
169	///   #[default]
170	///   _None,
171	///   Url
172	/// }
173	/// let bits = MyAtomSet::Url.as_bits();
174	/// assert_eq!(MyAtomSet::from_bits(bits), MyAtomSet::Url);
175	/// ```
176	fn as_bits(&self) -> u32;
177}
178
179/// Blanket implementation so any AtomSet can be used as a DynAtomSet
180impl<T: AtomSet + Clone + 'static> DynAtomSet for T {
181	fn str_to_bits(&self, keyword: &str) -> u32 {
182		T::from_str(keyword).as_bits()
183	}
184
185	fn bits_to_str(&self, bits: u32) -> &'static str {
186		T::from_bits(bits).to_str()
187	}
188
189	fn bits(&self) -> u32 {
190		self.clone().as_bits()
191	}
192}