use std::cmp::Ordering;
use std::str::FromStr;
use rust_decimal::Decimal;
use super::compare::compare_field_bytes;
use super::reader::{read_f64, read_integer, read_str};
pub fn decimal_reading(buf: &[u8], offset: usize) -> Option<Decimal> {
if let Some(text) = read_str(buf, offset) {
return Decimal::from_str(text.trim())
.or_else(|_| Decimal::from_scientific(text.trim()))
.ok();
}
if let Some(integer) = read_integer(buf, offset) {
return Decimal::try_from_i128_with_scale(integer, 0).ok();
}
read_f64(buf, offset).and_then(|float| Decimal::try_from(float).ok())
}
pub fn compare_decimal_field_bytes(
a_buf: &[u8],
a_range: (usize, usize),
b_buf: &[u8],
b_range: (usize, usize),
) -> Ordering {
match (
decimal_reading(a_buf, a_range.0),
decimal_reading(b_buf, b_range.0),
) {
(Some(a), Some(b)) => a.cmp(&b),
_ => compare_field_bytes(a_buf, a_range, b_buf, b_range),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn cell(value: &serde_json::Value) -> Vec<u8> {
nodedb_types::json_to_msgpack(value).expect("encode cell")
}
fn cmp(a: &serde_json::Value, b: &serde_json::Value) -> Ordering {
let (a, b) = (cell(a), cell(b));
compare_decimal_field_bytes(&a, (0, a.len()), &b, (0, b.len()))
}
#[test]
fn text_and_uint64_cells_order_numerically() {
let small = serde_json::json!("5");
let mid = serde_json::json!(9_223_372_036_854_775_808_u64);
let max = serde_json::json!(u64::MAX);
assert_eq!(cmp(&small, &mid), Ordering::Less);
assert_eq!(cmp(&mid, &max), Ordering::Less);
assert_eq!(cmp(&max, &small), Ordering::Greater);
}
#[test]
fn text_cells_order_by_value() {
assert_eq!(
cmp(&serde_json::json!("10"), &serde_json::json!("9.5")),
Ordering::Greater
);
assert_eq!(
cmp(&serde_json::json!("-2.50"), &serde_json::json!("-2.5")),
Ordering::Equal
);
}
#[test]
fn non_numeric_cell_falls_back() {
assert_eq!(
cmp(&serde_json::json!("abc"), &serde_json::json!("5")),
Ordering::Greater
);
}
}