Fix number comparison logic.

This commit is contained in:
Tom Alexander 2020-06-13 15:31:52 -04:00
parent 4d28120732
commit 03ff75b2de
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE

View File

@ -366,74 +366,27 @@ impl CompareContextElement for serde_json::Value {
.partial_compare(other);
}
// Handle other serde_json::Value
match other.to_any().downcast_ref::<Self>() {
None => (),
Some(other_json_value) => {
return match (self, other_json_value) {
(
serde_json::Value::Bool(self_boolean),
serde_json::Value::Bool(other_boolean),
) => self_boolean.partial_cmp(other_boolean),
(
serde_json::Value::Number(self_number),
serde_json::Value::Number(other_number),
) => return compare_json_numbers(self_number, other_number),
(
serde_json::Value::String(self_string),
serde_json::Value::Number(other_number),
) => return compare_json_numbers(self_string, other_number),
(
serde_json::Value::Number(self_number),
serde_json::Value::String(other_string),
) => return compare_json_numbers(self_number, other_string),
(
serde_json::Value::String(self_string),
serde_json::Value::String(other_string),
) => self_string.partial_cmp(other_string),
(
serde_json::Value::Array(_self_array),
serde_json::Value::Array(_other_array),
) => {
// TODO: is this reachable given the early convert to string before this block?
return self
.render(&Vec::new())
.unwrap_or("".to_owned())
.partial_cmp(
&other_json_value
.render(&Vec::new())
.unwrap_or("".to_owned()),
);
}
_ => None,
};
}
let maybe_json_other = other.to_any().downcast_ref::<Self>();
let maybe_literal_other = other.to_any().downcast_ref::<OwnedLiteral>();
// If they're both strings, compare them directly
match (self, maybe_json_other, maybe_literal_other) {
// If they're both strings, compare them directly
(
serde_json::Value::String(self_string),
Some(serde_json::Value::String(other_string)),
_,
) => return self_string.partial_cmp(&other_string),
(
serde_json::Value::String(self_string),
_,
Some(OwnedLiteral::LString(other_string)),
) => return self_string.partial_cmp(&other_string),
// Otherwise, convert to numbers are compare them that way
(_, Some(json_other), _) => return compare_json_numbers(self, json_other),
(_, _, Some(literal_other)) => return compare_json_numbers(self, literal_other),
_ => panic!("Unimplemented comparison type."),
}
// Handle literals
match (self, other.to_any().downcast_ref::<OwnedLiteral>()) {
(_, None) => (),
(serde_json::Value::String(self_text), Some(OwnedLiteral::LString(other_text))) => {
return self_text.partial_cmp(other_text);
}
(_, Some(OwnedLiteral::LString(text))) => {
return self.to_string().partial_cmp(text);
}
(_, Some(OwnedLiteral::LPositiveInteger(other_num))) => {
let other_json_num: serde_json::Number = std::convert::From::from(*other_num);
return self.partial_compare(
&serde_json::Value::Number(other_json_num) as &dyn ContextElement
);
}
(_, Some(OwnedLiteral::LFloat(other_num))) => {
let other_json_num = serde_json::Number::from_f64(*other_num);
match other_json_num {
None => return None,
Some(ojn) => return self
.partial_compare(&serde_json::Value::Number(ojn) as &dyn ContextElement),
}
}
};
None
}
}
@ -445,6 +398,32 @@ enum JsonNumber {
Failure,
}
impl From<&serde_json::Value> for JsonNumber {
/// Convert from a JSON value to a number for comparison based on
/// the logic described at
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Greater_than
fn from(original: &serde_json::Value) -> Self {
match original {
serde_json::Value::Null => JsonNumber::UnsignedInteger(0),
serde_json::Value::Bool(boolean) => {
if *boolean {
JsonNumber::UnsignedInteger(1)
} else {
JsonNumber::UnsignedInteger(0)
}
}
serde_json::Value::Number(num) => num.into(),
serde_json::Value::String(text) => text.into(),
serde_json::Value::Array(_) => {
panic!("Only primitives should be cast to numbers for comparisons")
}
serde_json::Value::Object(_) => {
panic!("Only primitives should be cast to numbers for comparisons")
}
}
}
}
impl From<&String> for JsonNumber {
fn from(original: &String) -> Self {
match original.parse::<u64>() {