Skip to main content

bare_config/
value.rs

1//! Value type for unified configuration value representation.
2//!
3//! This module provides the `Value` enum for representing
4//! configuration values across all supported formats.
5
6/// Represents a unified configuration value.
7///
8/// This enum provides a common representation for values
9/// from different configuration formats (JSON, YAML, TOML, etc.).
10///
11/// # Examples
12///
13/// ```
14/// use bare_config::value::Value;
15///
16/// let value = Value::String("hello".to_string());
17/// assert!(value.is_string());
18///
19/// let value = Value::integer(42);
20/// assert!(value.is_number());
21/// ```
22#[derive(Debug, Clone, PartialEq, Default)]
23pub enum Value {
24    /// Null value (JSON null, YAML null, etc.)
25    #[default]
26    Null,
27
28    /// Boolean value (true or false)
29    Bool(bool),
30
31    /// String value
32    String(String),
33
34    /// Integer number value
35    Integer(i64),
36
37    /// Floating-point number value
38    Float(f64),
39
40    /// Array value
41    Array(Vec<Value>),
42
43    /// Object/value map (JSON object, YAML mapping, etc.)
44    Map(Vec<(String, Value)>),
45}
46
47impl Value {
48    /// Creates a new Null value.
49    ///
50    /// # Examples
51    ///
52    /// ```
53    /// use bare_config::value::Value;
54    ///
55    /// let value = Value::null();
56    /// assert!(value.is_null());
57    /// ```
58    pub fn null() -> Self {
59        Value::Null
60    }
61
62    /// Creates a new Boolean value.
63    ///
64    /// # Examples
65    ///
66    /// ```
67    /// use bare_config::value::Value;
68    ///
69    /// let value = Value::bool(true);
70    /// assert_eq!(value.as_bool(), Some(true));
71    /// ```
72    pub fn bool(b: bool) -> Self {
73        Value::Bool(b)
74    }
75
76    /// Creates a new String value.
77    ///
78    /// # Examples
79    ///
80    /// ```
81    /// use bare_config::value::Value;
82    ///
83    /// let value = Value::string("hello");
84    /// assert_eq!(value.as_str(), Some("hello"));
85    /// ```
86    pub fn string<S: Into<String>>(s: S) -> Self {
87        Value::String(s.into())
88    }
89
90    /// Creates a new Integer value.
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// use bare_config::value::Value;
96    ///
97    /// let value = Value::integer(42);
98    /// assert_eq!(value.as_integer(), Some(42));
99    /// ```
100    pub fn integer(i: i64) -> Self {
101        Value::Integer(i)
102    }
103
104    /// Creates a new Float value.
105    ///
106    /// # Examples
107    ///
108    /// ```
109    /// use bare_config::value::Value;
110    ///
111    /// let value = Value::float(3.14);
112    /// assert_eq!(value.as_float(), Some(3.14));
113    /// ```
114    pub fn float(f: f64) -> Self {
115        Value::Float(f)
116    }
117
118    /// Creates a new Array value.
119    ///
120    /// # Examples
121    ///
122    /// ```
123    /// use bare_config::value::Value;
124    ///
125    /// let value = Value::array(vec![
126    ///     Value::integer(1),
127    ///     Value::integer(2),
128    /// ]);
129    /// assert!(value.is_array());
130    /// ```
131    pub fn array(v: Vec<Value>) -> Self {
132        Value::Array(v)
133    }
134
135    /// Creates a new Map value.
136    ///
137    /// # Examples
138    ///
139    /// ```
140    /// use bare_config::value::Value;
141    ///
142    /// let value = Value::map(vec![
143    ///     ("key".to_string(), Value::string("value".to_string())),
144    /// ]);
145    /// assert!(value.is_map());
146    /// ```
147    pub fn map(m: Vec<(String, Value)>) -> Self {
148        Value::Map(m)
149    }
150
151    /// Returns true if this value is Null.
152    ///
153    /// # Examples
154    ///
155    /// ```
156    /// use bare_config::value::Value;
157    ///
158    /// let value = Value::null();
159    /// assert!(value.is_null());
160    ///
161    /// let value = Value::string("test");
162    /// assert!(!value.is_null());
163    /// ```
164    pub fn is_null(&self) -> bool {
165        matches!(self, Value::Null)
166    }
167
168    /// Returns true if this value is a Boolean.
169    ///
170    /// # Examples
171    ///
172    /// ```
173    /// use bare_config::value::Value;
174    ///
175    /// let value = Value::bool(true);
176    /// assert!(value.is_bool());
177    /// ```
178    pub fn is_bool(&self) -> bool {
179        matches!(self, Value::Bool(_))
180    }
181
182    /// Returns true if this value is a String.
183    ///
184    /// # Examples
185    ///
186    /// ```
187    /// use bare_config::value::Value;
188    ///
189    /// let value = Value::string("test");
190    /// assert!(value.is_string());
191    /// ```
192    pub fn is_string(&self) -> bool {
193        matches!(self, Value::String(_))
194    }
195
196    /// Returns true if this value is a Number (Integer or Float).
197    ///
198    /// # Examples
199    ///
200    /// ```
201    /// use bare_config::value::Value;
202    ///
203    /// let value = Value::integer(42);
204    /// assert!(value.is_number());
205    ///
206    /// let value = Value::float(3.14);
207    /// assert!(value.is_number());
208    /// ```
209    pub fn is_number(&self) -> bool {
210        matches!(self, Value::Integer(_) | Value::Float(_))
211    }
212
213    /// Returns true if this value is an Integer.
214    ///
215    /// # Examples
216    ///
217    /// ```
218    /// use bare_config::value::Value;
219    ///
220    /// let value = Value::integer(42);
221    /// assert!(value.is_integer());
222    /// ```
223    pub fn is_integer(&self) -> bool {
224        matches!(self, Value::Integer(_))
225    }
226
227    /// Returns true if this value is a Float.
228    ///
229    /// # Examples
230    ///
231    /// ```
232    /// use bare_config::value::Value;
233    ///
234    /// let value = Value::float(3.14);
235    /// assert!(value.is_float());
236    /// ```
237    pub fn is_float(&self) -> bool {
238        matches!(self, Value::Float(_))
239    }
240
241    /// Returns true if this value is an Array.
242    ///
243    /// # Examples
244    ///
245    /// ```
246    /// use bare_config::value::Value;
247    ///
248    /// let value = Value::array(vec![]);
249    /// assert!(value.is_array());
250    /// ```
251    pub fn is_array(&self) -> bool {
252        matches!(self, Value::Array(_))
253    }
254
255    /// Returns true if this value is a Map.
256    ///
257    /// # Examples
258    ///
259    /// ```
260    /// use bare_config::value::Value;
261    ///
262    /// let value = Value::map(vec![]);
263    /// assert!(value.is_map());
264    /// ```
265    pub fn is_map(&self) -> bool {
266        matches!(self, Value::Map(_))
267    }
268
269    /// Returns the Boolean value if this is a Boolean.
270    ///
271    /// # Examples
272    ///
273    /// ```
274    /// use bare_config::value::Value;
275    ///
276    /// let value = Value::bool(true);
277    /// assert_eq!(value.as_bool(), Some(true));
278    /// ```
279    pub fn as_bool(&self) -> Option<bool> {
280        match self {
281            Value::Bool(b) => Some(*b),
282            _ => None,
283        }
284    }
285
286    /// Returns the String value if this is a String.
287    ///
288    /// # Examples
289    ///
290    /// ```
291    /// use bare_config::value::Value;
292    ///
293    /// let value = Value::string("hello");
294    /// assert_eq!(value.as_str(), Some("hello"));
295    /// ```
296    pub fn as_str(&self) -> Option<&str> {
297        match self {
298            Value::String(s) => Some(s),
299            _ => None,
300        }
301    }
302
303    /// Returns the Integer value if this is an Integer.
304    ///
305    /// # Examples
306    ///
307    /// ```
308    /// use bare_config::value::Value;
309    ///
310    /// let value = Value::integer(42);
311    /// assert_eq!(value.as_integer(), Some(42));
312    /// ```
313    pub fn as_integer(&self) -> Option<i64> {
314        match self {
315            Value::Integer(i) => Some(*i),
316            _ => None,
317        }
318    }
319
320    /// Returns the value as `u16` if this is an Integer value within range.
321    ///
322    /// Unlike [`to_u16`](Self::to_u16), this method only works on Integer values
323    /// and does not attempt to parse strings.
324    pub fn as_u16(&self) -> Option<u16> {
325        match self {
326            Value::Integer(i) => u16::try_from(*i).ok(),
327            _ => None,
328        }
329    }
330
331    /// Attempts to convert this value into `u16`.
332    ///
333    /// This accepts integer values directly and parses string values.
334    /// Unlike [`as_u16`](Self::as_u16), this also works on String values.
335    pub fn to_u16(&self) -> Option<u16> {
336        match self {
337            Value::Integer(i) => u16::try_from(*i).ok(),
338            Value::String(s) => s.parse::<u16>().ok(),
339            _ => None,
340        }
341    }
342
343    /// Returns `u16` value or a caller-provided default.
344    pub fn as_u16_or(&self, default: u16) -> u16 {
345        self.to_u16().unwrap_or(default)
346    }
347    /// Returns the Float value if this is a Float.
348    ///
349    /// # Examples
350    ///
351    /// ```
352    /// use bare_config::value::Value;
353    ///
354    /// let value = Value::float(3.14);
355    /// assert_eq!(value.as_float(), Some(3.14));
356    /// ```
357    pub fn as_float(&self) -> Option<f64> {
358        match self {
359            Value::Float(f) => Some(*f),
360            _ => None,
361        }
362    }
363
364    /// Returns the Array value if this is an Array.
365    ///
366    /// # Examples
367    ///
368    /// ```
369    /// use bare_config::value::Value;
370    ///
371    /// let value = Value::array(vec![Value::integer(1)]);
372    /// let arr = value.as_array().unwrap();
373    /// assert_eq!(arr.len(), 1);
374    /// ```
375    pub fn as_array(&self) -> Option<&Vec<Value>> {
376        match self {
377            Value::Array(a) => Some(a),
378            _ => None,
379        }
380    }
381
382    /// Returns the Map value if this is a Map.
383    ///
384    /// # Examples
385    ///
386    /// ```
387    /// use bare_config::value::Value;
388    ///
389    /// let value = Value::map(vec![("key".to_string(), Value::string("value".to_string()))]);
390    /// let map = value.as_map().unwrap();
391    /// assert_eq!(map.len(), 1);
392    /// ```
393    pub fn as_map(&self) -> Option<&Vec<(String, Value)>> {
394        match self {
395            Value::Map(m) => Some(m),
396            _ => None,
397        }
398    }
399
400    /// Returns the number of elements in this value.
401    ///
402    /// For Array, returns the number of elements.
403    /// For Map, returns the number of key-value pairs.
404    /// For other types, returns 0.
405    ///
406    /// # Examples
407    ///
408    /// ```
409    /// use bare_config::value::Value;
410    ///
411    /// let value = Value::array(vec![Value::integer(1), Value::integer(2)]);
412    /// assert_eq!(value.len(), 2);
413    /// ```
414    pub fn len(&self) -> usize {
415        match self {
416            Value::Array(a) => a.len(),
417            Value::Map(m) => m.len(),
418            _ => 0,
419        }
420    }
421
422    /// Returns true if this value has no elements.
423    ///
424    /// # Examples
425    ///
426    /// ```
427    /// use bare_config::value::Value;
428    ///
429    /// let value = Value::array(vec![]);
430    /// assert!(value.is_empty());
431    /// ```
432    pub fn is_empty(&self) -> bool {
433        match self {
434            Value::String(s) => s.is_empty(),
435            Value::Array(a) => a.is_empty(),
436            Value::Map(m) => m.is_empty(),
437            Value::Null => true,
438            _ => false,
439        }
440    }
441
442    /// Converts the value to a string representation.
443    ///
444    /// # Examples
445    ///
446    /// ```
447    /// use bare_config::value::Value;
448    ///
449    /// let value = Value::string("hello");
450    /// assert_eq!(value.to_string_repr(), "hello");
451    ///
452    /// let value = Value::integer(42);
453    /// assert_eq!(value.to_string_repr(), "42");
454    /// ```
455    pub fn to_string_repr(&self) -> String {
456        match self {
457            Value::Null => "null".to_string(),
458            Value::Bool(b) => b.to_string(),
459            Value::String(s) => s.clone(),
460            Value::Integer(i) => i.to_string(),
461            Value::Float(f) => f.to_string(),
462            Value::Array(a) => {
463                let mut items = Vec::with_capacity(a.len());
464                for v in a {
465                    items.push(v.to_string_repr());
466                }
467                format!("[{}]", items.join(", "))
468            }
469            Value::Map(m) => {
470                let mut items = Vec::with_capacity(m.len());
471                for (k, v) in m {
472                    items.push(format!("{}: {}", k, v.to_string_repr()));
473                }
474                format!("{{{}}}", items.join(", "))
475            }
476        }
477    }
478}
479
480impl From<bool> for Value {
481    fn from(b: bool) -> Self {
482        Value::Bool(b)
483    }
484}
485
486impl From<String> for Value {
487    fn from(s: String) -> Self {
488        Value::String(s)
489    }
490}
491
492impl From<&str> for Value {
493    fn from(s: &str) -> Self {
494        Value::String(s.to_string())
495    }
496}
497
498impl From<i8> for Value {
499    fn from(i: i8) -> Self {
500        Value::Integer(i as i64)
501    }
502}
503
504impl From<i16> for Value {
505    fn from(i: i16) -> Self {
506        Value::Integer(i as i64)
507    }
508}
509
510impl From<i32> for Value {
511    fn from(i: i32) -> Self {
512        Value::Integer(i as i64)
513    }
514}
515
516impl From<i64> for Value {
517    fn from(i: i64) -> Self {
518        Value::Integer(i)
519    }
520}
521
522impl From<isize> for Value {
523    fn from(i: isize) -> Self {
524        Value::Integer(i as i64)
525    }
526}
527
528impl From<u8> for Value {
529    fn from(i: u8) -> Self {
530        Value::Integer(i as i64)
531    }
532}
533
534impl From<u16> for Value {
535    fn from(i: u16) -> Self {
536        Value::Integer(i as i64)
537    }
538}
539
540impl From<u32> for Value {
541    fn from(i: u32) -> Self {
542        Value::Integer(i as i64)
543    }
544}
545
546impl From<u64> for Value {
547    #[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
548    fn from(i: u64) -> Self {
549        // Use Float for values that don't fit in i64 to avoid silent truncation
550        if i > i64::MAX as u64 {
551            Value::Float(i as f64)
552        } else {
553            Value::Integer(i as i64)
554        }
555    }
556}
557
558impl From<usize> for Value {
559    #[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
560    fn from(i: usize) -> Self {
561        // Use Float for values that don't fit in i64 to avoid silent truncation
562        if i > i64::MAX as usize {
563            Value::Float(i as f64)
564        } else {
565            Value::Integer(i as i64)
566        }
567    }
568}
569
570impl From<f32> for Value {
571    fn from(f: f32) -> Self {
572        Value::Float(f as f64)
573    }
574}
575
576impl From<f64> for Value {
577    fn from(f: f64) -> Self {
578        Value::Float(f)
579    }
580}
581
582impl<T> From<Vec<T>> for Value
583where
584    T: Into<Value>,
585{
586    fn from(v: Vec<T>) -> Self {
587        Value::Array(v.into_iter().map(|t| t.into()).collect())
588    }
589}
590
591impl<K, V> From<std::collections::HashMap<K, V>> for Value
592where
593    K: Into<String>,
594    V: Into<Value>,
595{
596    fn from(m: std::collections::HashMap<K, V>) -> Self {
597        Value::Map(m.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
598    }
599}
600
601#[cfg(test)]
602mod tests {
603    use super::*;
604
605    #[test]
606    fn test_value_null() {
607        let value = Value::null();
608        assert!(value.is_null());
609        assert!(!value.is_string());
610    }
611
612    #[test]
613    fn test_value_bool() {
614        let value = Value::bool(true);
615        assert!(value.is_bool());
616        assert_eq!(value.as_bool(), Some(true));
617    }
618
619    #[test]
620    fn test_value_string() {
621        let value = Value::string("hello");
622        assert!(value.is_string());
623        assert_eq!(value.as_str(), Some("hello"));
624    }
625
626    #[test]
627    fn test_value_integer() {
628        let value = Value::integer(42);
629        assert!(value.is_integer());
630        assert!(value.is_number());
631        assert_eq!(value.as_integer(), Some(42));
632    }
633
634    #[test]
635    fn test_value_float() {
636        let value = Value::float(3.14);
637        assert!(value.is_float());
638        assert!(value.is_number());
639        assert_eq!(value.as_float(), Some(3.14));
640    }
641
642    #[test]
643    fn test_value_array() {
644        let value = Value::array(vec![Value::integer(1), Value::integer(2)]);
645        assert!(value.is_array());
646        assert_eq!(value.len(), 2);
647    }
648
649    #[test]
650    fn test_value_map() {
651        let value = Value::map(vec![
652            ("key1".to_string(), Value::string("value1".to_string())),
653            ("key2".to_string(), Value::string("value2".to_string())),
654        ]);
655        assert!(value.is_map());
656        assert_eq!(value.len(), 2);
657    }
658
659    #[test]
660    fn test_value_len() {
661        let value = Value::array(vec![Value::integer(1)]);
662        assert_eq!(value.len(), 1);
663
664        let value = Value::string("test");
665        assert_eq!(value.len(), 0);
666    }
667
668    #[test]
669    fn test_value_is_empty() {
670        let value = Value::array(vec![]);
671        assert!(value.is_empty());
672
673        let value = Value::map(vec![]);
674        assert!(value.is_empty());
675    }
676
677    #[test]
678    fn test_value_to_string_repr() {
679        assert_eq!(Value::null().to_string_repr(), "null");
680        assert_eq!(Value::bool(true).to_string_repr(), "true");
681        assert_eq!(Value::string("hello").to_string_repr(), "hello");
682        assert_eq!(Value::integer(42).to_string_repr(), "42");
683        assert_eq!(Value::float(3.14).to_string_repr(), "3.14");
684        assert_eq!(
685            Value::array(vec![Value::integer(1), Value::integer(2)]).to_string_repr(),
686            "[1, 2]"
687        );
688    }
689
690    #[test]
691    fn test_value_from_bool() {
692        let value: Value = true.into();
693        assert!(value.is_bool());
694    }
695
696    #[test]
697    fn test_value_from_string() {
698        let value: Value = "hello".into();
699        assert!(value.is_string());
700    }
701
702    #[test]
703    fn test_value_from_integer() {
704        let value: Value = 42i32.into();
705        assert!(value.is_integer());
706    }
707
708    #[test]
709    fn test_value_from_float() {
710        let value: Value = 3.14f64.into();
711        assert!(value.is_float());
712    }
713
714    #[test]
715    fn test_value_from_vec() {
716        let value: Value = vec![1, 2, 3].into();
717        assert!(value.is_array());
718        assert_eq!(value.len(), 3);
719    }
720
721    #[test]
722    fn test_value_default() {
723        let value: Value = Default::default();
724        assert!(value.is_null());
725    }
726
727    #[test]
728    fn test_value_clone() {
729        let value1 = Value::string("test".to_string());
730        let value2 = value1.clone();
731        assert_eq!(value1, value2);
732    }
733
734    #[test]
735    fn test_value_partial_eq() {
736        assert_eq!(
737            Value::string("a".to_string()),
738            Value::string("a".to_string())
739        );
740        assert_ne!(
741            Value::string("a".to_string()),
742            Value::string("b".to_string())
743        );
744    }
745
746    #[test]
747    fn test_value_as_array_mut() {
748        let value = Value::array(vec![Value::integer(1)]);
749        let arr = value.as_array().expect("test should succeed");
750        assert_eq!(arr.len(), 1);
751    }
752
753    #[test]
754    fn test_value_as_map_mut() {
755        let value = Value::map(vec![(
756            "key".to_string(),
757            Value::string("value".to_string()),
758        )]);
759        let map = value.as_map().expect("test should succeed");
760        assert_eq!(map.len(), 1);
761    }
762
763    #[test]
764    fn test_value_factory_methods() {
765        assert!(Value::null().is_null());
766        assert!(Value::bool(true).is_bool());
767        assert!(Value::string("test").is_string());
768        assert!(Value::integer(42).is_integer());
769        assert!(Value::float(3.14).is_float());
770        assert!(Value::array(vec![]).is_array());
771        assert!(Value::map(vec![]).is_map());
772    }
773
774    #[test]
775    fn test_value_from_usize() {
776        let value: Value = 42usize.into();
777        assert_eq!(value.as_integer(), Some(42));
778    }
779
780    #[test]
781    fn test_value_from_u64() {
782        let value: Value = 42u64.into();
783        assert_eq!(value.as_integer(), Some(42));
784    }
785
786    #[test]
787    fn test_value_from_i8() {
788        let value: Value = (-42i8).into();
789        assert_eq!(value.as_integer(), Some(-42));
790    }
791
792    #[test]
793    fn test_value_from_f32() {
794        let value: Value = Value::from(3.14f32);
795        let f = value.as_float().expect("test should succeed");
796        assert!((f - 3.14f64).abs() < 1e-6);
797    }
798
799    #[test]
800    fn test_value_from_hashmap() {
801        use std::collections::HashMap;
802
803        let mut map = HashMap::new();
804        map.insert("key1", "value1");
805        map.insert("key2", "value2");
806
807        let value: Value = map.into();
808        assert!(value.is_map());
809        assert_eq!(value.len(), 2);
810    }
811    #[test]
812    fn test_value_u16_helpers() {
813        let i = Value::integer(8080);
814        assert_eq!(i.as_u16(), Some(8080));
815        assert_eq!(i.to_u16(), Some(8080));
816
817        let s = Value::string("65535");
818        assert_eq!(s.as_u16(), None);
819        assert_eq!(s.to_u16(), Some(65535));
820
821        let invalid = Value::integer(-1);
822        assert_eq!(invalid.as_u16(), None);
823        assert_eq!(invalid.as_u16_or(3000), 3000);
824    }
825}