From 830bb06a9243b2632a589fee6a214083c1f2e7ba Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 17 May 2020 00:27:21 -0400 Subject: [PATCH] Unifying number comparison. --- src/bin.rs | 179 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 117 insertions(+), 62 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index ac1b409..29e72b2 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -192,46 +192,24 @@ impl CompareContextElement for serde_json::Value { ( serde_json::Value::Number(self_number), serde_json::Value::Number(other_number), - ) => match ( - self_number.as_f64(), - other_number.as_f64(), - self_number.as_u64(), - other_number.as_u64(), - self_number.as_i64(), - other_number.as_i64(), - ) { - (_, _, _, _, Some(self_int), Some(other_int)) => { - self_int.partial_cmp(&other_int) - } - (_, _, Some(self_uint), Some(other_uint), _, _) => { - self_uint.partial_cmp(&other_uint) - } - (_, _, Some(_self_uint), _, _, Some(_other_int)) => { - // If the previous matches did not catch - // it, then other must be negative and - // self must be larger than can be - // represented with an i64, therefore self - // is greater than other. - Some(Ordering::Greater) - } - (_, _, _, Some(_other_uint), Some(_self_int), _) => { - // If the previous matches did not catch - // it, then self must be negative and - // other must be larger than can be - // represented with an i64, therefore - // other is greater than self. - Some(Ordering::Less) - } - (Some(self_float), Some(other_float), _, _, _, _) => { - self_float.partial_cmp(&other_float) - } - _ => panic!("This should be impossible since u64 and i64 can both be converted to floats"), - }, + ) => 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)) => convert_vec_to_context_element(self_array).partial_cmp(&convert_vec_to_context_element(other_array)), + ( + serde_json::Value::Array(self_array), + serde_json::Value::Array(other_array), + ) => convert_vec_to_context_element(self_array) + .partial_cmp(&convert_vec_to_context_element(other_array)), _ => None, }; } @@ -246,35 +224,14 @@ impl CompareContextElement for serde_json::Value { ( serde_json::Value::String(self_string), OwnedLiteral::LPositiveInteger(other_num), - ) => return self_string.partial_cmp(&other_num.to_string()), - ( - serde_json::Value::Number(self_num), - OwnedLiteral::LString(other_string), - ) => return None, + ) => return compare_json_numbers(self_string, other_literal), + (serde_json::Value::Number(self_num), OwnedLiteral::LString(other_string)) => { + return compare_json_numbers(self_num, other_string) + } ( serde_json::Value::Number(self_num), OwnedLiteral::LPositiveInteger(other_num), - ) => match ( - self_num.as_u64(), - self_num.as_i64(), - self_num.as_f64(), - ) { - (Some(self_uint), _, _) => { - return self_uint.partial_cmp(other_num) - } - (_, Some(self_int), _) => { - return if other_num <= &(i64::max_value() as u64) { - self_int.partial_cmp(&(*other_num as i64)) - } else { - Some(Ordering::Less) - } - } - (_, _, Some(self_float)) => { - // TODO: How does javascript compare ints and floats? I'm just going to assume a cast to a string for now. - return self_float.to_string().partial_cmp(&other_num.to_string()) - } - (None, None, None) => panic!("This should be impossible since u64 and i64 can both be converted to floats") - } + ) => return compare_json_numbers(self_num, other_literal), (serde_json::Value::Array(_), _) => { // TODO todo!() @@ -311,6 +268,104 @@ fn convert_vec_to_context_element(array: &Vec) -> Vec<&dyn Co array.iter().map(|v| v as _).collect() } +#[derive(Debug)] +enum JsonNumber { + UnsignedInteger(u64), + SignedInteger(i64), + Decimal(f64), + Failure, +} + +impl From<&String> for JsonNumber { + fn from(original: &String) -> Self { + match original.parse::() { + Ok(num) => return JsonNumber::UnsignedInteger(num), + Err(_) => (), + }; + match original.parse::() { + Ok(num) => return JsonNumber::SignedInteger(num), + Err(_) => (), + }; + match original.parse::() { + Ok(num) => return JsonNumber::Decimal(num), + Err(_) => (), + }; + JsonNumber::Failure + } +} + +impl From<&serde_json::Number> for JsonNumber { + fn from(original: &serde_json::Number) -> Self { + match original.as_u64() { + Some(num) => return JsonNumber::UnsignedInteger(num), + None => (), + }; + match original.as_i64() { + Some(num) => return JsonNumber::SignedInteger(num), + None => (), + }; + match original.as_f64() { + Some(num) => return JsonNumber::Decimal(num), + None => (), + }; + JsonNumber::Failure + } +} + +impl From<&OwnedLiteral> for JsonNumber { + fn from(original: &OwnedLiteral) -> Self { + match original { + OwnedLiteral::LPositiveInteger(num) => JsonNumber::UnsignedInteger(*num), + OwnedLiteral::LString(text) => text.into(), + } + } +} + +/// Compare json numbers +/// +/// While this function can be called with two strings, it would not +/// make sense because javascript does not use numeric comparisons for +/// strings +fn compare_json_numbers(self_input: S, other_input: O) -> Option +where + S: Into, + O: Into, +{ + let self_number: JsonNumber = self_input.into(); + let other_number: JsonNumber = other_input.into(); + // println!( + // "compare_number_and_string {:?} | {:?}", + // self_number, other_number + // ); + // TODO: Figure out how javascript compares floats and ints + match (self_number, other_number) { + (JsonNumber::Failure, _) => return None, + (_, JsonNumber::Failure) => return None, + (JsonNumber::UnsignedInteger(self_num), JsonNumber::UnsignedInteger(other_num)) => { + return self_num.partial_cmp(&other_num) + } + (JsonNumber::UnsignedInteger(self_num), JsonNumber::SignedInteger(other_num)) => { + return Some(Ordering::Greater) + } + (JsonNumber::UnsignedInteger(self_num), JsonNumber::Decimal(other_num)) => return None, + + (JsonNumber::SignedInteger(self_num), JsonNumber::UnsignedInteger(other_num)) => { + return Some(Ordering::Less) + } + (JsonNumber::SignedInteger(self_num), JsonNumber::SignedInteger(other_num)) => { + return self_num.partial_cmp(&other_num) + } + (JsonNumber::SignedInteger(self_num), JsonNumber::Decimal(other_num)) => return None, + + (JsonNumber::Decimal(self_num), JsonNumber::UnsignedInteger(other_num)) => return None, + (JsonNumber::Decimal(self_num), JsonNumber::SignedInteger(other_num)) => return None, + (JsonNumber::Decimal(self_num), JsonNumber::Decimal(other_num)) => { + return self_num.partial_cmp(&other_num) + } + } + None +} + #[cfg(test)] mod tests { use super::*;