use serde::{Deserialize, Serialize, de::DeserializeOwned};
use serde_json::{Map, Value};
#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)]
pub struct TestJson(Value);
impl TestJson {
#[inline]
pub fn value(&self) -> TestJsonValue<'_> {
TestJsonValue(&self.0)
}
}
macro_rules! impl_types {
($($(#[$docs:meta])* ($ty:ty, $name:ident, $method:ident)),*) => {
$(
$(#[$docs])*
pub fn $name(&self) -> $ty {
self.0.$method().expect(stringify!($name))
}
)*
};
}
macro_rules! impl_assert_types {
($($(#[$docs:meta])* ($ty:ty, $name:ident, $method:ident)),*) => {
$(
$(#[$docs])*
#[track_caller]
pub fn $name(&self, value: $ty) {
assert_eq!(self.$method(), value);
}
)*
};
}
macro_rules! impl_array_types {
($($(#[$docs:meta])* ($ty:ty, $name:ident, $method:ident)),*) => {
$(
$(#[$docs])*
pub fn $name(&self) -> Vec<$ty> {
self.array().iter().map(|value| value.$method()).collect()
}
)*
};
}
macro_rules! impl_assert_array_types {
($($(#[$docs:meta])* ($ty:ty, $name:ident, $method:ident)),*) => {
$(
$(#[$docs])*
#[track_caller]
pub fn $name(&self, values: &[$ty]) {
assert_eq!(self.$method(), values);
}
)*
};
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub struct TestJsonValue<'a>(&'a Value);
impl PartialEq<Value> for TestJsonValue<'_> {
fn eq(&self, other: &Value) -> bool {
self.0 == other
}
}
impl<'a> TestJsonValue<'a> {
impl_types!(
(i64, i64, as_i64),
(f64, f64, as_f64),
(bool, bool, as_bool)
);
impl_array_types!(
(i64, i64_array, i64),
(f64, f64_array, f64),
(bool, bool_array, bool)
);
impl_assert_types!(
(i64, assert_i64, i64),
(bool, assert_bool, bool),
(&str, assert_string, string)
);
impl_assert_array_types!(
(i64, assert_i64_array, i64_array),
(bool, assert_bool_array, bool_array),
(&str, assert_string_array, string_array)
);
#[track_caller]
pub fn assert_f64(&self, value: f64) {
assert!((self.f64() - value).abs() < f64::EPSILON);
}
#[track_caller]
pub fn assert_f64_array(&self, values: &[f64]) {
assert!(
self.f64_array()
.iter()
.zip(values)
.all(|(a, b)| (*a - *b).abs() < f64::EPSILON)
);
}
pub fn string(&self) -> &'a str {
self.0.as_str().expect("string")
}
pub fn string_array(&self) -> Vec<&'a str> {
self.array().iter().map(|value| value.string()).collect()
}
pub fn array(&self) -> TestJsonArray<'a> {
TestJsonArray(self.0.as_array().expect("array"))
}
pub fn object(&self) -> TestJsonObject<'a> {
TestJsonObject(self.0.as_object().expect("object"))
}
pub fn object_array(&self) -> Vec<TestJsonObject<'a>> {
self.array().iter().map(|value| value.object()).collect()
}
#[track_caller]
pub fn assert_null(&self) {
assert!(self.0.is_null())
}
#[track_caller]
pub fn assert_not_null(&self) {
assert!(!self.0.is_null())
}
pub fn deserialize<T: DeserializeOwned>(&self) -> T {
serde_json::from_value(self.0.clone()).expect("valid json")
}
}
#[derive(Debug, Copy, Clone)]
pub struct TestJsonArray<'a>(&'a [Value]);
impl<T> PartialEq<T> for TestJsonArray<'_>
where
T: AsRef<[Value]>,
{
fn eq(&self, other: &T) -> bool {
self.0 == other.as_ref()
}
}
impl<'a> TestJsonArray<'a> {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn get(&self, idx: usize) -> TestJsonValue<'a> {
self.get_opt(idx)
.unwrap_or_else(|| panic!("expect index `{idx}`"))
}
pub fn get_opt(&self, idx: usize) -> Option<TestJsonValue<'a>> {
self.0.get(idx).map(TestJsonValue)
}
pub fn iter(&self) -> impl Iterator<Item = TestJsonValue<'a>> {
self.0.iter().map(TestJsonValue)
}
#[track_caller]
pub fn assert_len(&self, len: usize) {
assert_eq!(self.len(), len);
}
#[track_caller]
pub fn assert_is_empty(&self) {
assert!(self.is_empty());
}
#[track_caller]
pub fn assert_contains(&self, f: impl FnMut(TestJsonValue<'_>) -> bool) {
assert!(self.0.iter().map(TestJsonValue).any(f));
}
#[track_caller]
pub fn assert_contains_exactly_one(&self, f: impl Fn(TestJsonValue<'_>) -> bool) {
assert_eq!(
self.0
.iter()
.map(TestJsonValue)
.filter(|value| f(*value))
.count(),
1
);
}
}
#[derive(Debug, Copy, Clone)]
pub struct TestJsonObject<'a>(&'a Map<String, Value>);
impl<'a> TestJsonObject<'a> {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn get(&self, name: impl AsRef<str>) -> TestJsonValue<'a> {
let name = name.as_ref();
self.get_opt(name)
.unwrap_or_else(|| panic!("expect key `{name}`"))
}
pub fn get_opt(&self, name: impl AsRef<str>) -> Option<TestJsonValue<'a>> {
self.0.get(name.as_ref()).map(TestJsonValue)
}
pub fn iter(&self) -> impl Iterator<Item = (&String, TestJsonValue<'a>)> {
self.0.iter().map(|(k, v)| (k, TestJsonValue(v)))
}
#[track_caller]
pub fn assert_len(&self, len: usize) {
assert_eq!(self.len(), len);
}
#[track_caller]
pub fn assert_is_empty(&self) {
assert!(self.is_empty());
}
}