use std::cmp::Ordering;
use std::convert::TryFrom;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::str::FromStr;
use std::sync::Arc;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[macro_export]
macro_rules! ijson {
($($json:tt)+) => {
$crate::Json::new(serde_json::json!($($json)+))
}
}
fn hash<H: Hasher>(value: &serde_json::Value, state: &mut H) {
match value {
serde_json::Value::Null => {
state.write_u8(0);
}
serde_json::Value::Bool(v) => {
state.write_u8(1);
v.hash(state);
}
serde_json::Value::Number(v) => {
state.write_u8(2);
if v.is_i64() {
v.as_i64().unwrap().hash(state);
} else if v.is_u64() {
v.as_u64().unwrap().hash(state);
} else {
v.as_f64().unwrap().to_bits().hash(state);
}
}
serde_json::Value::String(v) => {
state.write_u8(3);
v.hash(state);
}
serde_json::Value::Array(v) => {
state.write_u8(4);
for sv in v {
hash(sv, state);
}
}
serde_json::Value::Object(v) => {
state.write_u8(5);
for (sk, sv) in v {
sk.hash(state);
hash(sv, state);
}
}
}
}
fn partial_cmp(first: &serde_json::Value, second: &serde_json::Value) -> Option<Ordering> {
match (first, second) {
(serde_json::Value::Null, serde_json::Value::Null) => Some(Ordering::Equal),
(serde_json::Value::Bool(v1), serde_json::Value::Bool(v2)) => v1.partial_cmp(v2),
(serde_json::Value::Number(v1), serde_json::Value::Number(v2)) => {
if v1.is_i64() {
let v1 = v1.as_i64().unwrap();
if v2.is_i64() {
v1.partial_cmp(&v2.as_i64().unwrap())
} else if v2.is_u64() {
match i64::try_from(v2.as_u64().unwrap()) {
Ok(v2) => v1.partial_cmp(&v2),
Err(_) => Some(Ordering::Less),
}
} else {
(v1 as f64).partial_cmp(&v2.as_f64().unwrap())
}
} else if v1.is_u64() {
let v1 = v1.as_u64().unwrap();
if v2.is_i64() {
match u64::try_from(v2.as_i64().unwrap()) {
Ok(v2) => v1.partial_cmp(&v2),
Err(_) => Some(Ordering::Greater),
}
} else if v2.is_u64() {
v1.partial_cmp(&v2.as_u64().unwrap())
} else {
(v1 as f64).partial_cmp(&v2.as_f64().unwrap())
}
} else {
let v1 = v1.as_f64().unwrap();
if v2.is_i64() {
v1.partial_cmp(&(v2.as_i64().unwrap() as f64))
} else if v2.is_u64() {
v1.partial_cmp(&(v2.as_u64().unwrap() as f64))
} else {
v1.partial_cmp(&v2.as_f64().unwrap())
}
}
}
(serde_json::Value::String(v1), serde_json::Value::String(v2)) => v1.partial_cmp(v2),
(serde_json::Value::Array(v1), serde_json::Value::Array(v2)) => {
partial_cmp_by(v1.iter(), v2.iter(), partial_cmp)
}
(serde_json::Value::Object(v1), serde_json::Value::Object(v2)) => {
partial_cmp_by(v1.iter(), v2.iter(), |v1, v2| {
let (v1_key, v1_value) = v1;
let (v2_key, v2_value) = v2;
match v1_key.partial_cmp(v2_key) {
Some(Ordering::Equal) => partial_cmp(v1_value, v2_value),
non_eq => non_eq,
}
})
}
_ => None,
}
}
fn partial_cmp_by<I, F>(mut first: I, mut second: I, mut f: F) -> Option<Ordering>
where
I: Iterator,
F: FnMut(I::Item, I::Item) -> Option<Ordering>,
{
loop {
let x = match first.next() {
None => {
if second.next().is_none() {
return Some(Ordering::Equal);
} else {
return Some(Ordering::Less);
}
}
Some(val) => val,
};
let y = match second.next() {
None => return Some(Ordering::Greater),
Some(val) => val,
};
match f(x, y) {
Some(Ordering::Equal) => (),
non_eq => return non_eq,
}
}
}
#[derive(Clone, Eq, Debug)]
pub struct Json(pub Arc<serde_json::Value>);
impl Json {
pub fn new(value: serde_json::Value) -> Self {
Self(Arc::new(value))
}
}
impl From<serde_json::Value> for Json {
fn from(value: serde_json::Value) -> Self {
Self(Arc::new(value))
}
}
impl From<Arc<serde_json::Value>> for Json {
fn from(value: Arc<serde_json::Value>) -> Self {
Self(value)
}
}
impl Hash for Json {
fn hash<H: Hasher>(&self, state: &mut H) {
hash(&self.0, state);
}
}
impl PartialEq for Json {
fn eq(&self, other: &Self) -> bool {
self.partial_cmp(other) == Some(Ordering::Equal)
}
}
impl PartialOrd for Json {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
partial_cmp(&self.0, &other.0)
}
}
impl Deref for Json {
type Target = serde_json::Value;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Serialize for Json {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
(*self.0).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Json {
fn deserialize<D>(deserializer: D) -> Result<Json, D::Error>
where
D: Deserializer<'de>,
{
let v: serde_json::Value = Deserialize::deserialize(deserializer)?;
Ok(Json::new(v))
}
}
impl FromStr for Json {
type Err = serde_json::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let sj = serde_json::from_str(s)?;
Ok(Self::new(sj))
}
}
#[cfg(test)]
mod tests {
use super::Json;
use std::collections::HashSet;
fn json_u64() -> Json {
Json::new(serde_json::Value::Number(serde_json::Number::from(u64::max_value())))
}
fn json_i64() -> Json {
Json::new(serde_json::Value::Number(serde_json::Number::from(i64::min_value())))
}
#[test]
fn should_hash() {
assert_eq!(HashSet::from([ijson!(null)]), HashSet::from([ijson!(null)]));
assert_eq!(HashSet::from([json_i64()]), HashSet::from([json_i64()]));
assert_eq!(HashSet::from([json_u64()]), HashSet::from([json_u64()]));
assert_eq!(HashSet::from([ijson!(3.0)]), HashSet::from([ijson!(3.0)]));
assert_eq!(HashSet::from([ijson!("foo")]), HashSet::from([ijson!("foo")]));
assert_eq!(HashSet::from([ijson!(["foo"])]), HashSet::from([ijson!(["foo"])]));
assert_eq!(
HashSet::from([ijson!({"foo": true})]),
HashSet::from([ijson!({"foo": true})])
);
}
#[test]
fn should_compare() {
assert!(ijson!("foo1") < ijson!("foo2"));
assert_eq!(ijson!(null), ijson!(null));
assert!(ijson!(true) > ijson!(false));
assert!(ijson!(3) < ijson!(4));
assert!(ijson!(3) < ijson!(4.0));
assert_eq!(ijson!(4.0), ijson!(4.0));
assert!(ijson!(3.0) < ijson!(4));
assert!(ijson!([3.0, 4.0]) < ijson!([4.0, 3.0]));
assert_eq!(json_u64(), json_u64());
assert!(ijson!(3) < json_u64());
assert!(json_u64() > ijson!(3.0));
assert!(ijson!(3.0) < json_u64());
assert!(json_u64() > json_i64());
assert!(json_i64() < json_u64());
assert!(ijson!(3) > json_i64());
assert!(json_i64() < ijson!(3.0));
assert!(ijson!({}) == ijson!({}));
assert!(ijson!({"key": "value0"}) < ijson!({"key": "value1"}));
assert!(ijson!({"key": "value1"}) > ijson!({"key": "value0"}));
assert!(ijson!({"key1": "value0"}) > ijson!({"key0": "value1"}));
assert_eq!(ijson!({"key": "value"}), ijson!({"key": "value"}));
assert!(ijson!({"key": "value"}) > ijson!({}));
assert!(ijson!({}) < ijson!({"key": "value"}));
assert!(ijson!({"key": "value"}) > ijson!({}));
}
}