Skip to content

Commit f9f88ed

Browse files
authored
fix: improved number compare between different types (#679)
1 parent f7583b9 commit f9f88ed

File tree

2 files changed

+56
-5
lines changed

2 files changed

+56
-5
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pest = "2.1.0"
2828
pest_derive = "2.1.0"
2929
serde = "1.0.0"
3030
serde_json = "1.0.39"
31+
num-order = "1.2.0"
3132
walkdir = { version = "2.2.3", optional = true }
3233
rhai = { version = "1.16.1", optional = true, features = ["sync", "serde"] }
3334
rust-embed = { version = "8.0.0", optional = true, features = ["include-exclude"] }

src/helpers/helper_extras.rs

+55-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
//! Helpers for boolean operations
2+
3+
use std::cmp::Ordering;
4+
use std::str::FromStr;
5+
6+
use num_order::NumOrd;
27
use serde_json::Value as Json;
38

49
use crate::json::value::JsonTruthy;
5-
use std::cmp::Ordering;
610

711
handlebars_helper!(eq: |x: Json, y: Json| x == y);
812
handlebars_helper!(ne: |x: Json, y: Json| x != y);
@@ -24,13 +28,48 @@ handlebars_helper!(len: |x: Json| {
2428

2529
fn compare_json(x: &Json, y: &Json) -> Option<Ordering> {
2630
fn cmp_num_str(a_num: &serde_json::Number, b_str: &str) -> Option<Ordering> {
27-
let a_f64 = a_num.as_f64()?;
28-
let b_f64 = b_str.parse::<f64>().ok()?;
29-
a_f64.partial_cmp(&b_f64)
31+
let b_num = serde_json::Number::from_str(b_str).ok()?;
32+
cmp_nums(a_num, &b_num)
33+
}
34+
35+
// this function relies on serde_json::Numbers coerce logic
36+
// for number value between [0, u64::MAX], is_u64() returns true
37+
// for number value between [i64::MIN, i64::MAX], is_i64() returns true
38+
// for others, is_f64() returns true, note that this behaviour is not
39+
// guaranteed according to serde_json docs
40+
fn cmp_nums(a_num: &serde_json::Number, b_num: &serde_json::Number) -> Option<Ordering> {
41+
if a_num.is_u64() {
42+
let a = a_num.as_u64()?;
43+
if b_num.is_u64() {
44+
NumOrd::num_partial_cmp(&a, &b_num.as_u64()?)
45+
} else if b_num.is_i64() {
46+
NumOrd::num_partial_cmp(&a, &b_num.as_i64()?)
47+
} else {
48+
NumOrd::num_partial_cmp(&a, &b_num.as_f64()?)
49+
}
50+
} else if a_num.is_i64() {
51+
let a = a_num.as_i64()?;
52+
if b_num.is_u64() {
53+
NumOrd::num_partial_cmp(&a, &b_num.as_u64()?)
54+
} else if b_num.is_i64() {
55+
NumOrd::num_partial_cmp(&a, &b_num.as_i64()?)
56+
} else {
57+
NumOrd::num_partial_cmp(&a, &b_num.as_f64()?)
58+
}
59+
} else {
60+
let a = a_num.as_f64()?;
61+
if b_num.is_u64() {
62+
NumOrd::num_partial_cmp(&a, &b_num.as_u64()?)
63+
} else if b_num.is_i64() {
64+
NumOrd::num_partial_cmp(&a, &b_num.as_i64()?)
65+
} else {
66+
NumOrd::num_partial_cmp(&a, &b_num.as_f64()?)
67+
}
68+
}
3069
}
3170

3271
match (x, y) {
33-
(Json::Number(a), Json::Number(b)) => a.as_f64().partial_cmp(&b.as_f64()),
72+
(Json::Number(a), Json::Number(b)) => cmp_nums(a, b),
3473
(Json::String(a), Json::String(b)) => Some(a.cmp(b)),
3574
(Json::Bool(a), Json::Bool(b)) => Some(a.cmp(b)),
3675
(Json::Number(a), Json::String(b)) => cmp_num_str(a, b),
@@ -133,6 +172,7 @@ mod test_conditions {
133172
test_condition("(gte 5 5)", true);
134173
test_condition("(lt 3 5)", true);
135174
test_condition("(lte 5 5)", true);
175+
test_condition("(lt 9007199254740992 9007199254740993)", true);
136176

137177
// Float comparisons
138178
test_condition("(gt 5.5 3.3)", true);
@@ -151,6 +191,16 @@ mod test_conditions {
151191
test_condition(r#"(lt 53 "35")"#, false);
152192
test_condition(r#"(lt "35" 53)"#, true);
153193
test_condition(r#"(gte "53" 53)"#, true);
194+
test_condition(r#"(lt -1 0)"#, true);
195+
test_condition(r#"(lt "-1" 0)"#, true);
196+
test_condition(r#"(lt "-1.00" 0)"#, true);
197+
test_condition(r#"(gt "1.00" 0)"#, true);
198+
test_condition(r#"(gt 0 -1)"#, true);
199+
test_condition(r#"(gt 0 "-1")"#, true);
200+
test_condition(r#"(gt 0 "-1.00")"#, true);
201+
test_condition(r#"(lt 0 "1.00")"#, true);
202+
// u64::MAX
203+
test_condition(r#"(gt 18446744073709551615 -1)"#, true);
154204

155205
// Boolean comparisons
156206
test_condition("(gt true false)", true);

0 commit comments

Comments
 (0)