rusty_rules/
value.rs

1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::collections::BTreeMap;
4use std::hash::Hash;
5use std::net::{AddrParseError, IpAddr};
6use std::result::Result as StdResult;
7use std::str::FromStr;
8
9use serde_json::{Number, Value as JsonValue};
10
11/// Represents possible values returned by fetchers
12#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
13pub enum Value<'a> {
14    #[default]
15    None,
16    String(Cow<'a, str>),
17    Number(Number),
18    Bool(bool),
19    Ip(IpAddr),
20    Array(Vec<Value<'a>>),
21    Map(BTreeMap<String, Value<'a>>),
22}
23
24impl<'a> Value<'a> {
25    pub(crate) fn into_static(self) -> Value<'static> {
26        match self {
27            Value::None => Value::None,
28            Value::String(s) => Value::String(Cow::Owned(s.into_owned())),
29            Value::Number(n) => Value::Number(n),
30            Value::Bool(b) => Value::Bool(b),
31            Value::Ip(ip) => Value::Ip(ip),
32            Value::Array(arr) => Value::Array(arr.into_iter().map(|v| v.into_static()).collect()),
33            Value::Map(map) => {
34                Value::Map(map.into_iter().map(|(k, v)| (k, v.into_static())).collect())
35            }
36        }
37    }
38
39    /// Returns the value as a string if it is a string
40    pub fn as_str(&self) -> Option<&str> {
41        match self {
42            Value::String(s) => Some(s),
43            _ => None,
44        }
45    }
46
47    /// Returns the value as an integer if it is a number
48    pub fn as_i64(&self) -> Option<i64> {
49        match self {
50            Value::Number(n) => n.as_i64(),
51            _ => None,
52        }
53    }
54
55    /// Returns the value as a float if it is a number
56    pub fn as_f64(&self) -> Option<f64> {
57        match self {
58            Value::Number(n) => n.as_f64(),
59            _ => None,
60        }
61    }
62
63    /// Returns the value as a boolean if it is a boolean
64    pub fn as_bool(&self) -> Option<bool> {
65        match self {
66            Value::Bool(b) => Some(*b),
67            _ => None,
68        }
69    }
70
71    /// Returns the value as an IP address if it is an IP address
72    pub fn as_ip(&self) -> Option<IpAddr> {
73        match self {
74            Value::Ip(ip) => Some(*ip),
75            _ => None,
76        }
77    }
78
79    /// Converts the value to an IP address, returning an error if it cannot be converted
80    pub fn to_ip(&self) -> Result<IpAddr, AddrParseError> {
81        match self {
82            Value::String(s) => IpAddr::from_str(s),
83            Value::Ip(ip) => Ok(*ip),
84            _ => IpAddr::from_str(""), // Return an error for non-string or non-IP values
85        }
86    }
87
88    /// Returns the value as an array if it is an array
89    pub fn as_array(&self) -> Option<&Vec<Value<'a>>> {
90        match self {
91            Value::Array(arr) => Some(arr),
92            _ => None,
93        }
94    }
95
96    /// Returns the value as a mutable array if it is an array
97    pub fn as_array_mut(&mut self) -> Option<&mut Vec<Value<'a>>> {
98        match self {
99            Value::Array(arr) => Some(arr),
100            _ => None,
101        }
102    }
103
104    /// Returns the value as a map if it is a map
105    pub fn as_map(&self) -> Option<&BTreeMap<String, Value<'a>>> {
106        match self {
107            Value::Map(map) => Some(map),
108            _ => None,
109        }
110    }
111
112    /// Returns the value as a mutable map if it is a map
113    pub fn as_map_mut(&mut self) -> Option<&mut BTreeMap<String, Value<'a>>> {
114        match self {
115            Value::Map(map) => Some(map),
116            _ => None,
117        }
118    }
119}
120
121impl PartialOrd for Value<'_> {
122    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
123        match (self, other) {
124            (Value::None, Value::None) => Some(Ordering::Equal),
125            (Value::String(s), Value::String(t)) => s.partial_cmp(t),
126            (Value::Number(i), Value::Number(j)) => {
127                if let (Some(i), Some(j)) = (i.as_i64(), j.as_i64()) {
128                    i.partial_cmp(&j)
129                } else if let (Some(i), Some(j)) = (i.as_f64(), j.as_f64()) {
130                    i.partial_cmp(&j)
131                } else {
132                    None
133                }
134            }
135            (Value::Bool(i), Value::Bool(j)) => i.partial_cmp(j),
136            (Value::Ip(i), Value::Ip(j)) => i.partial_cmp(j),
137            (Value::Array(i), Value::Array(j)) => i.partial_cmp(j),
138            (Value::Map(i), Value::Map(j)) => i.partial_cmp(j),
139            _ => None,
140        }
141    }
142}
143
144impl From<JsonValue> for Value<'_> {
145    fn from(value: JsonValue) -> Self {
146        match value {
147            JsonValue::Null => Value::None,
148            JsonValue::String(s) => Value::String(Cow::Owned(s)),
149            JsonValue::Number(n) => Value::Number(n),
150            JsonValue::Bool(b) => Value::Bool(b),
151            JsonValue::Array(arr) => {
152                let arr = arr.into_iter().map(|v| v.into()).collect();
153                Value::Array(arr)
154            }
155            JsonValue::Object(obj) => {
156                let map = obj.into_iter().map(|(k, v)| (k, v.into())).collect();
157                Value::Map(map)
158            }
159        }
160    }
161}
162
163impl<'a> From<&'a JsonValue> for Value<'a> {
164    fn from(value: &'a JsonValue) -> Self {
165        match value {
166            JsonValue::Null => Value::None,
167            JsonValue::String(s) => Value::String(Cow::Borrowed(s)),
168            JsonValue::Number(n) => Value::Number(n.clone()),
169            JsonValue::Bool(b) => Value::Bool(*b),
170            JsonValue::Array(arr) => Value::Array(arr.iter().map(|v| v.into()).collect()),
171            JsonValue::Object(obj) => {
172                let map = obj.iter().map(|(k, v)| (k.clone(), v.into())).collect();
173                Value::Map(map)
174            }
175        }
176    }
177}
178
179impl From<String> for Value<'_> {
180    #[inline(always)]
181    fn from(s: String) -> Self {
182        Value::String(Cow::Owned(s))
183    }
184}
185
186impl<'a> From<&'a String> for Value<'a> {
187    #[inline(always)]
188    fn from(s: &'a String) -> Self {
189        Value::String(Cow::Borrowed(s))
190    }
191}
192
193impl<'a> From<&'a str> for Value<'a> {
194    #[inline(always)]
195    fn from(s: &'a str) -> Self {
196        Value::String(Cow::Borrowed(s))
197    }
198}
199
200impl<'a> From<Cow<'a, str>> for Value<'a> {
201    #[inline(always)]
202    fn from(s: Cow<'a, str>) -> Self {
203        Value::String(s)
204    }
205}
206
207macro_rules! impl_from_int {
208    ($($ty:ty),*) => {
209        $(
210            impl From<$ty> for Value<'_> {
211                #[inline(always)]
212                fn from(i: $ty) -> Self {
213                    Value::Number(Number::from(i))
214                }
215            }
216        )*
217    };
218}
219
220impl_from_int!(i8, u8, i16, u16, i32, u32, i64, u64, isize, usize);
221
222impl TryFrom<f64> for Value<'_> {
223    type Error = ();
224
225    #[inline(always)]
226    fn try_from(f: f64) -> StdResult<Self, Self::Error> {
227        Ok(Value::Number(Number::from_f64(f).ok_or(())?))
228    }
229}
230
231impl From<bool> for Value<'_> {
232    #[inline(always)]
233    fn from(b: bool) -> Self {
234        Value::Bool(b)
235    }
236}
237
238impl From<IpAddr> for Value<'_> {
239    #[inline(always)]
240    fn from(ip: IpAddr) -> Self {
241        Value::Ip(ip)
242    }
243}
244
245impl<'a, T> From<Option<T>> for Value<'a>
246where
247    T: Into<Value<'a>>,
248{
249    #[inline(always)]
250    fn from(opt: Option<T>) -> Self {
251        match opt {
252            Some(v) => v.into(),
253            None => Value::None,
254        }
255    }
256}
257
258impl<'a, T: Into<Value<'a>>> From<Vec<T>> for Value<'a> {
259    #[inline(always)]
260    fn from(arr: Vec<T>) -> Self {
261        Value::Array(arr.into_iter().map(|v| v.into()).collect())
262    }
263}