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