Skip to main content

atom_set/
lib.rs

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