use rust_decimal::Decimal;
use serde::Deserialize;
#[derive(Deserialize, Debug, PartialEq)]
struct All {
#[serde(deserialize_with = "crate::serde_util::decimal")]
bare: Decimal,
#[serde(deserialize_with = "crate::serde_util::option_decimal")]
opt: Option<Decimal>,
#[serde(deserialize_with = "crate::serde_util::vec_decimal")]
vec: Vec<Decimal>,
#[serde(deserialize_with = "crate::serde_util::vec_vec_decimal")]
vecs: Vec<Vec<Decimal>>,
}
#[test]
fn bare_integer() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::decimal")]
v: Decimal,
}
let s: S = serde_json::from_str(r#"{"v": 42}"#).unwrap();
assert_eq!(s.v, Decimal::from(42));
}
#[test]
fn bare_float() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::decimal")]
v: Decimal,
}
let s: S = serde_json::from_str(r#"{"v": 3.14}"#).unwrap();
assert!(s.v > Decimal::from(3) && s.v < Decimal::from(4));
}
#[test]
fn bare_negative() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::decimal")]
v: Decimal,
}
let s: S = serde_json::from_str(r#"{"v": -99}"#).unwrap();
assert_eq!(s.v, Decimal::from(-99));
}
#[test]
fn bare_zero() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::decimal")]
v: Decimal,
}
let s: S = serde_json::from_str(r#"{"v": 0}"#).unwrap();
assert_eq!(s.v, Decimal::ZERO);
}
#[test]
fn bare_rejects_string() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::decimal")]
v: Decimal,
}
assert!(serde_json::from_str::<S>(r#"{"v": "94"}"#).is_err());
}
#[test]
fn bare_rejects_bool() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::decimal")]
v: Decimal,
}
assert!(serde_json::from_str::<S>(r#"{"v": true}"#).is_err());
}
#[test]
fn bare_rejects_null() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::decimal")]
v: Decimal,
}
assert!(serde_json::from_str::<S>(r#"{"v": null}"#).is_err());
}
#[test]
fn bare_rejects_array() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::decimal")]
v: Decimal,
}
assert!(serde_json::from_str::<S>(r#"{"v": [1]}"#).is_err());
}
#[test]
fn bare_rejects_object() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::decimal")]
v: Decimal,
}
assert!(serde_json::from_str::<S>(r#"{"v": {}}"#).is_err());
}
#[test]
fn option_some_number() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::option_decimal")]
v: Option<Decimal>,
}
let s: S = serde_json::from_str(r#"{"v": 7}"#).unwrap();
assert_eq!(s.v, Some(Decimal::from(7)));
}
#[test]
fn option_null() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::option_decimal")]
v: Option<Decimal>,
}
let s: S = serde_json::from_str(r#"{"v": null}"#).unwrap();
assert_eq!(s.v, None);
}
#[test]
fn option_rejects_string() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::option_decimal")]
v: Option<Decimal>,
}
assert!(serde_json::from_str::<S>(r#"{"v": "42"}"#).is_err());
}
#[test]
fn vec_numbers() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::vec_decimal")]
v: Vec<Decimal>,
}
let s: S = serde_json::from_str(r#"{"v": [1, 2.5, -3]}"#).unwrap();
assert_eq!(s.v.len(), 3);
assert_eq!(s.v[0], Decimal::from(1));
assert_eq!(s.v[2], Decimal::from(-3));
}
#[test]
fn vec_empty() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::vec_decimal")]
v: Vec<Decimal>,
}
let s: S = serde_json::from_str(r#"{"v": []}"#).unwrap();
assert!(s.v.is_empty());
}
#[test]
fn vec_rejects_string_element() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::vec_decimal")]
v: Vec<Decimal>,
}
assert!(serde_json::from_str::<S>(r#"{"v": [1, "2", 3]}"#).is_err());
}
#[test]
fn vec_rejects_not_array() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::vec_decimal")]
v: Vec<Decimal>,
}
assert!(serde_json::from_str::<S>(r#"{"v": 42}"#).is_err());
}
#[test]
fn vec_vec_numbers() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::vec_vec_decimal")]
v: Vec<Vec<Decimal>>,
}
let s: S = serde_json::from_str(r#"{"v": [[1, 2], [3]]}"#).unwrap();
assert_eq!(s.v.len(), 2);
assert_eq!(s.v[0], vec![Decimal::from(1), Decimal::from(2)]);
assert_eq!(s.v[1], vec![Decimal::from(3)]);
}
#[test]
fn vec_vec_empty_outer() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::vec_vec_decimal")]
v: Vec<Vec<Decimal>>,
}
let s: S = serde_json::from_str(r#"{"v": []}"#).unwrap();
assert!(s.v.is_empty());
}
#[test]
fn vec_vec_empty_inner() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::vec_vec_decimal")]
v: Vec<Vec<Decimal>>,
}
let s: S = serde_json::from_str(r#"{"v": [[], [1]]}"#).unwrap();
assert_eq!(s.v.len(), 2);
assert!(s.v[0].is_empty());
}
#[test]
fn vec_vec_rejects_string_in_inner() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::vec_vec_decimal")]
v: Vec<Vec<Decimal>>,
}
assert!(serde_json::from_str::<S>(r#"{"v": [[1, "2"]]}"#).is_err());
}
#[test]
fn vec_vec_rejects_flat_numbers() {
#[derive(Deserialize)]
struct S {
#[serde(deserialize_with = "crate::serde_util::vec_vec_decimal")]
v: Vec<Vec<Decimal>>,
}
assert!(serde_json::from_str::<S>(r#"{"v": [1, 2, 3]}"#).is_err());
}
#[test]
fn untagged_enum_string_not_stolen_by_scalar() {
#[derive(Deserialize, Debug, PartialEq)]
#[serde(untagged)]
enum Output {
Scalar(#[serde(deserialize_with = "crate::serde_util::decimal")] Decimal),
Vector(#[serde(deserialize_with = "crate::serde_util::vec_decimal")] Vec<Decimal>),
Err(serde_json::Value),
}
let o: Output = serde_json::from_str("42").unwrap();
assert!(matches!(o, Output::Scalar(d) if d == Decimal::from(42)));
let o: Output = serde_json::from_str("[1, 2]").unwrap();
assert!(matches!(o, Output::Vector(_)));
let o: Output = serde_json::from_str(r#""94""#).unwrap();
assert!(matches!(o, Output::Err(serde_json::Value::String(_))),
"string \"94\" must fall through to Err, got: {:?}", o);
let o: Output = serde_json::from_str(r#""hello""#).unwrap();
assert!(matches!(o, Output::Err(serde_json::Value::String(_))));
let o: Output = serde_json::from_str("null").unwrap();
assert!(matches!(o, Output::Err(serde_json::Value::Null)));
let o: Output = serde_json::from_str("true").unwrap();
assert!(matches!(o, Output::Err(serde_json::Value::Bool(true))));
let o: Output = serde_json::from_str(r#"{"x": 1}"#).unwrap();
assert!(matches!(o, Output::Err(serde_json::Value::Object(_))));
let o: Output = serde_json::from_str(r#"["a", "b"]"#).unwrap();
assert!(matches!(o, Output::Err(serde_json::Value::Array(_))));
}
#[test]
fn all_fields_happy_path() {
let json = r#"{"bare": 1, "opt": 2.5, "vec": [3, 4], "vecs": [[5], [6, 7]]}"#;
let a: All = serde_json::from_str(json).unwrap();
assert_eq!(a.bare, Decimal::from(1));
assert!(a.opt.is_some());
assert_eq!(a.vec.len(), 2);
assert_eq!(a.vecs.len(), 2);
}
#[test]
fn all_fields_opt_null() {
let json = r#"{"bare": 0, "opt": null, "vec": [], "vecs": []}"#;
let a: All = serde_json::from_str(json).unwrap();
assert_eq!(a.opt, None);
}