1#[allow(clippy::module_name_repetitions)]
4pub use super::value::regex::ValueRegex;
5#[allow(clippy::module_name_repetitions)]
6pub use iter::{IterItem, ValueIter};
7
8use bytes::{Bytes, BytesMut};
9use chrono::{DateTime, SecondsFormat, Utc};
10use ordered_float::NotNan;
11use std::borrow::Cow;
12use std::{cmp::Ordering, collections::BTreeMap};
13
14use super::KeyString;
15use crate::path::ValuePath;
16
17mod convert;
18mod crud;
19mod display;
20mod iter;
21mod path;
22mod regex;
23
24#[cfg(any(test, feature = "arbitrary"))]
25mod arbitrary;
26#[cfg(any(test, feature = "lua"))]
27mod lua;
28mod serde;
29
30pub type StdError = Box<dyn std::error::Error + Send + Sync + 'static>;
32
33pub type ObjectMap = BTreeMap<KeyString, Value>;
35
36#[derive(Eq, PartialEq, Hash, Debug, Clone)]
38pub enum Value {
39 Bytes(Bytes),
41
42 Regex(ValueRegex),
46
47 Integer(i64),
49
50 Float(NotNan<f64>),
52
53 Boolean(bool),
55
56 Timestamp(DateTime<Utc>),
58
59 Object(ObjectMap),
61
62 Array(Vec<Value>),
64
65 Null,
67}
68
69impl Value {
70 pub const fn kind_str(&self) -> &str {
72 match self {
73 Self::Bytes(_) | Self::Regex(_) => "string",
74 Self::Timestamp(_) => "timestamp",
75 Self::Integer(_) => "integer",
76 Self::Float(_) => "float",
77 Self::Boolean(_) => "boolean",
78 Self::Object(_) => "map",
79 Self::Array(_) => "array",
80 Self::Null => "null",
81 }
82 }
83
84 pub fn merge(&mut self, incoming: Self) {
88 match (self, incoming) {
89 (Self::Bytes(self_bytes), Self::Bytes(ref incoming)) => {
90 let mut bytes = BytesMut::with_capacity(self_bytes.len() + incoming.len());
91 bytes.extend_from_slice(&self_bytes[..]);
92 bytes.extend_from_slice(&incoming[..]);
93 *self_bytes = bytes.freeze();
94 }
95 (current, incoming) => *current = incoming,
96 }
97 }
98
99 pub fn is_empty(&self) -> bool {
124 match &self {
125 Self::Boolean(_)
126 | Self::Bytes(_)
127 | Self::Regex(_)
128 | Self::Timestamp(_)
129 | Self::Float(_)
130 | Self::Integer(_) => false,
131 Self::Null => true,
132 Self::Object(v) => v.is_empty(),
133 Self::Array(v) => v.is_empty(),
134 }
135 }
136
137 #[allow(clippy::needless_pass_by_value)]
139 pub fn insert<'a>(
140 &mut self,
141 path: impl ValuePath<'a>,
142 insert_value: impl Into<Self>,
143 ) -> Option<Self> {
144 let insert_value = insert_value.into();
145 let path_iter = path.segment_iter().peekable();
146
147 crud::insert(self, (), path_iter, insert_value)
148 }
149
150 #[allow(clippy::needless_pass_by_value)]
155 pub fn remove<'a>(&mut self, path: impl ValuePath<'a>, prune: bool) -> Option<Self> {
156 crud::remove(self, &(), path.segment_iter(), prune)
157 .map(|(prev_value, _is_empty)| prev_value)
158 }
159
160 #[allow(clippy::needless_pass_by_value)]
162 pub fn get<'a>(&self, path: impl ValuePath<'a>) -> Option<&Self> {
163 crud::get(self, path.segment_iter())
164 }
165
166 #[allow(clippy::needless_pass_by_value)]
168 pub fn get_mut<'a>(&mut self, path: impl ValuePath<'a>) -> Option<&mut Self> {
169 crud::get_mut(self, path.segment_iter())
170 }
171
172 pub fn contains<'a>(&self, path: impl ValuePath<'a>) -> bool {
174 self.get(path).is_some()
175 }
176}
177
178impl PartialOrd for Value {
179 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
180 if std::mem::discriminant(self) != std::mem::discriminant(other) {
181 return None;
182 }
183 match (self, other) {
184 (Self::Bytes(a), Self::Bytes(b)) => a.partial_cmp(b),
185 (Self::Regex(a), Self::Regex(b)) => a.partial_cmp(b),
186 (Self::Integer(a), Self::Integer(b)) => a.partial_cmp(b),
187 (Self::Float(a), Self::Float(b)) => a.partial_cmp(b),
188 (Self::Boolean(a), Self::Boolean(b)) => a.partial_cmp(b),
189 (Self::Timestamp(a), Self::Timestamp(b)) => a.partial_cmp(b),
190 (Self::Object(a), Self::Object(b)) => a.partial_cmp(b),
191 (Self::Array(a), Self::Array(b)) => a.partial_cmp(b),
192 _ => None,
193 }
194 }
195}
196
197#[must_use]
199pub fn simdutf_bytes_utf8_lossy(v: &[u8]) -> Cow<'_, str> {
200 simdutf8::basic::from_utf8(v).map_or_else(
201 |_| {
202 const REPLACEMENT: &str = "\u{FFFD}";
203
204 let mut res = String::with_capacity(v.len());
205 for chunk in v.utf8_chunks() {
206 res.push_str(chunk.valid());
207 if !chunk.invalid().is_empty() {
208 res.push_str(REPLACEMENT);
209 }
210 }
211 Cow::Owned(res)
212 },
213 Cow::Borrowed,
214 )
215}
216
217#[must_use]
219pub fn timestamp_to_string(timestamp: &DateTime<Utc>) -> String {
220 timestamp.to_rfc3339_opts(SecondsFormat::AutoSi, true)
221}
222
223#[cfg(test)]
224mod test {
225 use quickcheck::{QuickCheck, TestResult};
226
227 use crate::path;
228 use crate::path::BorrowedSegment;
229
230 use super::*;
231
232 mod corner_cases {
233 use super::*;
234
235 #[test]
236 fn remove_prune_map_with_map() {
237 let mut value = Value::from(BTreeMap::default());
238 let key = "foo.bar";
239 let marker = Value::from(true);
240 assert_eq!(value.insert(key, marker.clone()), None);
241 assert_eq!(value.remove(key, true), Some(marker));
243 assert!(!value.contains("foo"));
244 }
245
246 #[test]
247 fn remove_prune_map_with_array() {
248 let mut value = Value::from(BTreeMap::default());
249 let key = "foo[0]";
250 let marker = Value::from(true);
251 assert_eq!(value.insert(key, marker.clone()), None);
252 assert_eq!(value.remove(key, true), Some(marker));
254 assert!(!value.contains("foo"));
255 }
256
257 #[test]
258 fn remove_prune_array_with_map() {
259 let mut value = Value::from(Vec::<Value>::default());
260 let key = "[0].bar";
261 let marker = Value::from(true);
262 assert_eq!(value.insert(key, marker.clone()), None);
263 assert_eq!(value.remove(key, true), Some(marker));
265 assert!(!value.contains(path!(0)));
266 }
267
268 #[test]
269 fn remove_prune_array_with_array() {
270 let mut value = Value::from(Vec::<Value>::default());
271 let key = "[0][0]";
272 let marker = Value::from(true);
273 assert_eq!(value.insert(key, marker.clone()), None);
274 assert_eq!(value.remove(key, true), Some(marker));
276 assert!(!value.contains(path!(0)));
277 }
278 }
279
280 #[test]
281 fn quickcheck_value() {
282 fn inner(mut path: Vec<BorrowedSegment<'static>>) -> TestResult {
283 let mut value = Value::from(BTreeMap::default());
284 let mut marker = Value::from(true);
285
286 path.insert(0, BorrowedSegment::from("field"));
288
289 assert_eq!(value.insert(&path, marker.clone()), None, "inserting value");
290 assert_eq!(value.get(&path), Some(&marker), "retrieving value");
291 assert_eq!(
292 value.get_mut(&path),
293 Some(&mut marker),
294 "retrieving mutable value"
295 );
296
297 assert_eq!(value.remove(&path, true), Some(marker), "removing value");
298
299 TestResult::passed()
300 }
301
302 QuickCheck::new()
303 .tests(100)
304 .max_tests(200)
305 .quickcheck(inner as fn(Vec<BorrowedSegment<'static>>) -> TestResult);
306 }
307
308 #[test]
309 fn partial_ord_value() {
310 assert_eq!(
311 Value::from(50).partial_cmp(&Value::from(77)),
312 Some(Ordering::Less)
313 );
314 assert_eq!(
315 Value::from("zzz").partial_cmp(&Value::from("aaa")),
316 Some(Ordering::Greater)
317 );
318 assert_eq!(
319 Value::from(10.5).partial_cmp(&Value::from(10.5)),
320 Some(Ordering::Equal)
321 );
322 assert_eq!(Value::from(10.5).partial_cmp(&Value::from(10)), None);
323 }
324}