Skip to main content

nautilus_core/
serialization.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Common serialization traits and functions.
17//!
18//! This module provides custom serde deserializers and serializers for common
19//! patterns encountered when parsing exchange API responses, particularly:
20//!
21//! - Empty strings that should be interpreted as `None` or zero.
22//! - Type conversions from strings to primitives.
23//! - Decimal values represented as strings.
24
25use std::{borrow::Cow, str::FromStr};
26
27use bytes::Bytes;
28use rust_decimal::Decimal;
29use serde::{
30    Deserialize, Deserializer, Serialize, Serializer,
31    de::{Error, MapAccess, Unexpected, Visitor, value::MapAccessDeserializer},
32    ser::SerializeSeq,
33};
34use serde_json::value::RawValue;
35use ustr::Ustr;
36
37pub mod decimal;
38
39/// Sorted serialization for `AHashSet<T>` where element order must be deterministic.
40///
41/// Use with `#[serde(with = "nautilus_core::serialization::sorted_hashset")]`.
42pub mod sorted_hashset {
43    use ahash::AHashSet;
44    use serde::{Deserialize, Deserializer, Serialize, Serializer};
45
46    /// Serializes an `AHashSet<T>` as a sorted array for deterministic output.
47    ///
48    /// # Errors
49    ///
50    /// Returns an error if the underlying [`Serializer`] fails when writing the sorted vector.
51    pub fn serialize<T, S>(set: &AHashSet<T>, s: S) -> Result<S::Ok, S::Error>
52    where
53        T: Serialize + Ord,
54        S: Serializer,
55    {
56        let mut sorted: Vec<&T> = set.iter().collect();
57        sorted.sort_unstable();
58        sorted.serialize(s)
59    }
60
61    /// Deserializes an array into an `AHashSet<T>`.
62    ///
63    /// # Errors
64    ///
65    /// Returns an error if the underlying [`Deserializer`] fails when reading the source array.
66    pub fn deserialize<'de, T, D>(d: D) -> Result<AHashSet<T>, D::Error>
67    where
68        T: Deserialize<'de> + Eq + std::hash::Hash,
69        D: Deserializer<'de>,
70    {
71        let vec = Vec::<T>::deserialize(d)?;
72        Ok(vec.into_iter().collect())
73    }
74}
75
76/// Decimal visitor for direct JSON token conversion.
77///
78/// Directly visits JSON tokens without intermediate `serde_json::Value` allocation.
79/// Handles all JSON numeric representations: strings, integers, floats, and null.
80struct DecimalVisitor;
81
82impl<'de> Visitor<'de> for DecimalVisitor {
83    type Value = Decimal;
84
85    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
86        formatter.write_str("a decimal number as string, integer, or float")
87    }
88
89    // Fast path: borrowed string (zero-copy)
90    fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
91        if v.is_empty() {
92            return Ok(Decimal::ZERO);
93        }
94        parse_decimal_str(v).map_err(E::custom)
95    }
96
97    // Owned string (rare case, delegates to visit_str)
98    fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
99        self.visit_str(&v)
100    }
101
102    // Direct integer handling - no string conversion needed
103    fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> {
104        Ok(Decimal::from(v))
105    }
106
107    fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
108        Ok(Decimal::from(v))
109    }
110
111    fn visit_i128<E: Error>(self, v: i128) -> Result<Self::Value, E> {
112        Decimal::try_from_i128_with_scale(v, 0).map_err(E::custom)
113    }
114
115    fn visit_u128<E: Error>(self, v: u128) -> Result<Self::Value, E> {
116        let v = i128::try_from(v).map_err(E::custom)?;
117        Decimal::try_from_i128_with_scale(v, 0).map_err(E::custom)
118    }
119
120    // Float handling - direct conversion
121    fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
122        if !v.is_finite() {
123            return Err(E::invalid_value(Unexpected::Float(v), &self));
124        }
125        Decimal::try_from(v).map_err(E::custom)
126    }
127
128    fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
129        let number = serde_json::Number::deserialize(MapAccessDeserializer::new(map))?;
130        self.visit_str(&number.to_string())
131    }
132
133    // Null → zero (matches existing behavior)
134    fn visit_unit<E: Error>(self) -> Result<Self::Value, E> {
135        Ok(Decimal::ZERO)
136    }
137
138    fn visit_none<E: Error>(self) -> Result<Self::Value, E> {
139        Ok(Decimal::ZERO)
140    }
141}
142
143/// Optional decimal visitor for direct JSON token conversion.
144///
145/// Handles null values as `None` and empty strings as `None`.
146/// Uses `deserialize_any` approach to handle all JSON value types uniformly.
147struct OptionalDecimalVisitor;
148
149impl<'de> Visitor<'de> for OptionalDecimalVisitor {
150    type Value = Option<Decimal>;
151
152    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
153        formatter.write_str("null or a decimal number as string, integer, or float")
154    }
155
156    // Fast path: borrowed string (zero-copy)
157    // Empty string → None (different from DecimalVisitor which returns ZERO)
158    fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
159        if v.is_empty() {
160            return Ok(None);
161        }
162        DecimalVisitor.visit_str(v).map(Some)
163    }
164
165    fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
166        self.visit_str(&v)
167    }
168
169    fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> {
170        DecimalVisitor.visit_i64(v).map(Some)
171    }
172
173    fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
174        DecimalVisitor.visit_u64(v).map(Some)
175    }
176
177    fn visit_i128<E: Error>(self, v: i128) -> Result<Self::Value, E> {
178        DecimalVisitor.visit_i128(v).map(Some)
179    }
180
181    fn visit_u128<E: Error>(self, v: u128) -> Result<Self::Value, E> {
182        DecimalVisitor.visit_u128(v).map(Some)
183    }
184
185    fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
186        DecimalVisitor.visit_f64(v).map(Some)
187    }
188
189    fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
190        DecimalVisitor.visit_map(map).map(Some)
191    }
192
193    // Null → None
194    fn visit_unit<E: Error>(self) -> Result<Self::Value, E> {
195        Ok(None)
196    }
197
198    fn visit_none<E: Error>(self) -> Result<Self::Value, E> {
199        Ok(None)
200    }
201}
202
203fn json_token_text(raw: &RawValue) -> serde_json::Result<Cow<'_, str>> {
204    let text = raw.get();
205    if text.starts_with('"') {
206        // Generic deserializers can supply RawValue text without lexical validation.
207        if let Some(contents) = text
208            .strip_prefix('"')
209            .and_then(|text| text.strip_suffix('"'))
210            && !contents
211                .bytes()
212                .any(|byte| byte < 0x20 || byte == b'"' || byte == b'\\')
213        {
214            return Ok(Cow::Borrowed(contents));
215        }
216        serde_json::from_str::<String>(text).map(Cow::Owned)
217    } else {
218        Ok(Cow::Borrowed(text))
219    }
220}
221
222fn parse_decimal_str(value: &str) -> Result<Decimal, String> {
223    let parsed = if value.contains('e') || value.contains('E') {
224        if value.len() <= Decimal::MAX_SCALE as usize {
225            Decimal::from_scientific(value)
226        } else {
227            decimal::parse(value).or_else(|_| Decimal::from_scientific(value))
228        }
229    } else {
230        Decimal::from_str(value)
231    };
232
233    match parsed {
234        Ok(decimal) => Ok(decimal),
235        Err(e) => decimal_str_rounded(value).ok_or_else(|| e.to_string()),
236    }
237}
238
239// Fractional digits beyond Decimal's maximum scale are sub-representable; round to the highest
240// scale that fits (venues quoting 18-decimal on-chain units emit such values)
241fn decimal_str_rounded(value: &str) -> Option<Decimal> {
242    for scale in (0..=Decimal::MAX_SCALE as usize).rev() {
243        let clamped = decimal_string_clamped_to_scale(value, scale)?;
244
245        if let Ok(decimal) = Decimal::from_str(&clamped) {
246            return Some(decimal);
247        }
248    }
249
250    None
251}
252
253fn decimal_string_clamped_to_scale(value: &str, max_scale: usize) -> Option<String> {
254    let (coefficient, exponent) = match value.find(['e', 'E']) {
255        Some(index) => {
256            let exponent = value[index + 1..].parse::<i32>().ok()?;
257            (&value[..index], exponent)
258        }
259        None => (value, 0),
260    };
261
262    let (sign, unsigned) = match coefficient.as_bytes().first()? {
263        b'+' => ("", &coefficient[1..]),
264        b'-' => ("-", &coefficient[1..]),
265        _ => ("", coefficient),
266    };
267    let (integer, fractional) = decimal_components(unsigned)?;
268    let digits = format!("{integer}{fractional}");
269    if decimal_digits_are_zero(&digits, "") {
270        return Some("0".to_string());
271    }
272
273    let point = i32::try_from(integer.len()).ok()?.checked_add(exponent)?;
274
275    // Decimal can hold at most 29 significant integer digits, and
276    // `Decimal::from_str` below still rejects values above Decimal::MAX.
277    // Check significant digits before expansion so absurd exponents keep the
278    // original parse error without allocating an absurd string.
279    if decimal_integer_digits_exceed_max(&digits, point) {
280        return None;
281    }
282
283    let (integer, fractional) = if point <= 0 {
284        // Digits past the rounding position are leading zeros, so the cap
285        // cannot change the result; bounds allocation for absurd exponents.
286        let zero_count = usize::try_from(-point).ok()?.min(max_scale + 1);
287        (
288            "0".to_string(),
289            format!("{}{digits}", "0".repeat(zero_count)),
290        )
291    } else {
292        let point = usize::try_from(point).ok()?;
293        if point >= digits.len() {
294            (
295                format!("{}{}", digits, "0".repeat(point - digits.len())),
296                String::new(),
297            )
298        } else {
299            (digits[..point].to_string(), digits[point..].to_string())
300        }
301    };
302
303    let (integer, fractional) = round_decimal_components(integer, fractional, max_scale);
304    let sign = if sign == "-" && decimal_digits_are_zero(&integer, &fractional) {
305        ""
306    } else {
307        sign
308    };
309
310    if fractional.is_empty() {
311        Some(format!("{sign}{integer}"))
312    } else {
313        Some(format!("{sign}{integer}.{fractional}"))
314    }
315}
316
317fn decimal_components(value: &str) -> Option<(&str, &str)> {
318    let mut split = value.split('.');
319    let integer = split.next()?;
320    let fractional = split.next().unwrap_or("");
321    if split.next().is_some()
322        || (integer.is_empty() && fractional.is_empty())
323        || !integer.chars().all(|c| c.is_ascii_digit())
324        || !fractional.chars().all(|c| c.is_ascii_digit())
325    {
326        return None;
327    }
328    Some((integer, fractional))
329}
330
331fn decimal_integer_digits_exceed_max(digits: &str, point: i32) -> bool {
332    const DECIMAL_MAX_INTEGER_DIGITS: i32 = 29;
333
334    if point <= 0 {
335        return false;
336    }
337
338    let Some(first_non_zero) = digits.bytes().position(|digit| digit != b'0') else {
339        return false;
340    };
341    let Ok(first_non_zero) = i32::try_from(first_non_zero) else {
342        return true;
343    };
344
345    point.saturating_sub(first_non_zero) > DECIMAL_MAX_INTEGER_DIGITS
346}
347
348fn round_decimal_components(
349    mut integer: String,
350    fractional: String,
351    max_scale: usize,
352) -> (String, String) {
353    if fractional.len() <= max_scale {
354        return (integer, fractional);
355    }
356
357    let mut rounded = fractional.as_bytes()[..max_scale].to_vec();
358    if fractional.as_bytes()[max_scale] >= b'5' {
359        increment_decimal_digits(&mut integer, &mut rounded);
360    }
361
362    (
363        integer,
364        String::from_utf8(rounded).expect("decimal digits are ASCII"),
365    )
366}
367
368fn increment_decimal_digits(integer: &mut String, fractional: &mut [u8]) {
369    for digit in fractional.iter_mut().rev() {
370        if *digit < b'9' {
371            *digit += 1;
372            return;
373        }
374        *digit = b'0';
375    }
376
377    let mut integer_digits = integer.as_bytes().to_vec();
378    for digit in integer_digits.iter_mut().rev() {
379        if *digit < b'9' {
380            *digit += 1;
381            *integer = String::from_utf8(integer_digits).expect("decimal digits are ASCII");
382            return;
383        }
384        *digit = b'0';
385    }
386    integer_digits.insert(0, b'1');
387    *integer = String::from_utf8(integer_digits).expect("decimal digits are ASCII");
388}
389
390fn decimal_digits_are_zero(integer: &str, fractional: &str) -> bool {
391    integer
392        .bytes()
393        .chain(fractional.bytes())
394        .all(|digit| digit == b'0')
395}
396
397/// Represents types which are serializable for JSON specifications.
398pub trait Serializable: Serialize + for<'de> Deserialize<'de> {
399    /// Deserialize an object from JSON encoded bytes.
400    ///
401    /// # Errors
402    ///
403    /// Returns an error if JSON deserialization fails.
404    fn from_json_bytes(data: &[u8]) -> Result<Self, serde_json::Error> {
405        serde_json::from_slice(data)
406    }
407
408    /// Serialize an object to JSON encoded bytes.
409    ///
410    /// # Errors
411    ///
412    /// Returns an error if JSON serialization fails.
413    fn to_json_bytes(&self) -> Result<Bytes, serde_json::Error> {
414        serde_json::to_vec(self).map(Bytes::from)
415    }
416}
417
418pub use self::msgpack::{FromMsgPack, MsgPackSerializable, ToMsgPack};
419
420/// Provides `MsgPack` serialization support for types implementing [`Serializable`].
421///
422/// This module contains traits for `MsgPack` serialization and deserialization,
423/// separated from the core [`Serializable`] trait to allow independent opt-in.
424pub mod msgpack {
425    use bytes::Bytes;
426    use serde::{Deserialize, Serialize};
427
428    use super::Serializable;
429
430    /// Provides deserialization from `MsgPack` encoded bytes.
431    pub trait FromMsgPack: for<'de> Deserialize<'de> + Sized {
432        /// Deserialize an object from `MsgPack` encoded bytes.
433        ///
434        /// # Errors
435        ///
436        /// Returns an error if MessagePack deserialization fails.
437        fn from_msgpack_bytes(data: &[u8]) -> Result<Self, rmp_serde::decode::Error> {
438            rmp_serde::from_slice(data)
439        }
440    }
441
442    /// Provides serialization to `MsgPack` encoded bytes.
443    pub trait ToMsgPack: Serialize {
444        /// Serialize an object to `MsgPack` encoded bytes.
445        ///
446        /// # Errors
447        ///
448        /// Returns an error if MessagePack serialization fails.
449        fn to_msgpack_bytes(&self) -> Result<Bytes, rmp_serde::encode::Error> {
450            rmp_serde::to_vec_named(self).map(Bytes::from)
451        }
452    }
453
454    /// Marker trait combining [`Serializable`], [`FromMsgPack`], and [`ToMsgPack`].
455    ///
456    /// This trait is automatically implemented for all types that implement [`Serializable`].
457    pub trait MsgPackSerializable: Serializable + FromMsgPack + ToMsgPack {}
458
459    impl<T> FromMsgPack for T where T: Serializable {}
460
461    impl<T> ToMsgPack for T where T: Serializable {}
462
463    impl<T> MsgPackSerializable for T where T: Serializable {}
464}
465
466/// Serde default value function that returns `true`.
467///
468/// Use with `#[serde(default = "default_true")]` on boolean fields.
469#[must_use]
470pub const fn default_true() -> bool {
471    true
472}
473
474/// Serde default value function that returns `false`.
475///
476/// Use with `#[serde(default = "default_false")]` on boolean fields.
477#[must_use]
478pub const fn default_false() -> bool {
479    false
480}
481
482/// Deserializes a `Decimal` from either a JSON string or number.
483///
484/// High-performance implementation using a custom visitor that avoids intermediate
485/// `serde_json::Value` allocations. Handles all JSON numeric representations:
486///
487/// - JSON string: `"123.456"` → Decimal (zero-copy for borrowed strings)
488/// - JSON integer: `123` → Decimal (direct conversion, no string allocation)
489/// - JSON float: `123.456` → Decimal
490/// - JSON null: → `Decimal::ZERO`
491/// - Scientific notation: `"1.5e-8"` → Decimal
492/// - Fractional digits beyond `Decimal`'s maximum scale (28) are rounded
493/// - Scientific strings preserve exactly representable values after exponent normalization
494///
495/// # Performance
496///
497/// This implementation is optimized for high-frequency trading scenarios:
498/// - Zero allocations for exactly representable plain borrowed strings
499/// - Scientific normalization and high-scale rounding may allocate
500/// - Direct integer conversion without string intermediary
501/// - No intermediate `serde_json::Value` heap allocation
502///
503/// # Errors
504///
505/// Returns an error if the value cannot be parsed as a valid decimal.
506pub fn deserialize_decimal<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
507where
508    D: Deserializer<'de>,
509{
510    deserializer.deserialize_any(DecimalVisitor)
511}
512
513/// Deserializes a `Decimal` through `rust_decimal`'s own `Deserialize` implementation, keeping
514/// its rounding and scale.
515///
516/// When `rust_decimal`'s arbitrary-precision integration is disabled, `serde_json`
517/// arbitrary-precision numbers parse from their text as `rust_decimal` parses a string, which
518/// matches the result with that integration enabled. Unlike [`deserialize_decimal`], null and
519/// empty strings are errors.
520///
521/// # Errors
522///
523/// Returns an error if:
524/// - The value is null or an empty string.
525/// - The value is a non-numeric type.
526/// - `rust_decimal` rejects the value.
527pub fn deserialize_decimal_native<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
528where
529    D: Deserializer<'de>,
530{
531    #[derive(Deserialize)]
532    #[serde(untagged)]
533    enum Value {
534        Decimal(Decimal),
535        Number(serde_json::Number),
536    }
537
538    match Value::deserialize(deserializer)? {
539        Value::Decimal(value) => Ok(value),
540
541        // Handle serde_json's numeric map when rust_decimal's AP integration is disabled
542        Value::Number(value) => <Decimal as Deserialize>::deserialize(
543            serde::de::value::StringDeserializer::<D::Error>::new(value.to_string()),
544        ),
545    }
546}
547
548/// Deserializes an `Option<Decimal>` from a JSON string, number, or null.
549///
550/// High-performance implementation using a custom visitor that avoids intermediate
551/// `serde_json::Value` allocations. Handles all JSON numeric representations:
552///
553/// - JSON string: `"123.456"` → Some(Decimal) (zero-copy for borrowed strings)
554/// - JSON integer: `123` → Some(Decimal) (direct conversion)
555/// - JSON float: `123.456` → Some(Decimal)
556/// - JSON null: → `None`
557/// - Empty string: `""` → `None`
558/// - Scientific notation: `"1.5e-8"` → Some(Decimal)
559/// - Fractional digits beyond `Decimal`'s maximum scale (28) are rounded
560/// - Scientific strings preserve exactly representable values after exponent normalization
561///
562/// # Performance
563///
564/// This implementation is optimized for high-frequency trading scenarios:
565/// - Zero allocations for exactly representable plain borrowed strings
566/// - Scientific normalization and high-scale rounding may allocate
567/// - Direct integer conversion without string intermediary
568/// - No intermediate `serde_json::Value` heap allocation
569///
570/// # Errors
571///
572/// Returns an error if the value cannot be parsed as a valid decimal.
573pub fn deserialize_optional_decimal<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
574where
575    D: Deserializer<'de>,
576{
577    // Use deserialize_any to handle all JSON value types uniformly
578    // (deserialize_option would route non-null through visit_some, losing empty string handling)
579    deserializer.deserialize_any(OptionalDecimalVisitor)
580}
581
582/// Deserializes a `Decimal` from the source text of a JSON number or numeric string.
583///
584/// Numeric tokens keep their source digits instead of passing through `f64`. Representable
585/// values, including scientific notation, parse exactly. Values with fractional digits beyond
586/// `Decimal`'s maximum scale (28) round as strings do in [`deserialize_decimal`]. Unlike
587/// [`deserialize_decimal`], null and empty strings are errors.
588///
589/// The raw token is only available from direct JSON input: serde's internally tagged and
590/// untagged enum buffers cannot carry it. A buffered `serde_json::Value` renders its number,
591/// which keeps the source digits only with `serde_json/arbitrary_precision`. For borrowed JSON
592/// text, [`deserialize_decimal_token_borrowed`] reads the same tokens without copying them.
593///
594/// # Errors
595///
596/// Returns an error if the token is null, an empty string, not a decimal, or outside `Decimal`'s
597/// range.
598pub fn deserialize_decimal_token<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
599where
600    D: Deserializer<'de>,
601{
602    deserialize_optional_decimal_token(deserializer)?
603        .ok_or_else(|| D::Error::custom(DECIMAL_TOKEN_MISSING))
604}
605
606/// Deserializes an `Option<Decimal>` from the source text of a JSON number, numeric string, or
607/// null.
608///
609/// Null and empty strings read as `None`; other tokens parse as in
610/// [`deserialize_decimal_token`]. Add `#[serde(default)]` to accept a missing field.
611///
612/// # Errors
613///
614/// Returns an error if a token other than null or an empty string is not a decimal or is outside
615/// `Decimal`'s range.
616pub fn deserialize_optional_decimal_token<'de, D>(
617    deserializer: D,
618) -> Result<Option<Decimal>, D::Error>
619where
620    D: Deserializer<'de>,
621{
622    Option::<Box<RawValue>>::deserialize(deserializer)?
623        .map_or(Ok(None), |raw| parse_decimal_token(&raw))
624}
625
626/// Deserializes a `Decimal` as [`deserialize_decimal_token`] does, borrowing the token from the
627/// input instead of copying it.
628///
629/// Requires a deserializer that borrows from its input, such as `serde_json::from_str` or
630/// `serde_json::from_slice`. Use [`deserialize_decimal_token`] for `serde_json::from_reader` and
631/// `serde_json::Value` input.
632///
633/// # Errors
634///
635/// Returns an error if the deserializer cannot lend the token, or for any token that
636/// [`deserialize_decimal_token`] rejects.
637pub fn deserialize_decimal_token_borrowed<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
638where
639    D: Deserializer<'de>,
640{
641    deserialize_optional_decimal_token_borrowed(deserializer)?
642        .ok_or_else(|| D::Error::custom(DECIMAL_TOKEN_MISSING))
643}
644
645/// Deserializes an `Option<Decimal>` as [`deserialize_optional_decimal_token`] does, borrowing
646/// the token from the input instead of copying it.
647///
648/// Requires borrowed input, as described for [`deserialize_decimal_token_borrowed`].
649///
650/// # Errors
651///
652/// Returns an error if the deserializer cannot lend the token, or for any token that
653/// [`deserialize_optional_decimal_token`] rejects.
654pub fn deserialize_optional_decimal_token_borrowed<'de, D>(
655    deserializer: D,
656) -> Result<Option<Decimal>, D::Error>
657where
658    D: Deserializer<'de>,
659{
660    Option::<&'de RawValue>::deserialize(deserializer)?.map_or(Ok(None), parse_decimal_token)
661}
662
663const DECIMAL_TOKEN_MISSING: &str = "expected a decimal, was null or an empty string";
664
665fn parse_decimal_token<E: Error>(raw: &RawValue) -> Result<Option<Decimal>, E> {
666    let text = json_token_text(raw).map_err(E::custom)?;
667
668    if let Ok(value) = decimal::parse(&text) {
669        return Ok(Some(value));
670    }
671
672    // `Decimal::from_scientific` rounds the significand before applying the exponent
673    if text.contains(['e', 'E'])
674        && let Some(value) = decimal_str_rounded(&text)
675    {
676        return Ok(Some(value));
677    }
678
679    OptionalDecimalVisitor.visit_str(&text)
680}
681
682/// Deserializes a `Decimal` from a JSON string.
683///
684/// This is the strict form that requires the value to be a string, rejecting
685/// numeric JSON values to avoid precision loss.
686///
687/// # Errors
688///
689/// Returns an error if the string cannot be parsed as a valid decimal.
690pub fn deserialize_decimal_from_str<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
691where
692    D: Deserializer<'de>,
693{
694    let s: std::borrow::Cow<'de, str> = Deserialize::deserialize(deserializer)?;
695    Decimal::from_str(s.as_ref()).map_err(D::Error::custom)
696}
697
698/// Deserializes a `Decimal` from a string field that might be empty.
699///
700/// Handles edge cases where empty string "" or "0" becomes `Decimal::ZERO`.
701///
702/// # Errors
703///
704/// Returns an error if the string cannot be parsed as a valid decimal.
705pub fn deserialize_decimal_or_zero<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
706where
707    D: Deserializer<'de>,
708{
709    let s: std::borrow::Cow<'de, str> = Deserialize::deserialize(deserializer)?;
710    if s.is_empty() || s == "0" {
711        Ok(Decimal::ZERO)
712    } else {
713        Decimal::from_str(s.as_ref()).map_err(D::Error::custom)
714    }
715}
716
717/// Deserializes an optional `Decimal` from a string field.
718///
719/// Returns `None` if the string is empty or "0", otherwise parses to `Decimal`.
720/// This is a strict string-only deserializer; for flexible handling of strings,
721/// numbers, and null, use [`deserialize_optional_decimal`].
722///
723/// # Errors
724///
725/// Returns an error if the string cannot be parsed as a valid decimal.
726pub fn deserialize_optional_decimal_str<'de, D>(
727    deserializer: D,
728) -> Result<Option<Decimal>, D::Error>
729where
730    D: Deserializer<'de>,
731{
732    let s: std::borrow::Cow<'de, str> = Deserialize::deserialize(deserializer)?;
733    if s.is_empty() || s == "0" {
734        Ok(None)
735    } else {
736        Decimal::from_str(s.as_ref())
737            .map(Some)
738            .map_err(D::Error::custom)
739    }
740}
741
742/// Deserializes an optional `Decimal` from a string-only field.
743///
744/// Returns `None` if the value is null or the string is empty, otherwise
745/// parses to `Decimal`.
746///
747/// # Errors
748///
749/// Returns an error if the string cannot be parsed as a valid decimal.
750pub fn deserialize_optional_decimal_from_str<'de, D>(
751    deserializer: D,
752) -> Result<Option<Decimal>, D::Error>
753where
754    D: Deserializer<'de>,
755{
756    let opt = Option::<String>::deserialize(deserializer)?;
757    match opt {
758        Some(s) if !s.is_empty() => Decimal::from_str(&s).map(Some).map_err(D::Error::custom),
759        _ => Ok(None),
760    }
761}
762
763/// Deserializes a `Decimal` from an optional string field, defaulting to zero.
764///
765/// Handles edge cases: `None`, empty string "", or "0" all become `Decimal::ZERO`.
766///
767/// # Errors
768///
769/// Returns an error if the string cannot be parsed as a valid decimal.
770pub fn deserialize_optional_decimal_or_zero<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
771where
772    D: Deserializer<'de>,
773{
774    let opt: Option<String> = Deserialize::deserialize(deserializer)?;
775    match opt {
776        None => Ok(Decimal::ZERO),
777        Some(s) if s.is_empty() || s == "0" => Ok(Decimal::ZERO),
778        Some(s) => Decimal::from_str(&s).map_err(D::Error::custom),
779    }
780}
781
782/// Deserializes a `Vec<Decimal>` from a JSON array of strings.
783///
784/// # Errors
785///
786/// Returns an error if any string cannot be parsed as a valid decimal.
787pub fn deserialize_vec_decimal_from_str<'de, D>(deserializer: D) -> Result<Vec<Decimal>, D::Error>
788where
789    D: Deserializer<'de>,
790{
791    let strings = Vec::<String>::deserialize(deserializer)?;
792    strings
793        .into_iter()
794        .map(|s| Decimal::from_str(&s).map_err(D::Error::custom))
795        .collect()
796}
797
798/// Serializes a `Decimal` as a string (lossless, no scientific notation).
799///
800/// # Errors
801///
802/// Returns an error if serialization fails.
803pub fn serialize_decimal_as_str<S>(decimal: &Decimal, serializer: S) -> Result<S::Ok, S::Error>
804where
805    S: Serializer,
806{
807    serializer.serialize_str(&decimal.to_string())
808}
809
810/// Serializes an optional `Decimal` as a string.
811///
812/// # Errors
813///
814/// Returns an error if serialization fails.
815pub fn serialize_optional_decimal_as_str<S>(
816    decimal: &Option<Decimal>,
817    serializer: S,
818) -> Result<S::Ok, S::Error>
819where
820    S: Serializer,
821{
822    match decimal {
823        Some(d) => serializer.serialize_str(&d.to_string()),
824        None => serializer.serialize_none(),
825    }
826}
827
828/// Serializes a `Vec<Decimal>` as an array of strings.
829///
830/// # Errors
831///
832/// Returns an error if serialization fails.
833pub fn serialize_vec_decimal_as_str<S>(
834    decimals: &Vec<Decimal>,
835    serializer: S,
836) -> Result<S::Ok, S::Error>
837where
838    S: Serializer,
839{
840    let mut seq = serializer.serialize_seq(Some(decimals.len()))?;
841    for decimal in decimals {
842        seq.serialize_element(&decimal.to_string())?;
843    }
844    seq.end()
845}
846
847/// Parses a string to `Decimal`, returning an error if parsing fails.
848///
849/// # Errors
850///
851/// Returns an error if the string cannot be parsed as a Decimal.
852pub fn parse_decimal(s: &str) -> anyhow::Result<Decimal> {
853    Decimal::from_str(s).map_err(|e| anyhow::anyhow!("Failed to parse decimal from '{s}': {e}"))
854}
855
856/// Parses an optional string to `Decimal`, returning `None` if the string is `None` or empty.
857///
858/// # Errors
859///
860/// Returns an error if the string cannot be parsed as a Decimal.
861pub fn parse_optional_decimal(s: &Option<String>) -> anyhow::Result<Option<Decimal>> {
862    match s {
863        None => Ok(None),
864        Some(s) if s.is_empty() => Ok(None),
865        Some(s) => parse_decimal(s).map(Some),
866    }
867}
868
869/// Deserializes an empty string into `None`.
870///
871/// Many exchange APIs represent null string fields as an empty string (`""`).
872/// When such a payload is mapped onto `Option<String>` the default behavior
873/// would yield `Some("")`, which is semantically different from the intended
874/// absence of a value. This deserializer normalizes empty strings
875/// to `None` during deserialization.
876///
877/// # Errors
878///
879/// Returns an error if the JSON value cannot be deserialized into a string.
880pub fn deserialize_empty_string_as_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
881where
882    D: Deserializer<'de>,
883{
884    let opt = Option::<String>::deserialize(deserializer)?;
885    Ok(opt.filter(|s| !s.is_empty()))
886}
887
888/// Deserializes an empty [`Ustr`] into `None`.
889///
890/// # Errors
891///
892/// Returns an error if the JSON value cannot be deserialized into a string.
893pub fn deserialize_empty_ustr_as_none<'de, D>(deserializer: D) -> Result<Option<Ustr>, D::Error>
894where
895    D: Deserializer<'de>,
896{
897    let opt = Option::<Ustr>::deserialize(deserializer)?;
898    Ok(opt.filter(|s| !s.is_empty()))
899}
900
901/// Deserializes a `u8` from a string field.
902///
903/// Returns 0 if the string is empty.
904///
905/// # Errors
906///
907/// Returns an error if the string cannot be parsed as a u8.
908pub fn deserialize_string_to_u8<'de, D>(deserializer: D) -> Result<u8, D::Error>
909where
910    D: Deserializer<'de>,
911{
912    let s: std::borrow::Cow<'de, str> = Deserialize::deserialize(deserializer)?;
913    if s.is_empty() {
914        return Ok(0);
915    }
916    s.as_ref().parse::<u8>().map_err(D::Error::custom)
917}
918
919/// Deserializes a `u64` from a string field.
920///
921/// Returns 0 if the string is empty.
922///
923/// # Errors
924///
925/// Returns an error if the string cannot be parsed as a u64.
926pub fn deserialize_string_to_u64<'de, D>(deserializer: D) -> Result<u64, D::Error>
927where
928    D: Deserializer<'de>,
929{
930    let s: std::borrow::Cow<'de, str> = Deserialize::deserialize(deserializer)?;
931    if s.is_empty() {
932        Ok(0)
933    } else {
934        s.as_ref().parse::<u64>().map_err(D::Error::custom)
935    }
936}
937
938/// Deserializes an optional `u64` from a string field.
939///
940/// Returns `None` if the value is null or the string is empty.
941///
942/// # Errors
943///
944/// Returns an error if the string cannot be parsed as a u64.
945pub fn deserialize_optional_string_to_u64<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
946where
947    D: Deserializer<'de>,
948{
949    let s: Option<String> = Option::deserialize(deserializer)?;
950    match s {
951        Some(s) if s.is_empty() => Ok(None),
952        Some(s) => s.parse().map(Some).map_err(D::Error::custom),
953        None => Ok(None),
954    }
955}
956
957#[cfg(test)]
958mod tests {
959    use ahash::AHashSet;
960    use rstest::*;
961    use rust_decimal::Decimal;
962    use rust_decimal_macros::dec;
963    use serde::{
964        Deserialize, Serialize,
965        de::{Visitor, value::Error as ValueError},
966    };
967    use serde_json::json;
968    use ustr::Ustr;
969
970    use super::{
971        DecimalVisitor, OptionalDecimalVisitor, Serializable, decimal, default_false, default_true,
972        deserialize_decimal, deserialize_decimal_from_str, deserialize_decimal_native,
973        deserialize_decimal_or_zero, deserialize_decimal_token, deserialize_decimal_token_borrowed,
974        deserialize_empty_string_as_none, deserialize_empty_ustr_as_none,
975        deserialize_optional_decimal, deserialize_optional_decimal_or_zero,
976        deserialize_optional_decimal_str, deserialize_optional_decimal_token,
977        deserialize_optional_decimal_token_borrowed, deserialize_optional_string_to_u64,
978        deserialize_string_to_u8, deserialize_string_to_u64, deserialize_vec_decimal_from_str,
979        msgpack::{FromMsgPack, ToMsgPack},
980        parse_decimal, parse_optional_decimal, serialize_decimal_as_str,
981        serialize_optional_decimal_as_str, serialize_vec_decimal_as_str, sorted_hashset,
982    };
983
984    #[derive(Debug, PartialEq, Serialize, Deserialize)]
985    struct SortedSetPayload {
986        #[serde(with = "sorted_hashset")]
987        values: AHashSet<i32>,
988    }
989
990    #[derive(Serialize, Deserialize, PartialEq, Debug)]
991    struct SerializableTestStruct {
992        id: u32,
993        name: String,
994        value: f64,
995    }
996
997    impl Serializable for SerializableTestStruct {}
998
999    #[rstest]
1000    #[case("\"")]
1001    #[case("\"1x")]
1002    #[case("\"1\u{e9}")]
1003    #[case("\"1\0")]
1004    #[case("\"1\n")]
1005    #[case("\"1\"\"")]
1006    #[case("\"1\0\"")]
1007    #[case("\"1\n\"")]
1008    fn test_json_decimal_rejects_malformed_raw_strings(#[case] raw: &str) {
1009        // A generic deserializer can supply RawValue text without serde_json's lexical validation.
1010        let expected = serde_json::from_str::<String>(raw).unwrap_err().to_string();
1011        let deserializer = serde::de::value::MapDeserializer::<_, ValueError>::new(
1012            [("$serde_json::private::RawValue", raw)].into_iter(),
1013        );
1014        let error = decimal::deserialize_json(deserializer).unwrap_err();
1015        assert_eq!(error.to_string(), expected);
1016    }
1017
1018    #[rstest]
1019    #[case("0.125", "0.125", 3)]
1020    #[case("9007199254740993", "9007199254740993", 0)]
1021    #[case("\"1.2500\"", "1.2500", 4)]
1022    #[case(
1023        "\"0.12345678901234567890123456789\"",
1024        "0.1234567890123456789012345679",
1025        28
1026    )]
1027    fn test_native_decimal_preserves_rounding_and_scale(
1028        #[case] token: &str,
1029        #[case] expected: &str,
1030        #[case] scale: u32,
1031    ) {
1032        let direct =
1033            deserialize_decimal_native(&mut serde_json::Deserializer::from_str(token)).unwrap();
1034        let buffered =
1035            deserialize_decimal_native(serde_json::from_str::<serde_json::Value>(token).unwrap())
1036                .unwrap();
1037        let expected = Decimal::from_str_exact(expected).unwrap();
1038
1039        assert_eq!(direct, expected);
1040        assert_eq!(direct.scale(), scale);
1041        assert_eq!(buffered, expected);
1042        assert_eq!(buffered.scale(), scale);
1043    }
1044
1045    #[rstest]
1046    #[case("null")]
1047    #[case("true")]
1048    #[case("\"\"")]
1049    #[case("\"NaN\"")]
1050    #[case("\"79228162514264337593543950336\"")]
1051    fn test_native_decimal_rejects_invalid_values(#[case] token: &str) {
1052        let direct = deserialize_decimal_native(&mut serde_json::Deserializer::from_str(token));
1053        let buffered =
1054            deserialize_decimal_native(serde_json::from_str::<serde_json::Value>(token).unwrap());
1055
1056        assert!(direct.is_err());
1057        assert!(buffered.is_err());
1058    }
1059
1060    #[rstest]
1061    #[case("1.25")]
1062    #[case("2.0")]
1063    #[case("1.2500")]
1064    #[case("-0.0")]
1065    #[case("1e-7")]
1066    #[case("1.5E3")]
1067    #[case("1e-30")]
1068    #[case("1e30")]
1069    #[case("13.223699999999997")]
1070    #[case("9007199254740993")]
1071    #[case("18446744073709551615")]
1072    #[case("-9223372036854775808")]
1073    #[case("\"1.2500\"")]
1074    #[case("\"1e5\"")]
1075    #[case("\"0.12345678901234567890123456789\"")]
1076    #[case("\"79228162514264337593543950336\"")]
1077    #[case("\"not-a-number\"")]
1078    fn test_native_decimal_matches_rust_decimal(#[case] token: &str) {
1079        let value = serde_json::from_str::<serde_json::Value>(token).unwrap();
1080
1081        // Where rust_decimal rejects an arbitrary-precision number map, the token digits must
1082        // parse as rust_decimal parses them when its own AP integration is enabled.
1083        let fallback = (is_arbitrary_precision() && value.is_number())
1084            .then(|| serde_json::from_value::<Decimal>(serde_json::json!(token)).ok())
1085            .flatten();
1086        let routes = [
1087            (
1088                serde_json::from_str::<Decimal>(token).ok(),
1089                deserialize_decimal_native(&mut serde_json::Deserializer::from_str(token)).ok(),
1090            ),
1091            (
1092                serde_json::from_value::<Decimal>(value.clone()).ok(),
1093                deserialize_decimal_native(value).ok(),
1094            ),
1095        ];
1096
1097        for (native, consolidated) in routes {
1098            let expected = native.or(fallback);
1099            assert_eq!(
1100                consolidated.map(|value| (value, value.scale())),
1101                expected.map(|value| (value, value.scale()))
1102            );
1103        }
1104    }
1105
1106    #[rstest]
1107    #[case("10.0", Decimal::from(10), 0, 1, 0)]
1108    #[case("1.2500", Decimal::new(125, 2), 2, 4, 4)]
1109    #[case("1.25e1", Decimal::new(125, 1), 1, 1, 1)]
1110    fn test_numeric_decimal_preserves_route_scale(
1111        #[case] token: &str,
1112        #[case] expected: Decimal,
1113        #[case] float_scale: u32,
1114        #[case] token_scale: u32,
1115        #[case] buffered_token_scale: u32,
1116    ) {
1117        let arbitrary_precision = is_arbitrary_precision();
1118
1119        let scale = if arbitrary_precision {
1120            token_scale
1121        } else {
1122            float_scale
1123        };
1124
1125        let buffered_scale = if arbitrary_precision {
1126            buffered_token_scale
1127        } else {
1128            float_scale
1129        };
1130
1131        let direct = deserialize_decimal(&mut serde_json::Deserializer::from_str(token)).unwrap();
1132        let buffered =
1133            deserialize_decimal(serde_json::from_str::<serde_json::Value>(token).unwrap()).unwrap();
1134        let quoted = deserialize_decimal(serde_json::Value::String("1.2500".to_string())).unwrap();
1135        assert_eq!(direct, expected);
1136        assert_eq!(direct.scale(), scale);
1137        assert_eq!(buffered, expected);
1138        assert_eq!(buffered.scale(), buffered_scale);
1139        assert_eq!(quoted, Decimal::new(12500, 4));
1140        assert_eq!(quoted.scale(), 4);
1141    }
1142
1143    fn is_arbitrary_precision() -> bool {
1144        serde_json::from_str::<serde_json::Number>("1.2500")
1145            .unwrap()
1146            .to_string()
1147            == "1.2500"
1148    }
1149
1150    #[rstest]
1151    fn test_sorted_hashset_serialization_is_deterministic() {
1152        let payload = SortedSetPayload {
1153            values: [29, 3, 17, 5, 23, 11].into_iter().collect(),
1154        };
1155
1156        let encoded = serde_json::to_string(&payload).unwrap();
1157        let expected = serde_json::to_string(&json!({ "values": [3, 5, 11, 17, 23, 29] })).unwrap();
1158        let restored: SortedSetPayload = serde_json::from_str(&encoded).unwrap();
1159
1160        assert_eq!(encoded, expected);
1161        assert_eq!(restored, payload);
1162    }
1163
1164    #[rstest]
1165    fn test_serializable_json_roundtrip() {
1166        let original = SerializableTestStruct {
1167            id: 42,
1168            name: "test".to_string(),
1169            value: std::f64::consts::PI,
1170        };
1171
1172        let json_bytes = original.to_json_bytes().unwrap();
1173        let deserialized = SerializableTestStruct::from_json_bytes(&json_bytes).unwrap();
1174
1175        assert_eq!(original, deserialized);
1176    }
1177
1178    #[rstest]
1179    fn test_serializable_msgpack_roundtrip() {
1180        let original = SerializableTestStruct {
1181            id: 123,
1182            name: "msgpack_test".to_string(),
1183            value: std::f64::consts::E,
1184        };
1185
1186        let msgpack_bytes = original.to_msgpack_bytes().unwrap();
1187        let deserialized = SerializableTestStruct::from_msgpack_bytes(&msgpack_bytes).unwrap();
1188
1189        assert_eq!(original, deserialized);
1190    }
1191
1192    #[rstest]
1193    fn test_serializable_json_invalid_data() {
1194        let invalid_json = b"invalid json data";
1195        let result = SerializableTestStruct::from_json_bytes(invalid_json);
1196        assert!(result.is_err());
1197    }
1198
1199    #[rstest]
1200    fn test_serializable_msgpack_invalid_data() {
1201        let invalid_msgpack = b"invalid msgpack data";
1202        let result = SerializableTestStruct::from_msgpack_bytes(invalid_msgpack);
1203        assert!(result.is_err());
1204    }
1205
1206    #[rstest]
1207    fn test_serializable_json_empty_values() {
1208        let test_struct = SerializableTestStruct {
1209            id: 0,
1210            name: String::new(),
1211            value: 0.0,
1212        };
1213
1214        let json_bytes = test_struct.to_json_bytes().unwrap();
1215        let deserialized = SerializableTestStruct::from_json_bytes(&json_bytes).unwrap();
1216
1217        assert_eq!(test_struct, deserialized);
1218    }
1219
1220    #[rstest]
1221    fn test_serializable_msgpack_empty_values() {
1222        let test_struct = SerializableTestStruct {
1223            id: 0,
1224            name: String::new(),
1225            value: 0.0,
1226        };
1227
1228        let msgpack_bytes = test_struct.to_msgpack_bytes().unwrap();
1229        let deserialized = SerializableTestStruct::from_msgpack_bytes(&msgpack_bytes).unwrap();
1230
1231        assert_eq!(test_struct, deserialized);
1232    }
1233
1234    #[derive(Deserialize)]
1235    struct TestOptionalDecimalStr {
1236        #[serde(deserialize_with = "deserialize_optional_decimal_str")]
1237        value: Option<Decimal>,
1238    }
1239
1240    #[derive(Deserialize)]
1241    struct TestDecimalOrZero {
1242        #[serde(deserialize_with = "deserialize_decimal_or_zero")]
1243        value: Decimal,
1244    }
1245
1246    #[derive(Deserialize)]
1247    struct TestOptionalDecimalOrZero {
1248        #[serde(deserialize_with = "deserialize_optional_decimal_or_zero")]
1249        value: Decimal,
1250    }
1251
1252    #[derive(Serialize, Deserialize, PartialEq, Debug)]
1253    struct TestDecimalRoundtrip {
1254        #[serde(
1255            serialize_with = "serialize_decimal_as_str",
1256            deserialize_with = "deserialize_decimal_from_str"
1257        )]
1258        value: Decimal,
1259        #[serde(
1260            serialize_with = "serialize_optional_decimal_as_str",
1261            deserialize_with = "super::deserialize_optional_decimal_from_str"
1262        )]
1263        optional_value: Option<Decimal>,
1264    }
1265
1266    #[rstest]
1267    #[case(r#"{"value":"123.45"}"#, Some(dec!(123.45)))]
1268    #[case(r#"{"value":"0"}"#, None)]
1269    #[case(r#"{"value":""}"#, None)]
1270    fn test_deserialize_optional_decimal_str(
1271        #[case] json: &str,
1272        #[case] expected: Option<Decimal>,
1273    ) {
1274        let result: TestOptionalDecimalStr = serde_json::from_str(json).unwrap();
1275        assert_eq!(result.value, expected);
1276    }
1277
1278    #[rstest]
1279    fn test_deserialize_optional_decimal_from_str_empty_is_none() {
1280        let json = r#"{"value": "1.5", "optional_value": ""}"#;
1281        let result: TestDecimalRoundtrip = serde_json::from_str(json).unwrap();
1282        assert_eq!(result.optional_value, None);
1283    }
1284
1285    #[rstest]
1286    #[case(r#"{"value":"123.45"}"#, dec!(123.45))]
1287    #[case(r#"{"value":"0"}"#, Decimal::ZERO)]
1288    #[case(r#"{"value":""}"#, Decimal::ZERO)]
1289    fn test_deserialize_decimal_or_zero(#[case] json: &str, #[case] expected: Decimal) {
1290        let result: TestDecimalOrZero = serde_json::from_str(json).unwrap();
1291        assert_eq!(result.value, expected);
1292    }
1293
1294    #[rstest]
1295    #[case(r#"{"value":"123.45"}"#, dec!(123.45))]
1296    #[case(r#"{"value":"0"}"#, Decimal::ZERO)]
1297    #[case(r#"{"value":null}"#, Decimal::ZERO)]
1298    #[case(r#"{"value":""}"#, Decimal::ZERO)]
1299    fn test_deserialize_optional_decimal_or_zero(#[case] json: &str, #[case] expected: Decimal) {
1300        let result: TestOptionalDecimalOrZero = serde_json::from_str(json).unwrap();
1301        assert_eq!(result.value, expected);
1302    }
1303
1304    #[rstest]
1305    fn test_decimal_serialization_roundtrip() {
1306        let original = TestDecimalRoundtrip {
1307            value: dec!(123.456789012345678),
1308            optional_value: Some(dec!(0.000000001)),
1309        };
1310
1311        let json = serde_json::to_string(&original).unwrap();
1312
1313        // Check that it's serialized as strings
1314        assert!(json.contains("\"123.456789012345678\""));
1315        assert!(json.contains("\"0.000000001\""));
1316
1317        let deserialized: TestDecimalRoundtrip = serde_json::from_str(&json).unwrap();
1318        assert_eq!(original.value, deserialized.value);
1319        assert_eq!(original.optional_value, deserialized.optional_value);
1320    }
1321
1322    #[rstest]
1323    fn test_decimal_optional_none_handling() {
1324        let test_struct = TestDecimalRoundtrip {
1325            value: dec!(42.0),
1326            optional_value: None,
1327        };
1328
1329        let json = serde_json::to_string(&test_struct).unwrap();
1330        assert!(json.contains("null"));
1331
1332        let parsed: TestDecimalRoundtrip = serde_json::from_str(&json).unwrap();
1333        assert_eq!(test_struct.value, parsed.value);
1334        assert_eq!(None, parsed.optional_value);
1335    }
1336
1337    #[derive(Deserialize)]
1338    struct TestEmptyStringAsNone {
1339        #[serde(deserialize_with = "deserialize_empty_string_as_none")]
1340        value: Option<String>,
1341    }
1342
1343    #[rstest]
1344    #[case(r#"{"value":"hello"}"#, Some("hello".to_string()))]
1345    #[case(r#"{"value":""}"#, None)]
1346    #[case(r#"{"value":null}"#, None)]
1347    fn test_deserialize_empty_string_as_none(#[case] json: &str, #[case] expected: Option<String>) {
1348        let result: TestEmptyStringAsNone = serde_json::from_str(json).unwrap();
1349        assert_eq!(result.value, expected);
1350    }
1351
1352    #[derive(Deserialize)]
1353    struct TestEmptyUstrAsNone {
1354        #[serde(deserialize_with = "deserialize_empty_ustr_as_none")]
1355        value: Option<Ustr>,
1356    }
1357
1358    #[rstest]
1359    #[case(r#"{"value":"hello"}"#, Some(Ustr::from("hello")))]
1360    #[case(r#"{"value":""}"#, None)]
1361    #[case(r#"{"value":null}"#, None)]
1362    fn test_deserialize_empty_ustr_as_none(#[case] json: &str, #[case] expected: Option<Ustr>) {
1363        let result: TestEmptyUstrAsNone = serde_json::from_str(json).unwrap();
1364        assert_eq!(result.value, expected);
1365    }
1366
1367    #[derive(Serialize, Deserialize, PartialEq, Debug)]
1368    struct TestVecDecimal {
1369        #[serde(
1370            serialize_with = "serialize_vec_decimal_as_str",
1371            deserialize_with = "deserialize_vec_decimal_from_str"
1372        )]
1373        values: Vec<Decimal>,
1374    }
1375
1376    #[rstest]
1377    fn test_vec_decimal_roundtrip() {
1378        let original = TestVecDecimal {
1379            values: vec![dec!(1.5), dec!(2.25), dec!(100.001)],
1380        };
1381
1382        let json = serde_json::to_string(&original).unwrap();
1383        assert!(json.contains("[\"1.5\",\"2.25\",\"100.001\"]"));
1384
1385        let parsed: TestVecDecimal = serde_json::from_str(&json).unwrap();
1386        assert_eq!(original.values, parsed.values);
1387    }
1388
1389    #[rstest]
1390    fn test_vec_decimal_empty() {
1391        let original = TestVecDecimal { values: vec![] };
1392
1393        let json = serde_json::to_string(&original).unwrap();
1394        let parsed: TestVecDecimal = serde_json::from_str(&json).unwrap();
1395        assert_eq!(original.values, parsed.values);
1396    }
1397
1398    #[derive(Deserialize)]
1399    struct TestStringToU8 {
1400        #[serde(deserialize_with = "deserialize_string_to_u8")]
1401        value: u8,
1402    }
1403
1404    #[rstest]
1405    #[case(r#"{"value":"42"}"#, 42)]
1406    #[case(r#"{"value":"0"}"#, 0)]
1407    #[case(r#"{"value":"255"}"#, 255)]
1408    #[case(r#"{"value":""}"#, 0)]
1409    fn test_deserialize_string_to_u8(#[case] json: &str, #[case] expected: u8) {
1410        let result: TestStringToU8 = serde_json::from_str(json).unwrap();
1411        assert_eq!(result.value, expected);
1412    }
1413
1414    #[rstest]
1415    #[case(r#"{"value":"256"}"#)]
1416    #[case(r#"{"value":"999"}"#)]
1417    #[case(r#"{"value":"abc"}"#)]
1418    fn test_deserialize_string_to_u8_invalid(#[case] json: &str) {
1419        let result: Result<TestStringToU8, _> = serde_json::from_str(json);
1420        assert!(result.is_err());
1421    }
1422
1423    #[derive(Deserialize)]
1424    struct TestStringToU64 {
1425        #[serde(deserialize_with = "deserialize_string_to_u64")]
1426        value: u64,
1427    }
1428
1429    #[rstest]
1430    #[case(r#"{"value":"12345678901234"}"#, 12_345_678_901_234)]
1431    #[case(r#"{"value":"0"}"#, 0)]
1432    #[case(r#"{"value":"18446744073709551615"}"#, u64::MAX)]
1433    #[case(r#"{"value":""}"#, 0)]
1434    fn test_deserialize_string_to_u64(#[case] json: &str, #[case] expected: u64) {
1435        let result: TestStringToU64 = serde_json::from_str(json).unwrap();
1436        assert_eq!(result.value, expected);
1437    }
1438
1439    #[rstest]
1440    #[case(r#"{"value":"18446744073709551616"}"#)]
1441    #[case(r#"{"value":"abc"}"#)]
1442    #[case(r#"{"value":"-1"}"#)]
1443    fn test_deserialize_string_to_u64_invalid(#[case] json: &str) {
1444        let result: Result<TestStringToU64, _> = serde_json::from_str(json);
1445        assert!(result.is_err());
1446    }
1447
1448    #[derive(Deserialize)]
1449    struct TestOptionalStringToU64 {
1450        #[serde(deserialize_with = "deserialize_optional_string_to_u64")]
1451        value: Option<u64>,
1452    }
1453
1454    #[rstest]
1455    #[case(r#"{"value":"12345678901234"}"#, Some(12_345_678_901_234))]
1456    #[case(r#"{"value":"0"}"#, Some(0))]
1457    #[case(r#"{"value":""}"#, None)]
1458    #[case(r#"{"value":null}"#, None)]
1459    fn test_deserialize_optional_string_to_u64(#[case] json: &str, #[case] expected: Option<u64>) {
1460        let result: TestOptionalStringToU64 = serde_json::from_str(json).unwrap();
1461        assert_eq!(result.value, expected);
1462    }
1463
1464    #[rstest]
1465    #[case("123.45", dec!(123.45))]
1466    #[case("0", Decimal::ZERO)]
1467    #[case("0.0", Decimal::ZERO)]
1468    fn test_parse_decimal(#[case] input: &str, #[case] expected: Decimal) {
1469        let result = parse_decimal(input).unwrap();
1470        assert_eq!(result, expected);
1471    }
1472
1473    #[rstest]
1474    fn test_parse_decimal_invalid() {
1475        assert!(parse_decimal("invalid").is_err());
1476        assert!(parse_decimal("").is_err());
1477    }
1478
1479    #[rstest]
1480    #[case(&Some("123.45".to_string()), Some(dec!(123.45)))]
1481    #[case(&Some("0".to_string()), Some(Decimal::ZERO))]
1482    #[case(&Some(String::new()), None)]
1483    #[case(&None, None)]
1484    fn test_parse_optional_decimal(
1485        #[case] input: &Option<String>,
1486        #[case] expected: Option<Decimal>,
1487    ) {
1488        let result = parse_optional_decimal(input).unwrap();
1489        assert_eq!(result, expected);
1490    }
1491
1492    // Tests for flexible decimal deserializers (handles both string and number JSON values)
1493
1494    #[derive(Debug, Serialize, Deserialize, PartialEq)]
1495    struct TestFlexibleDecimal {
1496        #[serde(
1497            serialize_with = "decimal::serialize",
1498            deserialize_with = "deserialize_decimal"
1499        )]
1500        value: Decimal,
1501        #[serde(
1502            serialize_with = "decimal::serialize_optional",
1503            deserialize_with = "deserialize_optional_decimal"
1504        )]
1505        optional_value: Option<Decimal>,
1506    }
1507
1508    #[rstest]
1509    #[case(r#"{"value": 123.456, "optional_value": 789.012}"#, dec!(123.456), Some(dec!(789.012)))]
1510    #[case(r#"{"value": "123.456", "optional_value": "789.012"}"#, dec!(123.456), Some(dec!(789.012)))]
1511    #[case(r#"{"value": 1.25e-3, "optional_value": 2.5e2}"#, dec!(0.00125), Some(dec!(250)))]
1512    #[case(r#"{"value": 100, "optional_value": null}"#, dec!(100), None)]
1513    #[case(r#"{"value": null, "optional_value": null}"#, Decimal::ZERO, None)]
1514    fn test_deserialize_flexible_decimal(
1515        #[case] json: &str,
1516        #[case] expected_value: Decimal,
1517        #[case] expected_optional: Option<Decimal>,
1518    ) {
1519        let result: TestFlexibleDecimal = serde_json::from_str(json).unwrap();
1520        assert_eq!(result.value, expected_value);
1521        assert_eq!(result.optional_value, expected_optional);
1522    }
1523
1524    #[rstest]
1525    fn test_flexible_decimal_roundtrip() {
1526        let original = TestFlexibleDecimal {
1527            value: dec!(123.456),
1528            optional_value: Some(dec!(789.012)),
1529        };
1530
1531        let json = serde_json::to_string(&original).unwrap();
1532        let deserialized: TestFlexibleDecimal = serde_json::from_str(&json).unwrap();
1533
1534        assert_eq!(original.value, deserialized.value);
1535        assert_eq!(original.optional_value, deserialized.optional_value);
1536    }
1537
1538    #[rstest]
1539    fn test_flexible_decimal_scientific_notation() {
1540        // Test that scientific notation from serde_json is handled correctly.
1541        // serde_json outputs very small numbers like 0.00000001 as "1e-8".
1542        // Note: JSON numbers are parsed as f64, so values are limited to ~15 significant digits.
1543        let json = r#"{"value": 0.00000001, "optional_value": 12345678.12345}"#;
1544        let parsed: TestFlexibleDecimal = serde_json::from_str(json).unwrap();
1545        assert_eq!(parsed.value, dec!(0.00000001));
1546        assert_eq!(parsed.optional_value, Some(dec!(12345678.12345)));
1547    }
1548
1549    #[rstest]
1550    fn test_flexible_decimal_empty_string_optional() {
1551        let json = r#"{"value": 100, "optional_value": ""}"#;
1552        let parsed: TestFlexibleDecimal = serde_json::from_str(json).unwrap();
1553        assert_eq!(parsed.value, dec!(100));
1554        assert_eq!(parsed.optional_value, None);
1555    }
1556
1557    #[derive(Debug, Deserialize)]
1558    struct TestDecimalToken {
1559        #[serde(deserialize_with = "deserialize_decimal_token")]
1560        value: Decimal,
1561        #[serde(default, deserialize_with = "deserialize_optional_decimal_token")]
1562        optional_value: Option<Decimal>,
1563    }
1564
1565    #[derive(Debug, Deserialize)]
1566    struct TestDecimalTokenBorrowed {
1567        #[serde(deserialize_with = "deserialize_decimal_token_borrowed")]
1568        value: Decimal,
1569        #[serde(
1570            default,
1571            deserialize_with = "deserialize_optional_decimal_token_borrowed"
1572        )]
1573        optional_value: Option<Decimal>,
1574    }
1575
1576    #[rstest]
1577    #[case(r#"{"value": 100000000.123456789}"#, "100000000.123456789", None)]
1578    #[case(
1579        r#"{"value": 100000000.123456788, "optional_value": null}"#,
1580        "100000000.123456788",
1581        None
1582    )]
1583    #[case(r#"{"value": "0.10", "optional_value": ""}"#, "0.10", None)]
1584    #[case(
1585        r#"{"value": 18446744073709551617, "optional_value": 0.30000000000000004}"#,
1586        "18446744073709551617",
1587        Some("0.30000000000000004")
1588    )]
1589    #[case(
1590        r#"{"value": 1.1403e-4, "optional_value": "2.5e2"}"#,
1591        "0.00011403",
1592        Some("250")
1593    )]
1594    #[case(
1595        r#"{"value": 5.551115123125783e-17}"#,
1596        "0.0000000000000000555111512313",
1597        None
1598    )]
1599    #[case(r#"{"value": 0.00000000000000000000000000001e28}"#, "0.1", None)]
1600    #[case(
1601        r#"{"value": 0.000000000000000000000000000012345678901234567890123456789e28}"#,
1602        "0.1234567890123456789012345679",
1603        None
1604    )]
1605    #[case(
1606        r#"{"value": 0.12345678901234567890123456789}"#,
1607        "0.1234567890123456789012345679",
1608        None
1609    )]
1610    #[case(
1611        r#"{"value": "-7.50", "optional_value": -0.001}"#,
1612        "-7.50",
1613        Some("-0.001")
1614    )]
1615    fn test_deserialize_decimal_token(
1616        #[case] json: &str,
1617        #[case] expected_value: &str,
1618        #[case] expected_optional: Option<&str>,
1619    ) {
1620        let result: TestDecimalToken = serde_json::from_str(json).unwrap();
1621        let borrowed: TestDecimalTokenBorrowed = serde_json::from_str(json).unwrap();
1622
1623        assert_eq!(result.value.to_string(), expected_value);
1624        assert_eq!(
1625            result
1626                .optional_value
1627                .map(|value| value.to_string())
1628                .as_deref(),
1629            expected_optional
1630        );
1631        assert_eq!(borrowed.value.to_string(), expected_value);
1632        assert_eq!(
1633            borrowed
1634                .optional_value
1635                .map(|value| value.to_string())
1636                .as_deref(),
1637            expected_optional
1638        );
1639    }
1640
1641    #[rstest]
1642    fn test_deserialize_decimal_token_keeps_decimals_that_collapse_in_f64() {
1643        let first: TestDecimalToken =
1644            serde_json::from_str(r#"{"value": 100000000.123456789}"#).unwrap();
1645        let second: TestDecimalToken =
1646            serde_json::from_str(r#"{"value": 100000000.123456788}"#).unwrap();
1647        let first_f64 = "100000000.123456789".parse::<f64>().unwrap();
1648        let second_f64 = "100000000.123456788".parse::<f64>().unwrap();
1649
1650        assert_eq!(first_f64.to_bits(), second_f64.to_bits());
1651        assert_eq!(first.value, dec!(100000000.123456789));
1652        assert_eq!(second.value, dec!(100000000.123456788));
1653    }
1654
1655    #[rstest]
1656    #[case(r#"{"value": null}"#)]
1657    #[case(r#"{"value": ""}"#)]
1658    #[case(r#"{"value": "abc"}"#)]
1659    #[case(r#"{"value": true}"#)]
1660    #[case(r#"{"value": {}}"#)]
1661    #[case(r#"{"value": [1]}"#)]
1662    #[case(r#"{"value": 1e400}"#)]
1663    #[case(r#"{"value": 79228162514264337593543950336}"#)]
1664    #[case(r#"{"value": 1, "optional_value": "abc"}"#)]
1665    #[case(r#"{"optional_value": 1}"#)]
1666    fn test_deserialize_decimal_token_rejects_invalid(#[case] json: &str) {
1667        assert!(serde_json::from_str::<TestDecimalToken>(json).is_err());
1668        assert!(serde_json::from_str::<TestDecimalTokenBorrowed>(json).is_err());
1669    }
1670
1671    #[rstest]
1672    fn test_deserialize_decimal_token_borrowed_requires_borrowed_input() {
1673        let json = r#"{"value": 1.25}"#;
1674        let value: serde_json::Value = serde_json::from_str(json).unwrap();
1675
1676        let from_slice: TestDecimalTokenBorrowed = serde_json::from_slice(json.as_bytes()).unwrap();
1677        let from_reader =
1678            serde_json::from_reader::<_, TestDecimalTokenBorrowed>(json.as_bytes()).unwrap_err();
1679        let from_value = serde_json::from_value::<TestDecimalTokenBorrowed>(value).unwrap_err();
1680
1681        assert_eq!(from_slice.value, dec!(1.25));
1682        assert_eq!(
1683            from_reader.to_string(),
1684            "invalid type: string \"1.25\", expected raw value at line 1 column 15"
1685        );
1686        assert_eq!(
1687            from_value.to_string(),
1688            "invalid type: string \"1.25\", expected raw value"
1689        );
1690    }
1691
1692    #[rstest]
1693    fn test_deserialize_decimal_token_buffered_value_renders_number() {
1694        let value = json!({"value": 65000.5, "optional_value": "0.10"});
1695
1696        let result: TestDecimalToken = serde_json::from_value(value).unwrap();
1697
1698        assert_eq!(result.value.to_string(), "65000.5");
1699        assert_eq!(
1700            result
1701                .optional_value
1702                .map(|value| value.to_string())
1703                .as_deref(),
1704            Some("0.10")
1705        );
1706    }
1707
1708    #[rstest]
1709    fn test_deserialize_decimal_token_rejects_tagged_enum_buffer() {
1710        #[derive(Debug, Deserialize)]
1711        #[serde(tag = "type")]
1712        #[allow(dead_code, reason = "the test only checks that decoding fails")]
1713        enum Tagged {
1714            Token(TestDecimalToken),
1715        }
1716
1717        let result = serde_json::from_str::<Tagged>(r#"{"type": "Token", "value": 1.5}"#);
1718
1719        assert!(result.is_err());
1720    }
1721
1722    // Additional tests for DecimalVisitor edge cases
1723
1724    #[derive(Debug, Deserialize)]
1725    struct TestDecimalOnly {
1726        #[serde(deserialize_with = "deserialize_decimal")]
1727        value: Decimal,
1728    }
1729
1730    #[rstest]
1731    #[case("0.12345678901234567890123456789e1", dec!(1.2345678901234567890123456789))]
1732    #[case("-0.12345678901234567890123456789E1", dec!(-1.2345678901234567890123456789))]
1733    #[case("0.00000000000000000000000000001e1", dec!(0.0000000000000000000000000001))]
1734    fn test_deserialize_decimal_scientific_representable(
1735        #[case] value: &str,
1736        #[case] expected: Decimal,
1737        #[values(false, true)] optional: bool,
1738    ) {
1739        let json = serde_json::to_string(&json!({"value": value})).unwrap();
1740
1741        let actual = if optional {
1742            serde_json::from_str::<TestOptionalDecimalOnly>(&json)
1743                .unwrap()
1744                .value
1745        } else {
1746            Some(
1747                serde_json::from_str::<TestDecimalOnly>(&json)
1748                    .unwrap()
1749                    .value,
1750            )
1751        };
1752
1753        assert_eq!(actual, Some(expected));
1754    }
1755
1756    #[rstest]
1757    #[case(23, 28, 23)]
1758    #[case(24, 29, 0)]
1759    fn test_deserialize_decimal_scientific_length_boundary(
1760        #[case] zeros: usize,
1761        #[case] length: usize,
1762        #[case] scale: u32,
1763    ) {
1764        let value = format!("1.5{}e1", "0".repeat(zeros));
1765        let json = serde_json::to_string(&json!({"value": value})).unwrap();
1766        let required: TestDecimalOnly = serde_json::from_str(&json).unwrap();
1767        let optional: TestOptionalDecimalOnly = serde_json::from_str(&json).unwrap();
1768
1769        assert_eq!(value.len(), length);
1770        assert_eq!(required.value, dec!(15));
1771        assert_eq!(required.value.scale(), scale);
1772        assert_eq!(optional.value, Some(dec!(15)));
1773        assert_eq!(optional.value.unwrap().scale(), scale);
1774    }
1775
1776    #[rstest]
1777    #[case(r#"{"value": "1.5e-8"}"#, dec!(0.000000015))]
1778    #[case(r#"{"value": "1E10"}"#, dec!(10000000000))]
1779    #[case(r#"{"value": "-1.23e5"}"#, dec!(-123000))]
1780    fn test_deserialize_decimal_scientific_string(#[case] json: &str, #[case] expected: Decimal) {
1781        let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1782        assert_eq!(result.value, expected);
1783    }
1784
1785    #[rstest]
1786    #[case(r#"{"value": 9223372036854775807}"#, dec!(9223372036854775807))] // i64::MAX
1787    #[case(r#"{"value": -9223372036854775808}"#, dec!(-9223372036854775808))] // i64::MIN
1788    #[case(r#"{"value": 0}"#, Decimal::ZERO)]
1789    fn test_deserialize_decimal_large_integers(#[case] json: &str, #[case] expected: Decimal) {
1790        let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1791        assert_eq!(result.value, expected);
1792    }
1793
1794    #[rstest]
1795    #[case(r#"{"value": "-123.456789"}"#, dec!(-123.456789))]
1796    #[case(r#"{"value": -999.99}"#, dec!(-999.99))]
1797    fn test_deserialize_decimal_negative(#[case] json: &str, #[case] expected: Decimal) {
1798        let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1799        assert_eq!(result.value, expected);
1800    }
1801
1802    #[rstest]
1803    #[case(r#"{"value": "123456789.123456789012345678"}"#)] // High precision string
1804    fn test_deserialize_decimal_high_precision(#[case] json: &str) {
1805        let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1806        assert_eq!(result.value, dec!(123456789.123456789012345678));
1807    }
1808
1809    #[rstest]
1810    #[case(
1811        r#"{"value": "1.234567890123456789012345678912345e-1"}"#,
1812        "0.1234567890123456789012345679"
1813    )]
1814    #[case(
1815        r#"{"value": "0.1234567890123456789012345678912345"}"#,
1816        "0.1234567890123456789012345679"
1817    )]
1818    #[case(
1819        r#"{"value": "999999999999999999999999999995e-29"}"#,
1820        "10.000000000000000000000000000"
1821    )]
1822    #[case(r#"{"value": "0.5e29"}"#, "50000000000000000000000000000")]
1823    #[case(r#"{"value": "-4e-29"}"#, "0.0000000000000000000000000000")]
1824    #[case(r#"{"value": "1.5e-999999"}"#, "0.0000000000000000000000000000")]
1825    #[case(r#"{"value": "9e-999999"}"#, "0.0000000000000000000000000000")]
1826    #[case(r#"{"value": "0e2000000000"}"#, "0")]
1827    fn test_deserialize_decimal_rounds_high_scale_values(
1828        #[case] json: &str,
1829        #[case] expected: &str,
1830    ) {
1831        // Carries propagate and a rounded-away negative loses its sign.
1832        let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1833        assert_eq!(result.value.to_string(), expected);
1834    }
1835
1836    #[rstest]
1837    #[rstest]
1838    fn test_deserialize_decimal_negative_rounding_to_zero_loses_sign() {
1839        let json = r#"{"value": "-1.5e-999999"}"#;
1840        let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1841        assert_eq!(result.value.to_string(), "0.0000000000000000000000000000");
1842    }
1843
1844    #[rstest]
1845    #[case(r#"{"value": "899999999999999999999999999995e-29"}"#)]
1846    #[case(r#"{"value": "+899999999999999999999999999995e-29"}"#)]
1847    fn test_deserialize_decimal_rounding_carries_into_integer(#[case] json: &str) {
1848        // Thirty-digit mantissas fail scientific parsing and round up into a non-nine digit
1849        let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1850        assert_eq!(result.value.to_string(), "9.000000000000000000000000000");
1851    }
1852
1853    #[rstest]
1854    #[case(r#"{"value": "8e28"}"#)] // above Decimal::MAX
1855    #[case(r#"{"value": "1e1000000000"}"#)] // absurd exponent must fail without expansion
1856    #[case(r#"{"value": {"number": "1.5"}}"#)]
1857    #[case(r#"{"value": "not-a-number"}"#)]
1858    #[case(r#"{"value": "1.2.3e1"}"#)] // multiple decimal points
1859    #[case(r#"{"value": ".e1"}"#)] // empty coefficient
1860    fn test_deserialize_decimal_rejects_unrepresentable_values(#[case] json: &str) {
1861        // The fallback rounds fractional digits only; oversized magnitudes
1862        // keep their original parse error.
1863        let result: Result<TestDecimalOnly, _> = serde_json::from_str(json);
1864        assert!(result.is_err());
1865    }
1866
1867    #[rstest]
1868    fn test_deserialize_optional_decimal_rounds_high_scale_values() {
1869        let json = r#"{"value": "51.234567890123456789012345678912345e-1"}"#;
1870        let result: TestOptionalDecimalOnly = serde_json::from_str(json).unwrap();
1871        assert_eq!(
1872            result.value.map(|v| v.to_string()),
1873            Some("5.1234567890123456789012345679".into()),
1874        );
1875    }
1876
1877    #[derive(Debug, Deserialize)]
1878    struct TestOptionalDecimalOnly {
1879        #[serde(deserialize_with = "deserialize_optional_decimal")]
1880        value: Option<Decimal>,
1881    }
1882
1883    #[rstest]
1884    #[case(r#"{"value": "1.5e-8"}"#, Some(dec!(0.000000015)))]
1885    #[case(r#"{"value": null}"#, None)]
1886    #[case(r#"{"value": ""}"#, None)]
1887    #[case(r#"{"value": 42}"#, Some(dec!(42)))]
1888    #[case(r#"{"value": -100.5}"#, Some(dec!(-100.5)))]
1889    fn test_deserialize_optional_decimal_various(
1890        #[case] json: &str,
1891        #[case] expected: Option<Decimal>,
1892    ) {
1893        let result: TestOptionalDecimalOnly = serde_json::from_str(json).unwrap();
1894        assert_eq!(result.value, expected);
1895    }
1896
1897    #[rstest]
1898    fn test_decimal_visitor_direct_owned_and_wide_integer_paths() {
1899        assert_eq!(
1900            DecimalVisitor
1901                .visit_string::<ValueError>("1.5".to_string())
1902                .unwrap(),
1903            dec!(1.5)
1904        );
1905        assert_eq!(
1906            DecimalVisitor.visit_i128::<ValueError>(5).unwrap(),
1907            Decimal::from(5i128)
1908        );
1909        assert_eq!(
1910            DecimalVisitor.visit_u128::<ValueError>(5).unwrap(),
1911            Decimal::from(5u128)
1912        );
1913        assert_eq!(
1914            DecimalVisitor.visit_none::<ValueError>().unwrap(),
1915            Decimal::ZERO
1916        );
1917    }
1918
1919    #[rstest]
1920    fn test_decimal_visitor_rejects_non_finite_float() {
1921        assert!(
1922            DecimalVisitor
1923                .visit_f64::<ValueError>(f64::INFINITY)
1924                .is_err()
1925        );
1926        assert!(
1927            DecimalVisitor
1928                .visit_f64::<ValueError>(f64::NEG_INFINITY)
1929                .is_err()
1930        );
1931        assert!(DecimalVisitor.visit_f64::<ValueError>(f64::NAN).is_err());
1932    }
1933
1934    #[rstest]
1935    fn test_optional_decimal_visitor_direct_paths() {
1936        assert_eq!(
1937            OptionalDecimalVisitor
1938                .visit_string::<ValueError>("1.5".to_string())
1939                .unwrap(),
1940            Some(dec!(1.5))
1941        );
1942        assert_eq!(
1943            OptionalDecimalVisitor.visit_i64::<ValueError>(42).unwrap(),
1944            Some(dec!(42))
1945        );
1946        assert_eq!(
1947            OptionalDecimalVisitor.visit_i128::<ValueError>(5).unwrap(),
1948            Some(Decimal::from(5i128))
1949        );
1950        assert_eq!(
1951            OptionalDecimalVisitor.visit_u128::<ValueError>(5).unwrap(),
1952            Some(Decimal::from(5u128))
1953        );
1954        assert_eq!(
1955            OptionalDecimalVisitor.visit_none::<ValueError>().unwrap(),
1956            None
1957        );
1958    }
1959
1960    #[rstest]
1961    fn test_serialize_optional_decimal_none_serializes_null() {
1962        let original = TestFlexibleDecimal {
1963            value: dec!(1),
1964            optional_value: None,
1965        };
1966
1967        let json = serde_json::to_string(&original).unwrap();
1968        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
1969        assert_eq!(parsed["optional_value"], json!(null));
1970    }
1971
1972    #[rstest]
1973    fn test_serde_bool_defaults() {
1974        assert!(default_true());
1975        assert!(!default_false());
1976    }
1977
1978    use proptest::prelude::*;
1979    use rust_decimal::prelude::ToPrimitive;
1980
1981    fn representable_decimal_strategy() -> impl Strategy<Value = Decimal> {
1982        // Mantissa spans Decimal's full 96-bit range; every generated value is
1983        // exactly representable.
1984        (
1985            -79_228_162_514_264_337_593_543_950_335i128
1986                ..=79_228_162_514_264_337_593_543_950_335i128,
1987            0u32..=28u32,
1988        )
1989            .prop_map(|(mantissa, scale)| Decimal::from_i128_with_scale(mantissa, scale))
1990    }
1991
1992    fn numeric_string_strategy() -> impl Strategy<Value = String> {
1993        // Signed digit strings with long fractions and exponents, covering
1994        // both natively-parsed shapes and the high-scale clamp fallback.
1995        (
1996            proptest::bool::ANY,
1997            "[0-9]{1,30}",
1998            proptest::option::of("[0-9]{1,40}"),
1999            proptest::option::of(-40i32..=40),
2000        )
2001            .prop_map(|(negative, integer, fraction, exponent)| {
2002                let mut value = String::new();
2003                if negative {
2004                    value.push('-');
2005                }
2006                value.push_str(&integer);
2007                if let Some(fraction) = fraction {
2008                    value.push('.');
2009                    value.push_str(&fraction);
2010                }
2011
2012                if let Some(exponent) = exponent {
2013                    value.push('e');
2014                    value.push_str(&exponent.to_string());
2015                }
2016                value
2017            })
2018    }
2019
2020    proptest! {
2021        #[rstest]
2022        fn prop_deserialize_decimal_scientific_roundtrips_representable_values(
2023            expected in representable_decimal_strategy(),
2024            zeros in 0usize..=35,
2025        ) {
2026            let scientific = format!(
2027                "{}{}e-{}",
2028                expected.mantissa(),
2029                "0".repeat(zeros),
2030                expected.scale() as usize + zeros,
2031            );
2032            let json = serde_json::to_string(&json!({"value": scientific})).unwrap();
2033            let required: TestDecimalOnly = serde_json::from_str(&json).unwrap();
2034            let optional: TestOptionalDecimalOnly = serde_json::from_str(&json).unwrap();
2035
2036            prop_assert_eq!(required.value, expected);
2037            prop_assert_eq!(optional.value, Some(expected));
2038        }
2039
2040        #[rstest]
2041        fn prop_deserialize_decimal_roundtrips_representable_values(
2042            expected in representable_decimal_strategy()
2043        ) {
2044            // The clamp fallback must never distort a value Decimal can hold
2045            // exactly.
2046            let json = format!(r#"{{"value": "{expected}"}}"#);
2047            let parsed: TestDecimalOnly = serde_json::from_str(&json).unwrap();
2048            prop_assert_eq!(parsed.value, expected);
2049        }
2050
2051        #[rstest]
2052        fn prop_deserialize_decimal_total_on_arbitrary_strings(value in "\\PC{0,64}") {
2053            // Any string input must decode or error without panicking.
2054            let json = serde_json::to_string(&serde_json::json!({"value": value})).unwrap();
2055            let _ = serde_json::from_str::<TestDecimalOnly>(&json);
2056        }
2057
2058        #[rstest]
2059        fn prop_deserialize_decimal_tracks_f64_reference(value in numeric_string_strategy()) {
2060            // Accepted values (rounded or not) must agree with an independent
2061            // f64 parse of the same string within f64 precision.
2062            let json = format!(r#"{{"value": "{value}"}}"#);
2063            if let Ok(parsed) = serde_json::from_str::<TestDecimalOnly>(&json) {
2064                let reference: f64 = value.parse().unwrap();
2065                let decoded = parsed.value.to_f64().unwrap();
2066                prop_assert!(
2067                    (decoded - reference).abs() <= reference.abs() * 1e-9 + 1e-27,
2068                    "decoded {decoded} diverges from reference {reference} for input {value}",
2069                );
2070            }
2071        }
2072
2073        #[rstest]
2074        fn prop_deserialize_optional_decimal_matches_required(
2075            value in numeric_string_strategy()
2076        ) {
2077            let json = format!(r#"{{"value": "{value}"}}"#);
2078            let required = serde_json::from_str::<TestDecimalOnly>(&json);
2079            let optional = serde_json::from_str::<TestOptionalDecimalOnly>(&json);
2080            match (required, optional) {
2081                (Ok(required), Ok(optional)) => {
2082                    prop_assert_eq!(optional.value, Some(required.value));
2083                }
2084                (Err(_), Err(_)) => {}
2085                (required, optional) => prop_assert!(
2086                    false,
2087                    "required and optional decoding disagree for input {}: {:?} vs {:?}",
2088                    value,
2089                    required.map(|r| r.value),
2090                    optional.map(|o| o.value),
2091                ),
2092            }
2093        }
2094    }
2095
2096    #[derive(Debug, Deserialize)]
2097    struct Token(#[serde(deserialize_with = "deserialize_decimal_token_borrowed")] Decimal);
2098
2099    #[rstest]
2100    #[case(r#""1.2300""#, 12300, 4)]
2101    #[case(r#""\u0031.2300""#, 12300, 4)]
2102    #[case(r#""1.2300\u0030""#, 123_000, 5)]
2103    #[case(r#""-0.00001000""#, -1000, 8)]
2104    #[case("9007199254740993", 9_007_199_254_740_993, 0)]
2105    #[case("18446744073709551617", 18_446_744_073_709_551_617, 0)]
2106    fn test_decimal_token_preserves_digits_and_scale(
2107        #[case] json: &str,
2108        #[case] mantissa: i128,
2109        #[case] scale: u32,
2110    ) {
2111        let value = serde_json::from_str::<Token>(json).unwrap().0;
2112        assert_eq!(value.mantissa(), mantissa);
2113        assert_eq!(value.scale(), scale);
2114    }
2115
2116    #[rstest]
2117    #[case(r#""\uD800""#)]
2118    #[case(r#""\uDC00""#)]
2119    #[case(r#""\q""#)]
2120    #[case(r#""1.2\n3""#)]
2121    #[case(r#""1.2\"3""#)]
2122    fn test_decimal_token_rejects_invalid_strings(#[case] json: &str) {
2123        assert!(serde_json::from_str::<Token>(json).is_err());
2124    }
2125}