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