known_values/known_value_store.rs
1use std::collections::HashMap;
2
3use super::known_value::KnownValue;
4
5/// A store that maps between Known Values and their assigned names.
6///
7/// The `KnownValuesStore` provides a bidirectional mapping between:
8/// - Numeric values (u64) and their corresponding KnownValue instances
9/// - String names and their corresponding KnownValue instances
10///
11/// This enables efficient lookup in both directions, making it possible to:
12/// - Find the name for a given numeric value
13/// - Find the numeric value for a given name
14/// - Retrieve complete KnownValue instances by either name or value
15///
16/// The store is typically populated with predefined Known Values from the
17/// registry, but can also be extended with custom values.
18///
19/// # Examples
20///
21/// ```
22/// use std::collections::HashMap;
23///
24/// use known_values::{KnownValue, KnownValuesStore};
25///
26/// // Create a store with predefined Known Values
27/// let store = KnownValuesStore::new([
28/// known_values::IS_A,
29/// known_values::NOTE,
30/// known_values::SIGNED,
31/// ]);
32///
33/// // Look up a Known Value by name
34/// let is_a = store.known_value_named("isA").unwrap();
35/// assert_eq!(is_a.value(), 1);
36///
37/// // Look up a name for a raw value
38/// let name = store.name(KnownValue::new(3));
39/// assert_eq!(name, "signed");
40///
41/// // Insert a custom Known Value
42/// let mut custom_store = store.clone();
43/// custom_store
44/// .insert(KnownValue::new_with_name(100u64, "customValue".to_string()));
45/// assert_eq!(
46/// custom_store
47/// .known_value_named("customValue")
48/// .unwrap()
49/// .value(),
50/// 100
51/// );
52/// ```
53#[derive(Clone, Debug)]
54pub struct KnownValuesStore {
55 known_values_by_raw_value: HashMap<u64, KnownValue>,
56 known_values_by_assigned_name: HashMap<String, KnownValue>,
57}
58
59impl KnownValuesStore {
60 /// Creates a new KnownValuesStore with the provided Known Values.
61 ///
62 /// This constructor takes any iterable of KnownValue instances and
63 /// populates the store with them, creating mappings from both raw
64 /// values and names to the corresponding KnownValue instances.
65 ///
66 /// # Examples
67 ///
68 /// ```
69 /// use known_values::KnownValuesStore;
70 ///
71 /// // Create a store with predefined Known Values
72 /// let store = KnownValuesStore::new([
73 /// known_values::IS_A,
74 /// known_values::NOTE,
75 /// known_values::SIGNED,
76 /// ]);
77 ///
78 /// // Look up Known Values
79 /// assert_eq!(store.known_value_named("isA").unwrap().value(), 1);
80 /// assert_eq!(store.known_value_named("note").unwrap().value(), 4);
81 /// ```
82 pub fn new<T>(known_values: T) -> Self
83 where
84 T: IntoIterator<Item = KnownValue>,
85 {
86 let mut known_values_by_raw_value = HashMap::new();
87 let mut known_values_by_assigned_name = HashMap::new();
88 for known_value in known_values {
89 Self::_insert(
90 known_value,
91 &mut known_values_by_raw_value,
92 &mut known_values_by_assigned_name,
93 );
94 }
95 Self {
96 known_values_by_raw_value,
97 known_values_by_assigned_name,
98 }
99 }
100
101 /// Inserts a KnownValue into the store.
102 ///
103 /// If the KnownValue has an assigned name, it will be indexed by both its
104 /// raw value and its name. If a KnownValue with the same raw value or name
105 /// already exists in the store, it will be replaced.
106 ///
107 /// # Examples
108 ///
109 /// ```
110 /// use known_values::{KnownValue, KnownValuesStore};
111 ///
112 /// let mut store = KnownValuesStore::default();
113 /// store.insert(KnownValue::new_with_name(100u64, "customValue".to_string()));
114 /// assert_eq!(store.known_value_named("customValue").unwrap().value(), 100);
115 /// ```
116 pub fn insert(&mut self, known_value: KnownValue) {
117 Self::_insert(
118 known_value,
119 &mut self.known_values_by_raw_value,
120 &mut self.known_values_by_assigned_name,
121 );
122 }
123
124 /// Returns the assigned name for a KnownValue, if present in the store.
125 ///
126 /// # Examples
127 ///
128 /// ```
129 /// use known_values::{KnownValue, KnownValuesStore};
130 ///
131 /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
132 ///
133 /// assert_eq!(store.assigned_name(&known_values::IS_A), Some("isA"));
134 /// assert_eq!(store.assigned_name(&KnownValue::new(999)), None);
135 /// ```
136 pub fn assigned_name(&self, known_value: &KnownValue) -> Option<&str> {
137 self.known_values_by_raw_value
138 .get(&known_value.value())
139 .and_then(|known_value| known_value.assigned_name())
140 }
141
142 /// Returns a human-readable name for a KnownValue.
143 ///
144 /// If the KnownValue has an assigned name in the store, that name is
145 /// returned. Otherwise, the KnownValue's default name (which may be its
146 /// numeric value as a string) is returned.
147 ///
148 /// # Examples
149 ///
150 /// ```
151 /// use known_values::{KnownValue, KnownValuesStore};
152 ///
153 /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
154 ///
155 /// assert_eq!(store.name(known_values::IS_A), "isA");
156 /// assert_eq!(store.name(KnownValue::new(999)), "999");
157 /// ```
158 pub fn name(&self, known_value: KnownValue) -> String {
159 self.assigned_name(&known_value)
160 .map(|name| name.to_string())
161 .unwrap_or_else(|| known_value.name())
162 }
163
164 /// Looks up a KnownValue by its assigned name.
165 ///
166 /// Returns a reference to the KnownValue if found, or None if no KnownValue
167 /// with the given name exists in the store.
168 ///
169 /// # Examples
170 ///
171 /// ```
172 /// use known_values::KnownValuesStore;
173 ///
174 /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
175 ///
176 /// let is_a = store.known_value_named("isA").unwrap();
177 /// assert_eq!(is_a.value(), 1);
178 ///
179 /// assert!(store.known_value_named("nonexistent").is_none());
180 /// ```
181 pub fn known_value_named(
182 &self,
183 assigned_name: &str,
184 ) -> Option<&KnownValue> {
185 self.known_values_by_assigned_name.get(assigned_name)
186 }
187
188 /// Retrieves a KnownValue for a raw value, using a store if provided.
189 ///
190 /// This static method allows looking up a KnownValue by its raw numeric
191 /// value:
192 /// - If a store is provided and contains a mapping for the raw value, that
193 /// KnownValue is returned
194 /// - Otherwise, a new KnownValue with no assigned name is created and
195 /// returned
196 ///
197 /// # Examples
198 ///
199 /// ```
200 /// use known_values::KnownValuesStore;
201 ///
202 /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
203 ///
204 /// // Known value from store
205 /// let is_a = KnownValuesStore::known_value_for_raw_value(1, Some(&store));
206 /// assert_eq!(is_a.name(), "isA");
207 ///
208 /// // Unknown value creates a new KnownValue
209 /// let unknown =
210 /// KnownValuesStore::known_value_for_raw_value(999, Some(&store));
211 /// assert_eq!(unknown.name(), "999");
212 ///
213 /// // No store provided also creates a new KnownValue
214 /// let unknown = KnownValuesStore::known_value_for_raw_value(1, None);
215 /// assert_eq!(unknown.name(), "1");
216 /// ```
217 pub fn known_value_for_raw_value(
218 raw_value: u64,
219 known_values: Option<&Self>,
220 ) -> KnownValue {
221 known_values
222 .and_then(|known_values| {
223 known_values.known_values_by_raw_value.get(&raw_value)
224 })
225 .cloned()
226 .unwrap_or_else(|| KnownValue::new(raw_value))
227 }
228
229 /// Attempts to find a KnownValue by its name, using a store if provided.
230 ///
231 /// This static method allows looking up a KnownValue by its name:
232 /// - If a store is provided and contains a mapping for the name, that
233 /// KnownValue is returned
234 /// - Otherwise, None is returned
235 ///
236 /// # Examples
237 ///
238 /// ```
239 /// use known_values::KnownValuesStore;
240 ///
241 /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
242 ///
243 /// // Known value from store
244 /// let is_a = KnownValuesStore::known_value_for_name("isA", Some(&store));
245 /// assert_eq!(is_a.unwrap().value(), 1);
246 ///
247 /// // Unknown name returns None
248 /// assert!(
249 /// KnownValuesStore::known_value_for_name("unknown", Some(&store))
250 /// .is_none()
251 /// );
252 ///
253 /// // No store provided also returns None
254 /// assert!(KnownValuesStore::known_value_for_name("isA", None).is_none());
255 /// ```
256 pub fn known_value_for_name(
257 name: &str,
258 known_values: Option<&Self>,
259 ) -> Option<KnownValue> {
260 known_values
261 .and_then(|known_values| known_values.known_value_named(name))
262 .cloned()
263 }
264
265 /// Returns a human-readable name for a KnownValue, using a store if
266 /// provided.
267 ///
268 /// This static method allows getting a name for a KnownValue:
269 /// - If a store is provided and contains a mapping for the KnownValue, its
270 /// assigned name is returned
271 /// - Otherwise, the KnownValue's default name (which may be its numeric
272 /// value as a string) is returned
273 ///
274 /// # Examples
275 ///
276 /// ```
277 /// use known_values::{KnownValue, KnownValuesStore};
278 ///
279 /// let store = KnownValuesStore::new([known_values::IS_A, known_values::NOTE]);
280 ///
281 /// // Known value from store
282 /// let name = KnownValuesStore::name_for_known_value(
283 /// known_values::IS_A,
284 /// Some(&store),
285 /// );
286 /// assert_eq!(name, "isA");
287 ///
288 /// // Unknown value in store uses KnownValue's name method
289 /// let name = KnownValuesStore::name_for_known_value(
290 /// KnownValue::new(999),
291 /// Some(&store),
292 /// );
293 /// assert_eq!(name, "999");
294 ///
295 /// // No store provided also uses KnownValue's name method
296 /// let name = KnownValuesStore::name_for_known_value(known_values::IS_A, None);
297 /// assert_eq!(name, "isA");
298 /// ```
299 pub fn name_for_known_value(
300 known_value: KnownValue,
301 known_values: Option<&Self>,
302 ) -> String {
303 known_values
304 .and_then(|known_values| known_values.assigned_name(&known_value))
305 .map(|assigned_name| assigned_name.to_string())
306 .unwrap_or_else(|| known_value.name())
307 }
308
309 /// Internal helper method to insert a KnownValue into the store's maps.
310 fn _insert(
311 known_value: KnownValue,
312 known_values_by_raw_value: &mut HashMap<u64, KnownValue>,
313 known_values_by_assigned_name: &mut HashMap<String, KnownValue>,
314 ) {
315 known_values_by_raw_value
316 .insert(known_value.value(), known_value.clone());
317 if let Some(name) = known_value.assigned_name() {
318 known_values_by_assigned_name.insert(name.to_string(), known_value);
319 }
320 }
321}
322
323/// Default implementation creates an empty KnownValuesStore.
324impl Default for KnownValuesStore {
325 fn default() -> Self { Self::new([]) }
326}