use super::JsonValueExt;
use crate::{
JsonValue, Map, Record, Uuid,
datetime::{self, Date, DateTime, Time},
helper,
model::Model,
validation::Validation,
};
use chrono::NaiveDateTime;
use convert_case::{Case, Casing};
use rust_decimal::Decimal;
use serde_json::map::Entry;
use std::{
borrow::Cow,
mem,
net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr},
num::{ParseFloatError, ParseIntError},
str::{FromStr, ParseBoolError},
time::Duration,
};
use url::Url;
pub trait JsonObjectExt {
fn get_bool(&self, key: &str) -> Option<bool>;
fn get_u8(&self, key: &str) -> Option<u8>;
fn get_u16(&self, key: &str) -> Option<u16>;
fn get_u32(&self, key: &str) -> Option<u32>;
fn get_u64(&self, key: &str) -> Option<u64>;
fn get_usize(&self, key: &str) -> Option<usize>;
fn get_i8(&self, key: &str) -> Option<i8>;
fn get_i16(&self, key: &str) -> Option<i16>;
fn get_i32(&self, key: &str) -> Option<i32>;
fn get_i64(&self, key: &str) -> Option<i64>;
fn get_isize(&self, key: &str) -> Option<isize>;
fn get_f32(&self, key: &str) -> Option<f32>;
fn get_f64(&self, key: &str) -> Option<f64>;
fn get_str(&self, key: &str) -> Option<&str>;
fn get_uuid(&self, key: &str) -> Option<Uuid>;
fn get_date(&self, key: &str) -> Option<Date>;
fn get_time(&self, key: &str) -> Option<Time>;
fn get_date_time(&self, key: &str) -> Option<DateTime>;
fn get_naive_date_time(&self, key: &str) -> Option<NaiveDateTime>;
fn get_duration(&self, key: &str) -> Option<Duration>;
fn get_array(&self, key: &str) -> Option<&Vec<JsonValue>>;
fn get_u64_array(&self, key: &str) -> Option<Vec<u64>>;
fn get_i64_array(&self, key: &str) -> Option<Vec<i64>>;
fn get_f32_array(&self, key: &str) -> Option<Vec<f32>>;
fn get_f64_array(&self, key: &str) -> Option<Vec<f64>>;
fn get_str_array(&self, key: &str) -> Option<Vec<&str>>;
fn get_map_array(&self, key: &str) -> Option<Vec<&Map>>;
fn get_object(&self, key: &str) -> Option<&Map>;
fn get_populated(&self, key: &str) -> Option<&Map>;
fn get_translated(&self, key: &str) -> Option<&str>;
fn parse_bool(&self, key: &str) -> Option<Result<bool, ParseBoolError>>;
fn parse_u8(&self, key: &str) -> Option<Result<u8, ParseIntError>>;
fn parse_u16(&self, key: &str) -> Option<Result<u16, ParseIntError>>;
fn parse_u32(&self, key: &str) -> Option<Result<u32, ParseIntError>>;
fn parse_u64(&self, key: &str) -> Option<Result<u64, ParseIntError>>;
fn parse_usize(&self, key: &str) -> Option<Result<usize, ParseIntError>>;
fn parse_i8(&self, key: &str) -> Option<Result<i8, ParseIntError>>;
fn parse_i16(&self, key: &str) -> Option<Result<i16, ParseIntError>>;
fn parse_i32(&self, key: &str) -> Option<Result<i32, ParseIntError>>;
fn parse_i64(&self, key: &str) -> Option<Result<i64, ParseIntError>>;
fn parse_isize(&self, key: &str) -> Option<Result<isize, ParseIntError>>;
fn parse_f32(&self, key: &str) -> Option<Result<f32, ParseFloatError>>;
fn parse_f64(&self, key: &str) -> Option<Result<f64, ParseFloatError>>;
fn parse_string(&self, key: &str) -> Option<Cow<'_, str>>;
fn parse_array<T: FromStr>(&self, key: &str) -> Option<Result<Vec<T>, <T as FromStr>::Err>>;
fn parse_str_array(&self, key: &str) -> Option<Vec<&str>>;
fn parse_enum_values(&self, key: &str) -> Option<Vec<JsonValue>>;
fn parse_object(&self, key: &str) -> Option<&Map>;
fn parse_uuid(&self, key: &str) -> Option<Result<Uuid, uuid::Error>>;
fn parse_decimal(&self, key: &str) -> Option<Result<Decimal, rust_decimal::Error>>;
fn parse_date(&self, key: &str) -> Option<Result<Date, chrono::format::ParseError>>;
fn parse_time(&self, key: &str) -> Option<Result<Time, chrono::format::ParseError>>;
fn parse_date_time(&self, key: &str) -> Option<Result<DateTime, chrono::format::ParseError>>;
fn parse_naive_date_time(
&self,
key: &str,
) -> Option<Result<NaiveDateTime, chrono::format::ParseError>>;
fn parse_duration(&self, key: &str) -> Option<Result<Duration, datetime::ParseDurationError>>;
fn parse_url(&self, key: &str) -> Option<Result<Url, url::ParseError>>;
fn parse_ip(&self, key: &str) -> Option<Result<IpAddr, AddrParseError>>;
fn parse_ipv4(&self, key: &str) -> Option<Result<Ipv4Addr, AddrParseError>>;
fn parse_ipv6(&self, key: &str) -> Option<Result<Ipv6Addr, AddrParseError>>;
fn parse_model<M: Model>(&self, key: &str) -> Option<Result<M, Validation>>;
fn pointer(&self, pointer: &str) -> Option<&JsonValue>;
fn pointer_mut(&mut self, pointer: &str) -> Option<&mut JsonValue>;
fn upsert(&mut self, key: impl Into<String>, value: impl Into<JsonValue>) -> Option<JsonValue>;
fn insert_if_vacant(&mut self, key: impl Into<String>, value: impl Into<JsonValue>) -> bool;
fn clone_from_populated<K: AsRef<str>>(&mut self, key: &str, fields: &[K]);
fn extract_from_populated<K: AsRef<str>>(&mut self, key: &str, fields: &[K]);
fn clone_entries<K: AsRef<str>>(&self, keys: &[K]) -> Self;
fn extract_entries<K: AsRef<str>>(&mut self, keys: &[K]) -> Self;
fn remove_entries<K: AsRef<str>>(&mut self, keys: &[K]);
fn rename_keys(&mut self, case: Case);
fn read_as_model<M: Model>(&self) -> Result<M, Validation>;
fn to_string(&self) -> String;
fn to_query_string(&self) -> String;
fn into_avro_record(self) -> Record;
fn from_entry(key: impl Into<String>, value: impl Into<JsonValue>) -> Self;
fn from_entries(entries: JsonValue) -> Self;
fn data_entry(value: Map) -> Self;
fn data_entries(values: Vec<Map>) -> Self;
fn data_item(value: impl Into<JsonValue>) -> Self;
fn data_items<T: Into<JsonValue>>(values: Vec<T>) -> Self;
}
impl JsonObjectExt for Map {
#[inline]
fn get_bool(&self, key: &str) -> Option<bool> {
self.get(key).and_then(|v| v.as_bool())
}
#[inline]
fn get_u8(&self, key: &str) -> Option<u8> {
self.get(key)
.and_then(|v| v.as_u64())
.and_then(|i| u8::try_from(i).ok())
}
#[inline]
fn get_u16(&self, key: &str) -> Option<u16> {
self.get(key)
.and_then(|v| v.as_u64())
.and_then(|i| u16::try_from(i).ok())
}
#[inline]
fn get_u32(&self, key: &str) -> Option<u32> {
self.get(key)
.and_then(|v| v.as_u64())
.and_then(|i| u32::try_from(i).ok())
}
#[inline]
fn get_u64(&self, key: &str) -> Option<u64> {
self.get(key).and_then(|v| v.as_u64())
}
#[inline]
fn get_usize(&self, key: &str) -> Option<usize> {
self.get(key)
.and_then(|v| v.as_u64())
.and_then(|i| usize::try_from(i).ok())
}
#[inline]
fn get_i8(&self, key: &str) -> Option<i8> {
self.get(key)
.and_then(|v| v.as_i64())
.and_then(|i| i8::try_from(i).ok())
}
#[inline]
fn get_i16(&self, key: &str) -> Option<i16> {
self.get(key)
.and_then(|v| v.as_i64())
.and_then(|i| i16::try_from(i).ok())
}
#[inline]
fn get_i32(&self, key: &str) -> Option<i32> {
self.get(key)
.and_then(|v| v.as_i64())
.and_then(|i| i32::try_from(i).ok())
}
#[inline]
fn get_i64(&self, key: &str) -> Option<i64> {
self.get(key).and_then(|v| v.as_i64())
}
#[inline]
fn get_isize(&self, key: &str) -> Option<isize> {
self.get(key)
.and_then(|v| v.as_i64())
.and_then(|i| isize::try_from(i).ok())
}
#[inline]
fn get_f32(&self, key: &str) -> Option<f32> {
self.get(key).and_then(|v| v.as_f64()).map(|f| f as f32)
}
#[inline]
fn get_f64(&self, key: &str) -> Option<f64> {
self.get(key).and_then(|v| v.as_f64())
}
#[inline]
fn get_str(&self, key: &str) -> Option<&str> {
self.get(key).and_then(|v| v.as_str())
}
#[inline]
fn get_uuid(&self, key: &str) -> Option<Uuid> {
self.get_str(key).and_then(|s| s.parse().ok())
}
#[inline]
fn get_date(&self, key: &str) -> Option<Date> {
self.get_str(key).and_then(|s| s.parse().ok())
}
#[inline]
fn get_time(&self, key: &str) -> Option<Time> {
self.get_str(key).and_then(|s| s.parse().ok())
}
#[inline]
fn get_date_time(&self, key: &str) -> Option<DateTime> {
self.get_str(key).and_then(|s| s.parse().ok())
}
#[inline]
fn get_naive_date_time(&self, key: &str) -> Option<NaiveDateTime> {
self.get_str(key).and_then(|s| s.parse().ok())
}
#[inline]
fn get_duration(&self, key: &str) -> Option<Duration> {
self.get_str(key)
.and_then(|s| datetime::parse_duration(s).ok())
}
#[inline]
fn get_array(&self, key: &str) -> Option<&Vec<JsonValue>> {
self.get(key).and_then(|v| v.as_array())
}
#[inline]
fn get_u64_array(&self, key: &str) -> Option<Vec<u64>> {
self.get_array(key)
.map(|values| values.iter().filter_map(|v| v.as_u64()).collect())
}
#[inline]
fn get_i64_array(&self, key: &str) -> Option<Vec<i64>> {
self.get_array(key)
.map(|values| values.iter().filter_map(|v| v.as_i64()).collect())
}
#[inline]
fn get_f32_array(&self, key: &str) -> Option<Vec<f32>> {
self.get_array(key).map(|values| {
values
.iter()
.filter_map(|v| v.as_f64().map(|f| f as f32))
.collect()
})
}
#[inline]
fn get_f64_array(&self, key: &str) -> Option<Vec<f64>> {
self.get_array(key)
.map(|values| values.iter().filter_map(|v| v.as_f64()).collect())
}
#[inline]
fn get_str_array(&self, key: &str) -> Option<Vec<&str>> {
self.get_array(key)
.map(|values| values.iter().filter_map(|v| v.as_str()).collect())
}
#[inline]
fn get_map_array(&self, key: &str) -> Option<Vec<&Map>> {
self.get_array(key).map(|values| {
values
.iter()
.filter_map(|v| v.as_object())
.collect::<Vec<_>>()
})
}
#[inline]
fn get_object(&self, key: &str) -> Option<&Map> {
self.get(key).and_then(|v| v.as_object())
}
#[inline]
fn get_populated(&self, key: &str) -> Option<&Map> {
let populated_field = [key, "_populated"].concat();
self.get_object(&populated_field)
}
#[inline]
fn get_translated(&self, key: &str) -> Option<&str> {
let translated_field = [key, "_translated"].concat();
self.get_str(&translated_field)
}
fn parse_bool(&self, key: &str) -> Option<Result<bool, ParseBoolError>> {
let value = self.get(key);
value
.and_then(|v| v.as_bool())
.map(Ok)
.or_else(|| value.and_then(|v| v.as_str()).map(|s| s.parse()))
}
fn parse_u8(&self, key: &str) -> Option<Result<u8, ParseIntError>> {
let value = self.get(key);
value
.and_then(|v| v.as_u64())
.and_then(|i| u8::try_from(i).ok())
.map(Ok)
.or_else(|| value.and_then(|v| v.as_str()).map(|s| s.parse()))
}
fn parse_u16(&self, key: &str) -> Option<Result<u16, ParseIntError>> {
let value = self.get(key);
value
.and_then(|v| v.as_u64())
.and_then(|i| u16::try_from(i).ok())
.map(Ok)
.or_else(|| value.and_then(|v| v.as_str()).map(|s| s.parse()))
}
fn parse_u32(&self, key: &str) -> Option<Result<u32, ParseIntError>> {
let value = self.get(key);
value
.and_then(|v| v.as_u64())
.and_then(|i| u32::try_from(i).ok())
.map(Ok)
.or_else(|| value.and_then(|v| v.as_str()).map(|s| s.parse()))
}
fn parse_u64(&self, key: &str) -> Option<Result<u64, ParseIntError>> {
let value = self.get(key);
value
.and_then(|v| v.as_u64())
.map(Ok)
.or_else(|| value.and_then(|v| v.as_str()).map(|s| s.parse()))
}
fn parse_usize(&self, key: &str) -> Option<Result<usize, ParseIntError>> {
let value = self.get(key);
value
.and_then(|v| v.as_u64())
.and_then(|i| usize::try_from(i).ok())
.map(Ok)
.or_else(|| value.and_then(|v| v.as_str()).map(|s| s.parse()))
}
fn parse_i8(&self, key: &str) -> Option<Result<i8, ParseIntError>> {
let value = self.get(key);
value
.and_then(|v| v.as_i64())
.and_then(|i| i8::try_from(i).ok())
.map(Ok)
.or_else(|| value.and_then(|v| v.as_str()).map(|s| s.parse()))
}
fn parse_i16(&self, key: &str) -> Option<Result<i16, ParseIntError>> {
let value = self.get(key);
value
.and_then(|v| v.as_i64())
.and_then(|i| i16::try_from(i).ok())
.map(Ok)
.or_else(|| value.and_then(|v| v.as_str()).map(|s| s.parse()))
}
fn parse_i32(&self, key: &str) -> Option<Result<i32, ParseIntError>> {
let value = self.get(key);
value
.and_then(|v| v.as_i64())
.and_then(|i| i32::try_from(i).ok())
.map(Ok)
.or_else(|| value.and_then(|v| v.as_str()).map(|s| s.parse()))
}
fn parse_i64(&self, key: &str) -> Option<Result<i64, ParseIntError>> {
let value = self.get(key);
value
.and_then(|v| v.as_i64())
.map(Ok)
.or_else(|| value.and_then(|v| v.as_str()).map(|s| s.parse()))
}
fn parse_isize(&self, key: &str) -> Option<Result<isize, ParseIntError>> {
let value = self.get(key);
value
.and_then(|v| v.as_i64())
.and_then(|i| isize::try_from(i).ok())
.map(Ok)
.or_else(|| value.and_then(|v| v.as_str()).map(|s| s.parse()))
}
fn parse_f32(&self, key: &str) -> Option<Result<f32, ParseFloatError>> {
let value = self.get(key);
value
.and_then(|v| v.as_f64())
.map(|f| Ok(f as f32))
.or_else(|| value.and_then(|v| v.as_str()).map(|s| s.parse()))
}
fn parse_f64(&self, key: &str) -> Option<Result<f64, ParseFloatError>> {
let value = self.get(key);
value
.and_then(|v| v.as_f64())
.map(Ok)
.or_else(|| value.and_then(|v| v.as_str()).map(|s| s.parse()))
}
fn parse_string(&self, key: &str) -> Option<Cow<'_, str>> {
self.get(key)
.and_then(|v| {
v.as_str()
.map(|s| Cow::Borrowed(s.trim()))
.or_else(|| Some(v.to_string().into()))
})
.filter(|s| !s.is_empty())
}
fn parse_array<T: FromStr>(&self, key: &str) -> Option<Result<Vec<T>, <T as FromStr>::Err>> {
let values = match self.get(key)? {
JsonValue::String(s) => helper::parse_str_array(s, ',')
.into_iter()
.filter_map(|s| (!s.is_empty()).then_some(Cow::Borrowed(s)))
.collect::<Vec<_>>(),
JsonValue::Array(vec) => vec
.iter()
.filter(|v| !v.is_null())
.filter_map(|v| v.parse_string())
.collect::<Vec<_>>(),
_ => return None,
};
let mut vec = Vec::with_capacity(values.len());
for value in values {
match value.parse() {
Ok(v) => vec.push(v),
Err(err) => return Some(Err(err)),
}
}
(!vec.is_empty()).then_some(Ok(vec))
}
fn parse_str_array(&self, key: &str) -> Option<Vec<&str>> {
self.get(key)
.and_then(|v| match v {
JsonValue::String(s) => Some(helper::parse_str_array(s, ',')),
JsonValue::Array(v) => Some(v.iter().filter_map(|v| v.as_str()).collect()),
_ => None,
})
.and_then(|values| {
let vec = values.iter().map(|s| s.trim()).collect::<Vec<_>>();
(!vec.is_empty()).then_some(vec)
})
}
fn parse_enum_values(&self, key: &str) -> Option<Vec<JsonValue>> {
self.get(key)
.and_then(|v| match v {
JsonValue::String(s) => {
let values = helper::parse_str_array(s, '|');
let vec = values
.iter()
.map(|s| {
let s = s.trim();
if let Ok(integer) = s.parse::<i64>() {
JsonValue::Number(integer.into())
} else {
JsonValue::String(s.to_owned())
}
})
.collect::<Vec<_>>();
Some(vec)
}
JsonValue::Array(vec) => Some(vec.to_owned()),
_ => None,
})
.filter(|vec| !vec.is_empty())
}
#[inline]
fn parse_object(&self, key: &str) -> Option<&Map> {
self.get_object(key).filter(|o| !o.is_empty())
}
fn parse_uuid(&self, key: &str) -> Option<Result<Uuid, uuid::Error>> {
self.get_str(key)
.map(|s| s.trim_start_matches("urn:uuid:"))
.filter(|s| !s.chars().all(|c| c == '0' || c == '-'))
.map(|s| s.parse())
}
#[inline]
fn parse_decimal(&self, key: &str) -> Option<Result<Decimal, rust_decimal::Error>> {
self.get_str(key).map(|s| s.parse())
}
#[inline]
fn parse_date(&self, key: &str) -> Option<Result<Date, chrono::format::ParseError>> {
self.get_str(key).map(|s| s.parse())
}
#[inline]
fn parse_time(&self, key: &str) -> Option<Result<Time, chrono::format::ParseError>> {
self.get_str(key).map(|s| s.parse())
}
#[inline]
fn parse_date_time(&self, key: &str) -> Option<Result<DateTime, chrono::format::ParseError>> {
self.get_str(key).map(|s| s.parse())
}
#[inline]
fn parse_naive_date_time(
&self,
key: &str,
) -> Option<Result<NaiveDateTime, chrono::format::ParseError>> {
self.get_str(key).map(|s| s.parse())
}
#[inline]
fn parse_duration(&self, key: &str) -> Option<Result<Duration, datetime::ParseDurationError>> {
self.get_str(key).map(datetime::parse_duration)
}
#[inline]
fn parse_url(&self, key: &str) -> Option<Result<Url, url::ParseError>> {
self.get_str(key).map(|s| s.parse())
}
#[inline]
fn parse_ip(&self, key: &str) -> Option<Result<IpAddr, AddrParseError>> {
self.get_str(key).map(|s| s.parse())
}
#[inline]
fn parse_ipv4(&self, key: &str) -> Option<Result<Ipv4Addr, AddrParseError>> {
self.get_str(key).map(|s| s.parse())
}
#[inline]
fn parse_ipv6(&self, key: &str) -> Option<Result<Ipv6Addr, AddrParseError>> {
self.get_str(key).map(|s| s.parse())
}
fn parse_model<M: Model>(&self, key: &str) -> Option<Result<M, Validation>> {
self.get_object(key).map(|data| {
let mut model = M::new();
let validation = model.read_map(data);
if validation.is_success() {
Ok(model)
} else {
Err(validation)
}
})
}
fn pointer(&self, pointer: &str) -> Option<&JsonValue> {
let path = pointer.strip_prefix('/')?;
if let Some(position) = path.find('/') {
let (key, pointer) = path.split_at(position);
self.get(key)?.pointer(pointer)
} else {
self.get(path)
}
}
fn pointer_mut(&mut self, pointer: &str) -> Option<&mut JsonValue> {
let path = pointer.strip_prefix('/')?;
if let Some(position) = path.find('/') {
let (key, pointer) = path.split_at(position);
self.get_mut(key)?.pointer_mut(pointer)
} else {
self.get_mut(path)
}
}
#[inline]
fn upsert(&mut self, key: impl Into<String>, value: impl Into<JsonValue>) -> Option<JsonValue> {
self.insert(key.into(), value.into())
}
#[inline]
fn insert_if_vacant(&mut self, key: impl Into<String>, value: impl Into<JsonValue>) -> bool {
let key = key.into();
if let Entry::Vacant(entry) = self.entry(key) {
entry.insert(value.into());
true
} else {
false
}
}
fn clone_from_populated<K: AsRef<str>>(&mut self, key: &str, fields: &[K]) {
let mut object = Map::new();
if let Some(map) = self.get_populated(key) {
for field in fields {
let field = field.as_ref();
if let Some(value) = map.get(field) {
object.insert(field.to_owned(), value.to_owned());
}
}
}
self.append(&mut object);
}
fn extract_from_populated<K: AsRef<str>>(&mut self, key: &str, fields: &[K]) {
let mut object = Map::new();
let populated_field = [key, "_populated"].concat();
if let Some(&mut ref mut map) = self
.get_mut(&populated_field)
.and_then(|v| v.as_object_mut())
{
for field in fields {
let field = field.as_ref();
if let Some(value) = map.remove(field) {
object.insert(field.to_owned(), value);
}
}
}
self.append(&mut object);
}
#[inline]
fn clone_entries<K: AsRef<str>>(&self, keys: &[K]) -> Self {
let mut map = Map::new();
for key in keys {
let field = key.as_ref();
if let Some(value) = self.get(field) {
map.insert(field.to_owned(), value.to_owned());
}
}
map
}
#[inline]
fn extract_entries<K: AsRef<str>>(&mut self, keys: &[K]) -> Self {
let mut map = Map::new();
for key in keys {
let field = key.as_ref();
if let Some(value) = self.remove(field) {
map.insert(field.to_owned(), value);
}
}
map
}
#[inline]
fn remove_entries<K: AsRef<str>>(&mut self, keys: &[K]) {
for key in keys {
self.remove(key.as_ref());
}
}
#[inline]
fn rename_keys(&mut self, case: Case) {
for (key, value) in mem::take(self) {
self.insert(key.to_case(case), value);
}
}
fn read_as_model<M: Model>(&self) -> Result<M, Validation> {
let mut model = M::new();
let validation = model.read_map(self);
if validation.is_success() {
Ok(model)
} else {
Err(validation)
}
}
#[inline]
fn to_string(&self) -> String {
serde_json::to_string(&self).unwrap_or_default()
}
#[inline]
fn to_query_string(&self) -> String {
serde_qs::to_string(&self).unwrap_or_default()
}
fn into_avro_record(self) -> Record {
let mut record = Record::with_capacity(self.len());
for (field, value) in self.into_iter() {
record.push((field, value.into()));
}
record
}
#[inline]
fn from_entry(key: impl Into<String>, value: impl Into<JsonValue>) -> Self {
let mut map = Map::new();
map.insert(key.into(), value.into());
map
}
#[inline]
fn from_entries(entries: JsonValue) -> Self {
if let JsonValue::Object(map) = entries {
map
} else {
Map::new()
}
}
#[inline]
fn data_entry(value: Map) -> Self {
let mut map = Map::new();
map.insert("entry".to_owned(), value.into());
map
}
#[inline]
fn data_entries(values: Vec<Map>) -> Self {
let mut map = Map::new();
map.insert("num_entries".to_owned(), values.len().into());
map.insert("entries".to_owned(), values.into());
map
}
#[inline]
fn data_item(value: impl Into<JsonValue>) -> Self {
let mut map = Map::new();
map.insert("item".to_owned(), value.into());
map
}
#[inline]
fn data_items<T: Into<JsonValue>>(values: Vec<T>) -> Self {
let mut map = Map::new();
map.insert("num_items".to_owned(), values.len().into());
map.insert("items".to_owned(), values.into());
map
}
}
#[cfg(test)]
mod tests {
use crate::{
Map,
extension::{JsonObjectExt, JsonValueExt},
};
#[test]
fn it_parses_str_array() {
let mut map = Map::new();
map.upsert("roles", vec!["admin", "", "worker"]);
assert_eq!(
map.get_str_array("roles"),
Some(vec!["admin", "", "worker"])
);
assert_eq!(
map.parse_str_array("roles"),
Some(vec!["admin", "", "worker"])
);
assert_eq!(
map.parse_array::<String>("roles"),
Some(Ok(vec!["admin".to_owned(), "worker".to_owned()]))
);
}
#[test]
fn it_lookups_json_value() {
let mut map = Map::new();
map.upsert("entries", vec![Map::from_entry("name", "alice")]);
map.upsert("total", 1);
assert_eq!(map.pointer("total"), None);
assert_eq!(map.pointer("/total").and_then(|v| v.as_usize()), Some(1));
assert_eq!(
map.pointer("/entries/0/name").and_then(|v| v.as_str()),
Some("alice")
);
}
}