1use std::ops::Index;
2use std::str::FromStr;
3
4use hashbrown::HashMap;
5use itertools::Itertools;
6
7#[derive(Debug, Clone, PartialEq, Default)]
8#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
9#[cfg_attr(feature = "serde", serde(untagged))]
10pub enum Value {
11 Int(i32),
12 Bool(bool),
13 Float(f64),
14 String(Box<str>),
15 Map(HashMap<String, Value>),
16 Array(Vec<Value>),
17 #[default]
18 None,
19}
20
21impl Value {
22 pub fn is_none(&self) -> bool {
23 matches!(self, Value::None)
24 }
25
26 pub fn as_array(&self) -> Option<Vec<Value>> {
27 match self {
28 Self::Array(v) => Some(v.clone()),
29 Self::String(q) => {
30 let xs = q
31 .split(',')
32 .map(|it| Value::String(it.into()))
33 .collect_vec();
34 Some(xs)
35 }
36 Self::Bool(b) => Some(vec![Value::String(b.to_string().into())]),
37 _ => None,
38 }
39 }
40}
41
42impl Index<&str> for Value {
43 type Output = Value;
44
45 fn index(&self, index: &str) -> &Self::Output {
46 match self {
47 Value::Map(map) => map.get(index).unwrap_or(&Value::None),
48 _ => unreachable!(),
49 }
50 }
51}
52
53impl Value {
54 pub fn to_bool(&self) -> bool {
55 match *self {
56 Value::Int(v) => v != 0,
57 Value::Bool(v) => v,
58 Value::Float(v) => v != 0.0,
59 Value::String(ref v) => !v.is_empty(),
60 Value::Map(ref v) => !v.is_empty(),
61 Value::None => false,
62 Value::Array(ref v) => !v.is_empty(),
63 }
64 }
65
66 pub fn map<T>(&self, f: impl Fn(&Self) -> T) -> Option<T> {
67 if self == &Value::None {
68 return None;
69 }
70
71 Some(f(self))
72 }
73
74 pub fn as_map(&self) -> Option<&HashMap<String, Value>> {
75 if let Self::Map(map) = self {
76 Some(map)
77 } else {
78 None
79 }
80 }
81
82 pub fn as_map_mut(&mut self) -> Option<&mut HashMap<String, Value>> {
83 if let Self::Map(map) = self {
84 Some(map)
85 } else {
86 None
87 }
88 }
89
90 pub fn as_int(&self) -> Option<i32> {
91 if let Self::Int(v) = self {
92 Some(*v)
93 } else {
94 None
95 }
96 }
97
98 pub fn as_string(&self) -> Option<&str> {
99 if let Self::String(v) = self {
100 Some(v)
101 } else {
102 None
103 }
104 }
105
106 pub fn as_bool(&self) -> Option<bool> {
107 if let Self::Bool(v) = self {
108 Some(*v)
109 } else {
110 None
111 }
112 }
113}
114
115impl FromStr for Value {
116 type Err = ();
117
118 fn from_str(s: &str) -> Result<Self, Self::Err> {
119 if let Ok(value) = s.parse() {
120 return Ok(Value::Int(value));
121 }
122
123 if let Ok(value) = s.parse() {
124 return Ok(Value::Float(value));
125 }
126
127 let value = match () {
128 _ if s.eq_ignore_ascii_case("true") => Value::Bool(true),
129 _ if s.eq_ignore_ascii_case("false") => Value::Bool(false),
130 _ if s.eq_ignore_ascii_case("none") => Value::None,
131 _ => Value::String(Box::from(s)),
132 };
133
134 Ok(value)
135 }
136}