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}