irx_config/value.rs
1//! This module define [`Value`] structure which represent key-value based configuration data.
2
3use crate::{Error, Result, DEFAULT_KEYS_SEPARATOR};
4use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer};
5pub use serde_json::json;
6pub(super) use serde_json::Error as SerdeError;
7use serde_json::{map::Map, Value as InnerValue};
8use std::{
9 borrow::Cow,
10 fmt::{Debug, Display, Error as FmtError, Formatter, Result as FmtResult},
11 result::Result as StdResult,
12};
13
14type ValueMap = Map<String, InnerValue>;
15type CowInnerValue<'a> = Cow<'a, InnerValue>;
16
17/// The sealed states for [`Value`] structure.
18///
19/// If [`Value`] is sealed, the sensitive fields values will be obfuscated with `********` during display/debugging output.
20///
21/// **IMPORTANT:** Once [`Value`] was sealed, but fully/partially mutated after that, it will be represented as empty
22/// dictionary during display/debugging output, to prevent sensitive data leakages.
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
24pub enum SealedState {
25 /// A [`Value`] was never sealed, all data will be represented as is during display/debugging output.
26 #[default]
27 None,
28 /// A [`Value`] was sealed, sensitive data will be obfuscated during display/debugging output.
29 On,
30 /// A [`Value`] was sealed and fully/partially mutated after that, whole [`Value`] will be represented as empty
31 /// dictionary during display/debugging output (see above).
32 Mutated,
33}
34
35/// This structure represent key-value based configuration data.
36///
37/// **IMPORTANT:** All functionality related to the sealed state only affects the display/debugging output.
38#[derive(Clone)]
39pub struct Value {
40 value: InnerValue,
41 sealed: Option<InnerValue>,
42 sealed_state: SealedState,
43 case_on: bool,
44}
45
46impl Value {
47 /// Try to create [`Value`] structure from any type which implements [`Serialize`] trait and make key names to be
48 /// case-sensitive. If successful return instance of [`Value`] structure.
49 ///
50 /// # Errors
51 ///
52 /// If any errors will occur during construction then error will be returned.
53 ///
54 /// # Example
55 ///
56 /// ```
57 /// use irx_config::{json, Value};
58 ///
59 /// let data = Value::try_from(json!(
60 /// {
61 /// "name": "John Doe",
62 /// "age": 43,
63 /// "phones": [
64 /// "+44 1234567",
65 /// "+44 2345678"
66 /// ]
67 /// }))?;
68 /// ```
69 #[inline]
70 pub fn try_from<T: Serialize>(value: T) -> Result<Self> {
71 Self::try_from_with_case(value, true)
72 }
73
74 /// Try to create [`Value`] structure from any type which implements [`Serialize`] trait and make key names to be
75 /// case-sensitive/insensitive. If successful return instance of [`Value`] structure.
76 ///
77 /// # Errors
78 ///
79 /// If any errors will occur during construction then error will be returned.
80 #[inline]
81 pub fn try_from_with_case<T: Serialize>(value: T, case_on: bool) -> Result<Self> {
82 Ok(Self {
83 value: set(value, case_on)?,
84 case_on,
85 ..Default::default()
86 })
87 }
88
89 /// Create default [`Value`] structure with key names to be case-sensitive/insensitive.
90 #[inline]
91 pub fn with_case(on: bool) -> Self {
92 Self {
93 case_on: on,
94 ..Default::default()
95 }
96 }
97
98 /// Return `true` if key names is case-sensitive, otherwise return `false`.
99 #[inline]
100 pub fn is_case_sensitive(&self) -> bool {
101 self.case_on
102 }
103
104 /// Merge a input [`Value`] to the given [`Value`] structure. The key names will use case-sensitivity of given
105 /// [`Value`] during merge. Return merged result [`Value`] structure. If given [`Value`] was sealed and merge
106 /// operation was mutating then it will be in [`SealedState::Mutated`].
107 ///
108 /// # Example
109 ///
110 /// ```
111 /// use irx_config::{json, Value};
112 ///
113 /// let mut person = Value::try_from(json!({
114 /// "name": "John Doe",
115 /// "age": 43
116 /// }))?;
117 ///
118 /// let phones = Value::try_from(json!({
119 /// "phones": [
120 /// "+44 1234567",
121 /// "+44 2345678"
122 /// ]
123 /// }))?;
124 ///
125 /// person = person.merge(&phones);
126 /// ```
127 #[inline]
128 pub fn merge(self, value: &Value) -> Self {
129 let case_on = self.case_on;
130 self.merge_with_case(value, case_on)
131 }
132
133 /// Merge a input [`Value`] to the given [`Value`] structure. The key names will be case-sensitive or
134 /// case-insensitive during merge, according to `case_on` parameter. Return merged result [`Value`] structure.
135 /// If given [`Value`] was sealed and merge operation was mutating then it will be in [`SealedState::Mutated`].
136 pub fn merge_with_case(mut self, value: &Value, case_on: bool) -> Self {
137 let mut is_changed = self.normalize_case(case_on);
138 self.value = match self.value {
139 InnerValue::Object(dst) if value.value.is_object() => {
140 is_changed = true;
141 merge_into_value_map(dst, &value.value, self.case_on)
142 }
143 _ => self.value,
144 };
145
146 if is_changed {
147 self.unseal();
148 }
149 self
150 }
151
152 /// Return deserialized data of any type which implements [`Deserialize`] trait for given key path represented
153 /// as iterator. If given key path does not exists `Ok(None)` will be returned.
154 ///
155 /// # Errors
156 ///
157 /// If any errors will occur then error will be returned.
158 ///
159 /// # Example
160 ///
161 /// ```
162 /// use irx_config::{json, Value};
163 ///
164 /// let logger = Value::try_from(json!({
165 /// "logger": {
166 /// "id": 42,
167 /// "host": "localhost"
168 /// }
169 /// }))?;
170 ///
171 /// let id: u32 = logger.get_by_keys(["logger", "id"])?.unwrap();
172 /// ```
173 pub fn get_by_keys<I, K, T>(&self, keys: I) -> Result<Option<T>>
174 where
175 I: IntoIterator<Item = K>,
176 K: AsRef<str>,
177 T: DeserializeOwned,
178 {
179 let mut result = &self.value;
180 for key in keys {
181 result = if let InnerValue::Object(map) = result {
182 let key = crate::normalize_case(key.as_ref(), self.case_on);
183 match map.get(key.as_ref()) {
184 None => return Ok(None),
185 Some(v) => v,
186 }
187 } else {
188 return Ok(None);
189 };
190 }
191
192 Ok(Some(get(result.clone())?))
193 }
194
195 /// Return deserialized data of any type which implements [`Deserialize`] trait for given key path represented
196 /// as string with default keys level delimiter [`DEFAULT_KEYS_SEPARATOR`]. If given key path does not exists
197 /// `Ok(None)` will be returned.
198 ///
199 /// # Errors
200 ///
201 /// If any errors will occur then error will be returned.
202 ///
203 /// # Example
204 ///
205 /// ```
206 /// use irx_config::{json, Value};
207 ///
208 /// let logger = Value::try_from(json!({
209 /// "logger": {
210 /// "id": 42,
211 /// "host": "localhost"
212 /// }
213 /// }))?;
214 ///
215 /// let id: u32 = logger.get_by_key_path("logger:id")?.unwrap();
216 /// ```
217 #[inline]
218 pub fn get_by_key_path<T, P>(&self, path: P) -> Result<Option<T>>
219 where
220 T: DeserializeOwned,
221 P: AsRef<str>,
222 {
223 self.get_by_key_path_with_delim(path.as_ref(), DEFAULT_KEYS_SEPARATOR)
224 }
225
226 /// Return deserialized data of any type which implements [`Deserialize`] trait for given key path represented
227 /// as string with given keys level delimiter. If given key path does not exists `Ok(None)` will be returned.
228 ///
229 /// # Errors
230 ///
231 /// If any errors will occur then error will be returned.
232 ///
233 /// # Example
234 ///
235 /// ```
236 /// use irx_config::{json, Value};
237 ///
238 /// let logger = Value::try_from(json!({
239 /// "logger": {
240 /// "id": 42,
241 /// "host": "localhost"
242 /// }
243 /// }))?;
244 ///
245 /// let host: String = logger.get_by_key_path_with_delim("logger/host", "/")?.unwrap();
246 /// ```
247 pub fn get_by_key_path_with_delim<T, P, D>(&self, path: P, delim: D) -> Result<Option<T>>
248 where
249 T: DeserializeOwned,
250 P: AsRef<str>,
251 D: AsRef<str>,
252 {
253 fn inner<T>(value: &Value, path: &str, delim: &str) -> Result<Option<T>>
254 where
255 T: DeserializeOwned,
256 {
257 if delim.is_empty() {
258 return Err(Error::EmptySeparator("get", path.into()));
259 }
260
261 if path.is_empty() {
262 return value.get_by_keys([""; 0]);
263 }
264
265 value.get_by_keys(path.split(delim))
266 }
267
268 inner(self, path.as_ref(), delim.as_ref())
269 }
270
271 /// Return deserialized data of any type which implements [`Deserialize`] trait.
272 ///
273 /// # Errors
274 ///
275 /// If any errors will occur then error will be returned.
276 ///
277 /// # Example
278 ///
279 /// ```
280 /// use irx_config::{json, Value};
281 /// use serde::Deserialize;
282 ///
283 /// #[derive(Deserialize)]
284 /// struct Logger {
285 /// pub id: u32,
286 /// pub host: String,
287 /// }
288 ///
289 /// #[derive(Deserialize)]
290 /// struct Config {
291 /// logger: Logger,
292 /// }
293 ///
294 /// let config = Value::try_from(json!({
295 /// "logger": {
296 /// "id": 42,
297 /// "host": "localhost"
298 /// }
299 /// }))?;
300 ///
301 /// let config: Config = config.get()?;
302 /// ```
303 #[inline]
304 pub fn get<T: DeserializeOwned>(&self) -> Result<T> {
305 get(self.value.clone())
306 }
307
308 /// Set value of any type which implements [`Serialize`] trait for given key path represented as iterator.
309 /// If [`Value`] was sealed and set operation was successful then it will be in [`SealedState::Mutated`]. Return
310 /// previous value for same key path if any.
311 ///
312 /// # Errors
313 ///
314 /// If any errors will occur then error will be returned.
315 ///
316 /// # Example
317 ///
318 /// ```
319 /// use irx_config::Value;
320 ///
321 /// let mut value = Value::default();
322 /// value.set_by_keys(["logger", "id"], 42)?;
323 /// ```
324 pub fn set_by_keys<I, K, T>(&mut self, keys: I, value: T) -> Result<Option<Self>>
325 where
326 I: IntoIterator<Item = K>,
327 K: AsRef<str>,
328 T: Serialize,
329 {
330 let inner = || {
331 let mut result = &mut self.value;
332 let mut keys = keys.into_iter().peekable();
333 while let Some(key) = keys.next() {
334 let key = crate::normalize_case(key.as_ref(), self.case_on);
335 result = match result {
336 InnerValue::Object(m) if keys.peek().is_none() => {
337 return Ok(m
338 .insert(key.into_owned(), set(value, self.case_on)?)
339 .map(|r| Self {
340 value: r,
341 case_on: self.case_on,
342 ..Default::default()
343 }))
344 }
345 InnerValue::Object(m) => m.entry(key).or_insert_with(|| json!({})),
346 _ => return Err(Error::NotMap),
347 }
348 }
349
350 let prev = self.clone();
351 self.value = set(value, self.case_on)?;
352 Ok(Some(prev))
353 };
354
355 let result = inner()?;
356 self.unseal();
357 Ok(result)
358 }
359
360 /// Set value of any type which implements [`Serialize`] trait for given key path represented as string with default
361 /// keys level delimiter [`DEFAULT_KEYS_SEPARATOR`]. If [`Value`] was sealed and set operation was successful then
362 /// it will be in [`SealedState::Mutated`]. Return previous value for same key path if any.
363 ///
364 /// # Errors
365 ///
366 /// If any errors will occur then error will be returned.
367 ///
368 /// # Example
369 ///
370 /// ```
371 /// use irx_config::Value;
372 ///
373 /// let mut value = Value::default();
374 /// value.set_by_key_path("logger:id", 42)?;
375 /// ```
376 #[inline]
377 pub fn set_by_key_path<T, P>(&mut self, path: P, value: T) -> Result<Option<Self>>
378 where
379 T: Serialize,
380 P: AsRef<str>,
381 {
382 self.set_by_key_path_with_delim(path.as_ref(), DEFAULT_KEYS_SEPARATOR, value)
383 }
384
385 /// Set value of any type which implements [`Serialize`] trait for given key path represented as string with given
386 /// keys level delimiter. If [`Value`] was sealed and set operation was successful then it will be in
387 /// [`SealedState::Mutated`]. Return previous value for same key path if any.
388 ///
389 /// # Errors
390 ///
391 /// If any errors will occur then error will be returned.
392 ///
393 /// # Example
394 ///
395 /// ```
396 /// use irx_config::Value;
397 ///
398 /// let mut value = Value::default();
399 /// value.set_by_key_path_with_delim("logger/id", "/", 42)?;
400 /// ```
401 pub fn set_by_key_path_with_delim<T, P, D>(
402 &mut self,
403 path: P,
404 delim: D,
405 value: T,
406 ) -> Result<Option<Self>>
407 where
408 T: Serialize,
409 P: AsRef<str>,
410 D: AsRef<str>,
411 {
412 fn inner<T>(this: &mut Value, path: &str, delim: &str, value: T) -> Result<Option<Value>>
413 where
414 T: Serialize,
415 {
416 if delim.is_empty() {
417 return Err(Error::EmptySeparator("set", path.into()));
418 }
419
420 if path.is_empty() {
421 return this.set_by_keys([""; 0], value);
422 }
423
424 this.set_by_keys(path.split(delim), value)
425 }
426
427 inner(self, path.as_ref(), delim.as_ref(), value)
428 }
429
430 /// Return [`Value`] structure as a sequence of bytes.
431 #[inline]
432 pub fn as_bytes(&self) -> Vec<u8> {
433 self.value.to_string().as_bytes().to_owned()
434 }
435
436 /// Seal secret values in [`Value`] structure with given suffix. Such values will be obfuscated with `********`
437 /// during display/debugging output. If not set then all values will be displayed as is.
438 ///
439 /// # Example
440 ///
441 /// ```
442 /// use irx_config::Value;
443 ///
444 /// let mut value = Value::try_from(json!({
445 /// "user": "user name",
446 /// "password_sealed_": "secret"
447 /// }))?;
448 ///
449 /// value.seal("_sealed_");
450 /// ```
451 pub fn seal<S>(&mut self, suffix: S) -> &mut Self
452 where
453 S: AsRef<str>,
454 {
455 fn inner<'a>(this: &'a mut Value, suffix: &str) -> &'a mut Value {
456 if SealedState::On == this.sealed_state {
457 return this;
458 }
459
460 this.sealed = None;
461 this.sealed_state = SealedState::On;
462
463 if suffix.is_empty() {
464 return this;
465 }
466
467 if let InnerValue::Object(ref map) = this.value {
468 let (v, s) = get_sealed(map, suffix, this.case_on);
469 this.value = v;
470 this.sealed = s;
471 }
472 this
473 }
474
475 inner(self, suffix.as_ref())
476 }
477
478 /// Return `true` if [`Value`] is sealed, otherwise return `false`.
479 #[inline]
480 pub fn is_sealed(&self) -> bool {
481 SealedState::On == self.sealed_state
482 }
483
484 /// Return sealed state [`SealedState`].
485 #[inline]
486 pub fn sealed_state(&self) -> SealedState {
487 self.sealed_state
488 }
489
490 fn normalize_case(&mut self, case_on: bool) -> bool {
491 if case_on == self.case_on {
492 return false;
493 }
494
495 if let InnerValue::Object(ref map) = self.value {
496 self.case_on = case_on;
497 if case_on {
498 return false;
499 }
500 self.value = unicase_value_map(map);
501 return true;
502 }
503
504 false
505 }
506
507 fn get_sealed(&self) -> CowInnerValue {
508 if SealedState::Mutated == self.sealed_state {
509 return CowInnerValue::Owned(json!({}));
510 }
511
512 if let Some(ref s) = self.sealed {
513 if let InnerValue::Object(ref m) = self.value {
514 return CowInnerValue::Owned(merge_into_value_map(m.clone(), s, self.case_on));
515 }
516 }
517 CowInnerValue::Borrowed(&self.value)
518 }
519
520 fn unseal(&mut self) {
521 if SealedState::On == self.sealed_state {
522 self.sealed_state = SealedState::Mutated;
523 }
524 self.sealed = None;
525 }
526}
527
528impl Default for Value {
529 #[inline]
530 fn default() -> Self {
531 Self {
532 value: json!({}),
533 sealed: None,
534 sealed_state: SealedState::None,
535 case_on: true,
536 }
537 }
538}
539
540impl PartialEq for Value {
541 #[inline]
542 fn eq(&self, other: &Self) -> bool {
543 self.value == other.value
544 }
545}
546
547impl Eq for Value {}
548
549impl Serialize for Value {
550 #[inline]
551 fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
552 where
553 S: Serializer,
554 {
555 self.value.serialize(serializer)
556 }
557}
558
559impl<'de> Deserialize<'de> for Value {
560 #[inline]
561 fn deserialize<D>(deserializer: D) -> StdResult<Self, D::Error>
562 where
563 D: Deserializer<'de>,
564 {
565 Ok(Self {
566 value: InnerValue::deserialize(deserializer)?,
567 ..Default::default()
568 })
569 }
570}
571
572impl Debug for Value {
573 #[inline]
574 fn fmt(&self, f: &mut Formatter) -> FmtResult {
575 f.write_fmt(format_args!(
576 "Value {{ value: {:?}, sealed: {:?}, sealed_state: {:?}, case_on: {:?} }}",
577 self.get_sealed(),
578 self.sealed,
579 self.sealed_state,
580 self.case_on
581 ))
582 }
583}
584
585impl Display for Value {
586 #[inline]
587 fn fmt(&self, f: &mut Formatter) -> FmtResult {
588 let value = serde_json::to_string_pretty(&self.get_sealed()).map_err(|_| FmtError)?;
589 f.write_str(&value)
590 }
591}
592
593fn merge_into_value_map(dst: ValueMap, src: &InnerValue, case_on: bool) -> InnerValue {
594 if let InnerValue::Object(src) = src {
595 let mut result = dst;
596 for (k, v) in src {
597 let norm_key = crate::normalize_case(k, case_on);
598 let val = if let Some(InnerValue::Object(m)) = result.get(norm_key.as_ref()) {
599 merge_into_value_map(m.clone(), v, case_on)
600 } else {
601 v.clone()
602 };
603
604 result.insert(norm_key.into_owned(), val);
605 }
606 return InnerValue::Object(result);
607 }
608 src.clone()
609}
610
611fn unicase_value_map(map: &ValueMap) -> InnerValue {
612 let mut result = ValueMap::default();
613 for (k, v) in map {
614 let val = if let InnerValue::Object(m) = v {
615 unicase_value_map(m)
616 } else {
617 v.clone()
618 };
619 result.insert(crate::unicase(k), val);
620 }
621 InnerValue::Object(result)
622}
623
624#[inline]
625fn get<T: DeserializeOwned>(value: InnerValue) -> Result<T> {
626 serde_json::from_value(value)
627 .map_err(|e| Error::SerdeError(e, "Failed to deserialize value".into()))
628}
629
630fn set<T: Serialize>(value: T, case_on: bool) -> Result<InnerValue> {
631 let value = serde_json::to_value(value)
632 .map_err(|e| Error::SerdeError(e, "Failed to serialize value".into()))?;
633 Ok(match value {
634 InnerValue::Object(ref map) if !case_on => unicase_value_map(map),
635 _ => value,
636 })
637}
638
639fn get_sealed(value: &ValueMap, suffix: &str, case_on: bool) -> (InnerValue, Option<InnerValue>) {
640 let mut result = ValueMap::default();
641 let mut sealed = ValueMap::default();
642 let uni_suffix = crate::normalize_case(suffix, case_on);
643 for (k, v) in value {
644 let key = crate::normalize_case(k, case_on);
645 let (val, opt) = if let Some(InnerValue::Object(nested)) = value.get(key.as_ref()) {
646 let (v, s) = get_sealed(nested, suffix, case_on);
647 (CowInnerValue::Owned(v), s)
648 } else {
649 (CowInnerValue::Borrowed(v), None)
650 };
651
652 let norm_key = key.trim_end_matches(uni_suffix.as_ref());
653 result.insert(norm_key.to_string(), val.into_owned());
654 if key.len() != norm_key.len() {
655 sealed.insert(norm_key.to_string(), json!("********"));
656 } else if let Some(s) = opt {
657 sealed.insert(norm_key.to_string(), s);
658 }
659 }
660 (
661 InnerValue::Object(result),
662 if sealed.is_empty() {
663 None
664 } else {
665 Some(InnerValue::Object(sealed))
666 },
667 )
668}