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::str::FromStr;
26
27use bytes::Bytes;
28use rust_decimal::Decimal;
29use serde::{
30    Deserialize, Deserializer, Serialize, Serializer,
31    de::{Error, Unexpected, Visitor},
32    ser::SerializeSeq,
33};
34use ustr::Ustr;
35
36struct BoolVisitor;
37
38/// Zero-allocation decimal visitor for maximum deserialization performance.
39///
40/// Directly visits JSON tokens without intermediate `serde_json::Value` allocation.
41/// Handles all JSON numeric representations: strings, integers, floats, and null.
42struct DecimalVisitor;
43
44impl Visitor<'_> for DecimalVisitor {
45    type Value = Decimal;
46
47    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
48        formatter.write_str("a decimal number as string, integer, or float")
49    }
50
51    // Fast path: borrowed string (zero-copy)
52    fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
53        if v.is_empty() {
54            return Ok(Decimal::ZERO);
55        }
56        // Check for scientific notation
57        if v.contains('e') || v.contains('E') {
58            Decimal::from_scientific(v).map_err(E::custom)
59        } else {
60            Decimal::from_str(v).map_err(E::custom)
61        }
62    }
63
64    // Owned string (rare case, delegates to visit_str)
65    fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
66        self.visit_str(&v)
67    }
68
69    // Direct integer handling - no string conversion needed
70    fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> {
71        Ok(Decimal::from(v))
72    }
73
74    fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
75        Ok(Decimal::from(v))
76    }
77
78    fn visit_i128<E: Error>(self, v: i128) -> Result<Self::Value, E> {
79        Ok(Decimal::from(v))
80    }
81
82    fn visit_u128<E: Error>(self, v: u128) -> Result<Self::Value, E> {
83        Ok(Decimal::from(v))
84    }
85
86    // Float handling - direct conversion
87    fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
88        if v.is_nan() {
89            return Err(E::invalid_value(Unexpected::Float(v), &self));
90        }
91        if v.is_infinite() {
92            return Err(E::invalid_value(Unexpected::Float(v), &self));
93        }
94        Decimal::try_from(v).map_err(E::custom)
95    }
96
97    // Null → zero (matches existing behavior)
98    fn visit_unit<E: Error>(self) -> Result<Self::Value, E> {
99        Ok(Decimal::ZERO)
100    }
101
102    fn visit_none<E: Error>(self) -> Result<Self::Value, E> {
103        Ok(Decimal::ZERO)
104    }
105}
106
107/// Zero-allocation optional decimal visitor for maximum deserialization performance.
108///
109/// Handles null values as `None` and empty strings as `None`.
110/// Uses `deserialize_any` approach to handle all JSON value types uniformly.
111struct OptionalDecimalVisitor;
112
113impl Visitor<'_> for OptionalDecimalVisitor {
114    type Value = Option<Decimal>;
115
116    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
117        formatter.write_str("null or a decimal number as string, integer, or float")
118    }
119
120    // Fast path: borrowed string (zero-copy)
121    // Empty string → None (different from DecimalVisitor which returns ZERO)
122    fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
123        if v.is_empty() {
124            return Ok(None);
125        }
126        DecimalVisitor.visit_str(v).map(Some)
127    }
128
129    fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
130        self.visit_str(&v)
131    }
132
133    fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> {
134        DecimalVisitor.visit_i64(v).map(Some)
135    }
136
137    fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
138        DecimalVisitor.visit_u64(v).map(Some)
139    }
140
141    fn visit_i128<E: Error>(self, v: i128) -> Result<Self::Value, E> {
142        DecimalVisitor.visit_i128(v).map(Some)
143    }
144
145    fn visit_u128<E: Error>(self, v: u128) -> Result<Self::Value, E> {
146        DecimalVisitor.visit_u128(v).map(Some)
147    }
148
149    fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
150        DecimalVisitor.visit_f64(v).map(Some)
151    }
152
153    // Null → None
154    fn visit_unit<E: Error>(self) -> Result<Self::Value, E> {
155        Ok(None)
156    }
157
158    fn visit_none<E: Error>(self) -> Result<Self::Value, E> {
159        Ok(None)
160    }
161}
162
163/// Represents types which are serializable for JSON specifications.
164pub trait Serializable: Serialize + for<'de> Deserialize<'de> {
165    /// Deserialize an object from JSON encoded bytes.
166    ///
167    /// # Errors
168    ///
169    /// Returns serialization errors.
170    fn from_json_bytes(data: &[u8]) -> Result<Self, serde_json::Error> {
171        serde_json::from_slice(data)
172    }
173
174    /// Serialize an object to JSON encoded bytes.
175    ///
176    /// # Errors
177    ///
178    /// Returns serialization errors.
179    fn to_json_bytes(&self) -> Result<Bytes, serde_json::Error> {
180        serde_json::to_vec(self).map(Bytes::from)
181    }
182}
183
184pub use self::msgpack::{FromMsgPack, MsgPackSerializable, ToMsgPack};
185
186/// Provides MsgPack serialization support for types implementing [`Serializable`].
187///
188/// This module contains traits for MsgPack serialization and deserialization,
189/// separated from the core [`Serializable`] trait to allow independent opt-in.
190pub mod msgpack {
191    use bytes::Bytes;
192    use serde::{Deserialize, Serialize};
193
194    use super::Serializable;
195
196    /// Provides deserialization from MsgPack encoded bytes.
197    pub trait FromMsgPack: for<'de> Deserialize<'de> + Sized {
198        /// Deserialize an object from MsgPack encoded bytes.
199        ///
200        /// # Errors
201        ///
202        /// Returns serialization errors.
203        fn from_msgpack_bytes(data: &[u8]) -> Result<Self, rmp_serde::decode::Error> {
204            rmp_serde::from_slice(data)
205        }
206    }
207
208    /// Provides serialization to MsgPack encoded bytes.
209    pub trait ToMsgPack: Serialize {
210        /// Serialize an object to MsgPack encoded bytes.
211        ///
212        /// # Errors
213        ///
214        /// Returns serialization errors.
215        fn to_msgpack_bytes(&self) -> Result<Bytes, rmp_serde::encode::Error> {
216            rmp_serde::to_vec_named(self).map(Bytes::from)
217        }
218    }
219
220    /// Marker trait combining [`Serializable`], [`FromMsgPack`], and [`ToMsgPack`].
221    ///
222    /// This trait is automatically implemented for all types that implement [`Serializable`].
223    pub trait MsgPackSerializable: Serializable + FromMsgPack + ToMsgPack {}
224
225    impl<T> FromMsgPack for T where T: Serializable {}
226
227    impl<T> ToMsgPack for T where T: Serializable {}
228
229    impl<T> MsgPackSerializable for T where T: Serializable {}
230}
231
232impl Visitor<'_> for BoolVisitor {
233    type Value = u8;
234
235    fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236        formatter.write_str("a boolean as u8")
237    }
238
239    fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
240    where
241        E: serde::de::Error,
242    {
243        Ok(u8::from(value))
244    }
245
246    #[allow(
247        clippy::cast_possible_truncation,
248        reason = "Intentional for parsing, value range validated"
249    )]
250    fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
251    where
252        E: serde::de::Error,
253    {
254        // Only 0 or 1 are considered valid representations when provided as an
255        // integer. We deliberately reject values outside this range to avoid
256        // silently truncating larger integers into impl-defined boolean
257        // semantics.
258        if value > 1 {
259            Err(E::invalid_value(Unexpected::Unsigned(value), &self))
260        } else {
261            Ok(value as u8)
262        }
263    }
264}
265
266/// Serde default value function that returns `true`.
267///
268/// Use with `#[serde(default = "default_true")]` on boolean fields.
269#[must_use]
270pub const fn default_true() -> bool {
271    true
272}
273
274/// Serde default value function that returns `false`.
275///
276/// Use with `#[serde(default = "default_false")]` on boolean fields.
277#[must_use]
278pub const fn default_false() -> bool {
279    false
280}
281
282/// Deserialize the boolean value as a `u8`.
283///
284/// # Errors
285///
286/// Returns serialization errors.
287pub fn from_bool_as_u8<'de, D>(deserializer: D) -> Result<u8, D::Error>
288where
289    D: Deserializer<'de>,
290{
291    deserializer.deserialize_any(BoolVisitor)
292}
293
294/// Deserializes a `Decimal` from either a JSON string or number.
295///
296/// High-performance implementation using a custom visitor that avoids intermediate
297/// `serde_json::Value` allocations. Handles all JSON numeric representations:
298///
299/// - JSON string: `"123.456"` → Decimal (zero-copy for borrowed strings)
300/// - JSON integer: `123` → Decimal (direct conversion, no string allocation)
301/// - JSON float: `123.456` → Decimal
302/// - JSON null: → `Decimal::ZERO`
303/// - Scientific notation: `"1.5e-8"` → Decimal
304///
305/// # Performance
306///
307/// This implementation is optimized for high-frequency trading scenarios:
308/// - Zero allocations for string values (uses borrowed `&str`)
309/// - Direct integer conversion without string intermediary
310/// - No intermediate `serde_json::Value` heap allocation
311///
312/// # Errors
313///
314/// Returns an error if the value cannot be parsed as a valid decimal.
315pub fn deserialize_decimal<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
316where
317    D: Deserializer<'de>,
318{
319    deserializer.deserialize_any(DecimalVisitor)
320}
321
322/// Deserializes an `Option<Decimal>` from a JSON string, number, or null.
323///
324/// High-performance implementation using a custom visitor that avoids intermediate
325/// `serde_json::Value` allocations. Handles all JSON numeric representations:
326///
327/// - JSON string: `"123.456"` → Some(Decimal) (zero-copy for borrowed strings)
328/// - JSON integer: `123` → Some(Decimal) (direct conversion)
329/// - JSON float: `123.456` → Some(Decimal)
330/// - JSON null: → `None`
331/// - Empty string: `""` → `None`
332/// - Scientific notation: `"1.5e-8"` → Some(Decimal)
333///
334/// # Performance
335///
336/// This implementation is optimized for high-frequency trading scenarios:
337/// - Zero allocations for string values (uses borrowed `&str`)
338/// - Direct integer conversion without string intermediary
339/// - No intermediate `serde_json::Value` heap allocation
340///
341/// # Errors
342///
343/// Returns an error if the value cannot be parsed as a valid decimal.
344pub fn deserialize_optional_decimal<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
345where
346    D: Deserializer<'de>,
347{
348    // Use deserialize_any to handle all JSON value types uniformly
349    // (deserialize_option would route non-null through visit_some, losing empty string handling)
350    deserializer.deserialize_any(OptionalDecimalVisitor)
351}
352
353/// Serializes a `Decimal` as a JSON number (float).
354///
355/// Used for outgoing requests where exchange APIs expect JSON numbers.
356///
357/// # Errors
358///
359/// Returns an error if serialization fails.
360pub fn serialize_decimal<S: Serializer>(d: &Decimal, s: S) -> Result<S::Ok, S::Error> {
361    rust_decimal::serde::float::serialize(d, s)
362}
363
364/// Serializes an `Option<Decimal>` as a JSON number or null.
365///
366/// # Errors
367///
368/// Returns an error if serialization fails.
369pub fn serialize_optional_decimal<S: Serializer>(
370    d: &Option<Decimal>,
371    s: S,
372) -> Result<S::Ok, S::Error> {
373    match d {
374        Some(decimal) => rust_decimal::serde::float::serialize(decimal, s),
375        None => s.serialize_none(),
376    }
377}
378
379/// Deserializes a `Decimal` from a JSON string.
380///
381/// This is the strict form that requires the value to be a string, rejecting
382/// numeric JSON values to avoid precision loss.
383///
384/// # Errors
385///
386/// Returns an error if the string cannot be parsed as a valid decimal.
387pub fn deserialize_decimal_from_str<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
388where
389    D: Deserializer<'de>,
390{
391    let s = String::deserialize(deserializer)?;
392    Decimal::from_str(&s).map_err(D::Error::custom)
393}
394
395/// Deserializes a `Decimal` from a string field that might be empty.
396///
397/// Handles edge cases where empty string "" or "0" becomes `Decimal::ZERO`.
398///
399/// # Errors
400///
401/// Returns an error if the string cannot be parsed as a valid decimal.
402pub fn deserialize_decimal_or_zero<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
403where
404    D: Deserializer<'de>,
405{
406    let s: String = Deserialize::deserialize(deserializer)?;
407    if s.is_empty() || s == "0" {
408        Ok(Decimal::ZERO)
409    } else {
410        Decimal::from_str(&s).map_err(D::Error::custom)
411    }
412}
413
414/// Deserializes an optional `Decimal` from a string field.
415///
416/// Returns `None` if the string is empty or "0", otherwise parses to `Decimal`.
417/// This is a strict string-only deserializer; for flexible handling of strings,
418/// numbers, and null, use [`deserialize_optional_decimal`].
419///
420/// # Errors
421///
422/// Returns an error if the string cannot be parsed as a valid decimal.
423pub fn deserialize_optional_decimal_str<'de, D>(
424    deserializer: D,
425) -> Result<Option<Decimal>, D::Error>
426where
427    D: Deserializer<'de>,
428{
429    let s: String = Deserialize::deserialize(deserializer)?;
430    if s.is_empty() || s == "0" {
431        Ok(None)
432    } else {
433        Decimal::from_str(&s).map(Some).map_err(D::Error::custom)
434    }
435}
436
437/// Deserializes an optional `Decimal` from a string-only field.
438///
439/// Returns `None` if the value is null or the string is empty, otherwise
440/// parses to `Decimal`.
441///
442/// # Errors
443///
444/// Returns an error if the string cannot be parsed as a valid decimal.
445pub fn deserialize_optional_decimal_from_str<'de, D>(
446    deserializer: D,
447) -> Result<Option<Decimal>, D::Error>
448where
449    D: Deserializer<'de>,
450{
451    let opt = Option::<String>::deserialize(deserializer)?;
452    match opt {
453        Some(s) if !s.is_empty() => Decimal::from_str(&s).map(Some).map_err(D::Error::custom),
454        _ => Ok(None),
455    }
456}
457
458/// Deserializes a `Decimal` from an optional string field, defaulting to zero.
459///
460/// Handles edge cases: `None`, empty string "", or "0" all become `Decimal::ZERO`.
461///
462/// # Errors
463///
464/// Returns an error if the string cannot be parsed as a valid decimal.
465pub fn deserialize_optional_decimal_or_zero<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
466where
467    D: Deserializer<'de>,
468{
469    let opt: Option<String> = Deserialize::deserialize(deserializer)?;
470    match opt {
471        None => Ok(Decimal::ZERO),
472        Some(s) if s.is_empty() || s == "0" => Ok(Decimal::ZERO),
473        Some(s) => Decimal::from_str(&s).map_err(D::Error::custom),
474    }
475}
476
477/// Deserializes a `Vec<Decimal>` from a JSON array of strings.
478///
479/// # Errors
480///
481/// Returns an error if any string cannot be parsed as a valid decimal.
482pub fn deserialize_vec_decimal_from_str<'de, D>(deserializer: D) -> Result<Vec<Decimal>, D::Error>
483where
484    D: Deserializer<'de>,
485{
486    let strings = Vec::<String>::deserialize(deserializer)?;
487    strings
488        .into_iter()
489        .map(|s| Decimal::from_str(&s).map_err(D::Error::custom))
490        .collect()
491}
492
493/// Serializes a `Decimal` as a string (lossless, no scientific notation).
494///
495/// # Errors
496///
497/// Returns an error if serialization fails.
498pub fn serialize_decimal_as_str<S>(decimal: &Decimal, serializer: S) -> Result<S::Ok, S::Error>
499where
500    S: Serializer,
501{
502    serializer.serialize_str(&decimal.to_string())
503}
504
505/// Serializes an optional `Decimal` as a string.
506///
507/// # Errors
508///
509/// Returns an error if serialization fails.
510pub fn serialize_optional_decimal_as_str<S>(
511    decimal: &Option<Decimal>,
512    serializer: S,
513) -> Result<S::Ok, S::Error>
514where
515    S: Serializer,
516{
517    match decimal {
518        Some(d) => serializer.serialize_str(&d.to_string()),
519        None => serializer.serialize_none(),
520    }
521}
522
523/// Serializes a `Vec<Decimal>` as an array of strings.
524///
525/// # Errors
526///
527/// Returns an error if serialization fails.
528pub fn serialize_vec_decimal_as_str<S>(
529    decimals: &Vec<Decimal>,
530    serializer: S,
531) -> Result<S::Ok, S::Error>
532where
533    S: Serializer,
534{
535    let mut seq = serializer.serialize_seq(Some(decimals.len()))?;
536    for decimal in decimals {
537        seq.serialize_element(&decimal.to_string())?;
538    }
539    seq.end()
540}
541
542/// Parses a string to `Decimal`, returning an error if parsing fails.
543///
544/// # Errors
545///
546/// Returns an error if the string cannot be parsed as a Decimal.
547pub fn parse_decimal(s: &str) -> anyhow::Result<Decimal> {
548    Decimal::from_str(s).map_err(|e| anyhow::anyhow!("Failed to parse decimal from '{s}': {e}"))
549}
550
551/// Parses an optional string to `Decimal`, returning `None` if the string is `None` or empty.
552///
553/// # Errors
554///
555/// Returns an error if the string cannot be parsed as a Decimal.
556pub fn parse_optional_decimal(s: &Option<String>) -> anyhow::Result<Option<Decimal>> {
557    match s {
558        None => Ok(None),
559        Some(s) if s.is_empty() => Ok(None),
560        Some(s) => parse_decimal(s).map(Some),
561    }
562}
563
564/// Deserializes an empty string into `None`.
565///
566/// Many exchange APIs represent null string fields as an empty string (`""`).
567/// When such a payload is mapped onto `Option<String>` the default behavior
568/// would yield `Some("")`, which is semantically different from the intended
569/// absence of a value. This helper ensures that empty strings are normalized
570/// to `None` during deserialization.
571///
572/// # Errors
573///
574/// Returns an error if the JSON value cannot be deserialized into a string.
575pub fn deserialize_empty_string_as_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
576where
577    D: Deserializer<'de>,
578{
579    let opt = Option::<String>::deserialize(deserializer)?;
580    Ok(opt.filter(|s| !s.is_empty()))
581}
582
583/// Deserializes an empty [`Ustr`] into `None`.
584///
585/// # Errors
586///
587/// Returns an error if the JSON value cannot be deserialized into a string.
588pub fn deserialize_empty_ustr_as_none<'de, D>(deserializer: D) -> Result<Option<Ustr>, D::Error>
589where
590    D: Deserializer<'de>,
591{
592    let opt = Option::<Ustr>::deserialize(deserializer)?;
593    Ok(opt.filter(|s| !s.is_empty()))
594}
595
596/// Deserializes a `u8` from a string field.
597///
598/// Returns 0 if the string is empty.
599///
600/// # Errors
601///
602/// Returns an error if the string cannot be parsed as a u8.
603pub fn deserialize_string_to_u8<'de, D>(deserializer: D) -> Result<u8, D::Error>
604where
605    D: Deserializer<'de>,
606{
607    let s: String = Deserialize::deserialize(deserializer)?;
608    if s.is_empty() {
609        return Ok(0);
610    }
611    s.parse::<u8>().map_err(D::Error::custom)
612}
613
614/// Deserializes a `u64` from a string field.
615///
616/// Returns 0 if the string is empty.
617///
618/// # Errors
619///
620/// Returns an error if the string cannot be parsed as a u64.
621pub fn deserialize_string_to_u64<'de, D>(deserializer: D) -> Result<u64, D::Error>
622where
623    D: Deserializer<'de>,
624{
625    let s = String::deserialize(deserializer)?;
626    if s.is_empty() {
627        Ok(0)
628    } else {
629        s.parse::<u64>().map_err(D::Error::custom)
630    }
631}
632
633/// Deserializes an optional `u64` from a string field.
634///
635/// Returns `None` if the value is null or the string is empty.
636///
637/// # Errors
638///
639/// Returns an error if the string cannot be parsed as a u64.
640pub fn deserialize_optional_string_to_u64<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
641where
642    D: Deserializer<'de>,
643{
644    let s: Option<String> = Option::deserialize(deserializer)?;
645    match s {
646        Some(s) if s.is_empty() => Ok(None),
647        Some(s) => s.parse().map(Some).map_err(D::Error::custom),
648        None => Ok(None),
649    }
650}
651
652#[cfg(test)]
653mod tests {
654    use rstest::*;
655    use rust_decimal::Decimal;
656    use rust_decimal_macros::dec;
657    use serde::{Deserialize, Serialize};
658    use ustr::Ustr;
659
660    use super::{
661        Serializable, deserialize_decimal, deserialize_decimal_from_str,
662        deserialize_decimal_or_zero, deserialize_empty_string_as_none,
663        deserialize_empty_ustr_as_none, deserialize_optional_decimal,
664        deserialize_optional_decimal_or_zero, deserialize_optional_decimal_str,
665        deserialize_optional_string_to_u64, deserialize_string_to_u8, deserialize_string_to_u64,
666        deserialize_vec_decimal_from_str, from_bool_as_u8,
667        msgpack::{FromMsgPack, ToMsgPack},
668        parse_decimal, parse_optional_decimal, serialize_decimal, serialize_decimal_as_str,
669        serialize_optional_decimal, serialize_optional_decimal_as_str,
670        serialize_vec_decimal_as_str,
671    };
672
673    #[derive(Deserialize)]
674    pub struct TestStruct {
675        #[serde(deserialize_with = "from_bool_as_u8")]
676        pub value: u8,
677    }
678
679    #[rstest]
680    #[case(r#"{"value": true}"#, 1)]
681    #[case(r#"{"value": false}"#, 0)]
682    fn test_deserialize_bool_as_u8_with_boolean(#[case] json_str: &str, #[case] expected: u8) {
683        let test_struct: TestStruct = serde_json::from_str(json_str).unwrap();
684        assert_eq!(test_struct.value, expected);
685    }
686
687    #[rstest]
688    #[case(r#"{"value": 1}"#, 1)]
689    #[case(r#"{"value": 0}"#, 0)]
690    fn test_deserialize_bool_as_u8_with_u64(#[case] json_str: &str, #[case] expected: u8) {
691        let test_struct: TestStruct = serde_json::from_str(json_str).unwrap();
692        assert_eq!(test_struct.value, expected);
693    }
694
695    #[rstest]
696    fn test_deserialize_bool_as_u8_with_invalid_integer() {
697        // Any integer other than 0/1 is invalid and should error
698        let json = r#"{"value": 2}"#;
699        let result: Result<TestStruct, _> = serde_json::from_str(json);
700        assert!(result.is_err());
701    }
702
703    #[derive(Serialize, Deserialize, PartialEq, Debug)]
704    struct SerializableTestStruct {
705        id: u32,
706        name: String,
707        value: f64,
708    }
709
710    impl Serializable for SerializableTestStruct {}
711
712    #[rstest]
713    fn test_serializable_json_roundtrip() {
714        let original = SerializableTestStruct {
715            id: 42,
716            name: "test".to_string(),
717            value: std::f64::consts::PI,
718        };
719
720        let json_bytes = original.to_json_bytes().unwrap();
721        let deserialized = SerializableTestStruct::from_json_bytes(&json_bytes).unwrap();
722
723        assert_eq!(original, deserialized);
724    }
725
726    #[rstest]
727    fn test_serializable_msgpack_roundtrip() {
728        let original = SerializableTestStruct {
729            id: 123,
730            name: "msgpack_test".to_string(),
731            value: std::f64::consts::E,
732        };
733
734        let msgpack_bytes = original.to_msgpack_bytes().unwrap();
735        let deserialized = SerializableTestStruct::from_msgpack_bytes(&msgpack_bytes).unwrap();
736
737        assert_eq!(original, deserialized);
738    }
739
740    #[rstest]
741    fn test_serializable_json_invalid_data() {
742        let invalid_json = b"invalid json data";
743        let result = SerializableTestStruct::from_json_bytes(invalid_json);
744        assert!(result.is_err());
745    }
746
747    #[rstest]
748    fn test_serializable_msgpack_invalid_data() {
749        let invalid_msgpack = b"invalid msgpack data";
750        let result = SerializableTestStruct::from_msgpack_bytes(invalid_msgpack);
751        assert!(result.is_err());
752    }
753
754    #[rstest]
755    fn test_serializable_json_empty_values() {
756        let test_struct = SerializableTestStruct {
757            id: 0,
758            name: String::new(),
759            value: 0.0,
760        };
761
762        let json_bytes = test_struct.to_json_bytes().unwrap();
763        let deserialized = SerializableTestStruct::from_json_bytes(&json_bytes).unwrap();
764
765        assert_eq!(test_struct, deserialized);
766    }
767
768    #[rstest]
769    fn test_serializable_msgpack_empty_values() {
770        let test_struct = SerializableTestStruct {
771            id: 0,
772            name: String::new(),
773            value: 0.0,
774        };
775
776        let msgpack_bytes = test_struct.to_msgpack_bytes().unwrap();
777        let deserialized = SerializableTestStruct::from_msgpack_bytes(&msgpack_bytes).unwrap();
778
779        assert_eq!(test_struct, deserialized);
780    }
781
782    #[derive(Deserialize)]
783    struct TestOptionalDecimalStr {
784        #[serde(deserialize_with = "deserialize_optional_decimal_str")]
785        value: Option<Decimal>,
786    }
787
788    #[derive(Deserialize)]
789    struct TestDecimalOrZero {
790        #[serde(deserialize_with = "deserialize_decimal_or_zero")]
791        value: Decimal,
792    }
793
794    #[derive(Deserialize)]
795    struct TestOptionalDecimalOrZero {
796        #[serde(deserialize_with = "deserialize_optional_decimal_or_zero")]
797        value: Decimal,
798    }
799
800    #[derive(Serialize, Deserialize, PartialEq, Debug)]
801    struct TestDecimalRoundtrip {
802        #[serde(
803            serialize_with = "serialize_decimal_as_str",
804            deserialize_with = "deserialize_decimal_from_str"
805        )]
806        value: Decimal,
807        #[serde(
808            serialize_with = "serialize_optional_decimal_as_str",
809            deserialize_with = "super::deserialize_optional_decimal_from_str"
810        )]
811        optional_value: Option<Decimal>,
812    }
813
814    #[rstest]
815    #[case(r#"{"value":"123.45"}"#, Some(dec!(123.45)))]
816    #[case(r#"{"value":"0"}"#, None)]
817    #[case(r#"{"value":""}"#, None)]
818    fn test_deserialize_optional_decimal_str(
819        #[case] json: &str,
820        #[case] expected: Option<Decimal>,
821    ) {
822        let result: TestOptionalDecimalStr = serde_json::from_str(json).unwrap();
823        assert_eq!(result.value, expected);
824    }
825
826    #[rstest]
827    #[case(r#"{"value":"123.45"}"#, dec!(123.45))]
828    #[case(r#"{"value":"0"}"#, Decimal::ZERO)]
829    #[case(r#"{"value":""}"#, Decimal::ZERO)]
830    fn test_deserialize_decimal_or_zero(#[case] json: &str, #[case] expected: Decimal) {
831        let result: TestDecimalOrZero = serde_json::from_str(json).unwrap();
832        assert_eq!(result.value, expected);
833    }
834
835    #[rstest]
836    #[case(r#"{"value":"123.45"}"#, dec!(123.45))]
837    #[case(r#"{"value":"0"}"#, Decimal::ZERO)]
838    #[case(r#"{"value":null}"#, Decimal::ZERO)]
839    fn test_deserialize_optional_decimal_or_zero(#[case] json: &str, #[case] expected: Decimal) {
840        let result: TestOptionalDecimalOrZero = serde_json::from_str(json).unwrap();
841        assert_eq!(result.value, expected);
842    }
843
844    #[rstest]
845    fn test_decimal_serialization_roundtrip() {
846        let original = TestDecimalRoundtrip {
847            value: dec!(123.456789012345678),
848            optional_value: Some(dec!(0.000000001)),
849        };
850
851        let json = serde_json::to_string(&original).unwrap();
852
853        // Check that it's serialized as strings
854        assert!(json.contains("\"123.456789012345678\""));
855        assert!(json.contains("\"0.000000001\""));
856
857        let deserialized: TestDecimalRoundtrip = serde_json::from_str(&json).unwrap();
858        assert_eq!(original.value, deserialized.value);
859        assert_eq!(original.optional_value, deserialized.optional_value);
860    }
861
862    #[rstest]
863    fn test_decimal_optional_none_handling() {
864        let test_struct = TestDecimalRoundtrip {
865            value: dec!(42.0),
866            optional_value: None,
867        };
868
869        let json = serde_json::to_string(&test_struct).unwrap();
870        assert!(json.contains("null"));
871
872        let parsed: TestDecimalRoundtrip = serde_json::from_str(&json).unwrap();
873        assert_eq!(test_struct.value, parsed.value);
874        assert_eq!(None, parsed.optional_value);
875    }
876
877    #[derive(Deserialize)]
878    struct TestEmptyStringAsNone {
879        #[serde(deserialize_with = "deserialize_empty_string_as_none")]
880        value: Option<String>,
881    }
882
883    #[rstest]
884    #[case(r#"{"value":"hello"}"#, Some("hello".to_string()))]
885    #[case(r#"{"value":""}"#, None)]
886    #[case(r#"{"value":null}"#, None)]
887    fn test_deserialize_empty_string_as_none(#[case] json: &str, #[case] expected: Option<String>) {
888        let result: TestEmptyStringAsNone = serde_json::from_str(json).unwrap();
889        assert_eq!(result.value, expected);
890    }
891
892    #[derive(Deserialize)]
893    struct TestEmptyUstrAsNone {
894        #[serde(deserialize_with = "deserialize_empty_ustr_as_none")]
895        value: Option<Ustr>,
896    }
897
898    #[rstest]
899    #[case(r#"{"value":"hello"}"#, Some(Ustr::from("hello")))]
900    #[case(r#"{"value":""}"#, None)]
901    #[case(r#"{"value":null}"#, None)]
902    fn test_deserialize_empty_ustr_as_none(#[case] json: &str, #[case] expected: Option<Ustr>) {
903        let result: TestEmptyUstrAsNone = serde_json::from_str(json).unwrap();
904        assert_eq!(result.value, expected);
905    }
906
907    #[derive(Serialize, Deserialize, PartialEq, Debug)]
908    struct TestVecDecimal {
909        #[serde(
910            serialize_with = "serialize_vec_decimal_as_str",
911            deserialize_with = "deserialize_vec_decimal_from_str"
912        )]
913        values: Vec<Decimal>,
914    }
915
916    #[rstest]
917    fn test_vec_decimal_roundtrip() {
918        let original = TestVecDecimal {
919            values: vec![dec!(1.5), dec!(2.25), dec!(100.001)],
920        };
921
922        let json = serde_json::to_string(&original).unwrap();
923        assert!(json.contains("[\"1.5\",\"2.25\",\"100.001\"]"));
924
925        let parsed: TestVecDecimal = serde_json::from_str(&json).unwrap();
926        assert_eq!(original.values, parsed.values);
927    }
928
929    #[rstest]
930    fn test_vec_decimal_empty() {
931        let original = TestVecDecimal { values: vec![] };
932
933        let json = serde_json::to_string(&original).unwrap();
934        let parsed: TestVecDecimal = serde_json::from_str(&json).unwrap();
935        assert_eq!(original.values, parsed.values);
936    }
937
938    #[derive(Deserialize)]
939    struct TestStringToU8 {
940        #[serde(deserialize_with = "deserialize_string_to_u8")]
941        value: u8,
942    }
943
944    #[rstest]
945    #[case(r#"{"value":"42"}"#, 42)]
946    #[case(r#"{"value":"0"}"#, 0)]
947    #[case(r#"{"value":""}"#, 0)]
948    fn test_deserialize_string_to_u8(#[case] json: &str, #[case] expected: u8) {
949        let result: TestStringToU8 = serde_json::from_str(json).unwrap();
950        assert_eq!(result.value, expected);
951    }
952
953    #[derive(Deserialize)]
954    struct TestStringToU64 {
955        #[serde(deserialize_with = "deserialize_string_to_u64")]
956        value: u64,
957    }
958
959    #[rstest]
960    #[case(r#"{"value":"12345678901234"}"#, 12345678901234)]
961    #[case(r#"{"value":"0"}"#, 0)]
962    #[case(r#"{"value":""}"#, 0)]
963    fn test_deserialize_string_to_u64(#[case] json: &str, #[case] expected: u64) {
964        let result: TestStringToU64 = serde_json::from_str(json).unwrap();
965        assert_eq!(result.value, expected);
966    }
967
968    #[derive(Deserialize)]
969    struct TestOptionalStringToU64 {
970        #[serde(deserialize_with = "deserialize_optional_string_to_u64")]
971        value: Option<u64>,
972    }
973
974    #[rstest]
975    #[case(r#"{"value":"12345678901234"}"#, Some(12345678901234))]
976    #[case(r#"{"value":"0"}"#, Some(0))]
977    #[case(r#"{"value":""}"#, None)]
978    #[case(r#"{"value":null}"#, None)]
979    fn test_deserialize_optional_string_to_u64(#[case] json: &str, #[case] expected: Option<u64>) {
980        let result: TestOptionalStringToU64 = serde_json::from_str(json).unwrap();
981        assert_eq!(result.value, expected);
982    }
983
984    #[rstest]
985    #[case("123.45", dec!(123.45))]
986    #[case("0", Decimal::ZERO)]
987    #[case("0.0", Decimal::ZERO)]
988    fn test_parse_decimal(#[case] input: &str, #[case] expected: Decimal) {
989        let result = parse_decimal(input).unwrap();
990        assert_eq!(result, expected);
991    }
992
993    #[rstest]
994    fn test_parse_decimal_invalid() {
995        assert!(parse_decimal("invalid").is_err());
996        assert!(parse_decimal("").is_err());
997    }
998
999    #[rstest]
1000    #[case(&Some("123.45".to_string()), Some(dec!(123.45)))]
1001    #[case(&Some("0".to_string()), Some(Decimal::ZERO))]
1002    #[case(&Some(String::new()), None)]
1003    #[case(&None, None)]
1004    fn test_parse_optional_decimal(
1005        #[case] input: &Option<String>,
1006        #[case] expected: Option<Decimal>,
1007    ) {
1008        let result = parse_optional_decimal(input).unwrap();
1009        assert_eq!(result, expected);
1010    }
1011
1012    // Tests for flexible decimal deserializers (handles both string and number JSON values)
1013
1014    #[derive(Debug, Serialize, Deserialize, PartialEq)]
1015    struct TestFlexibleDecimal {
1016        #[serde(
1017            serialize_with = "serialize_decimal",
1018            deserialize_with = "deserialize_decimal"
1019        )]
1020        value: Decimal,
1021        #[serde(
1022            serialize_with = "serialize_optional_decimal",
1023            deserialize_with = "deserialize_optional_decimal"
1024        )]
1025        optional_value: Option<Decimal>,
1026    }
1027
1028    #[rstest]
1029    #[case(r#"{"value": 123.456, "optional_value": 789.012}"#, dec!(123.456), Some(dec!(789.012)))]
1030    #[case(r#"{"value": "123.456", "optional_value": "789.012"}"#, dec!(123.456), Some(dec!(789.012)))]
1031    #[case(r#"{"value": 100, "optional_value": null}"#, dec!(100), None)]
1032    #[case(r#"{"value": null, "optional_value": null}"#, Decimal::ZERO, None)]
1033    fn test_deserialize_flexible_decimal(
1034        #[case] json: &str,
1035        #[case] expected_value: Decimal,
1036        #[case] expected_optional: Option<Decimal>,
1037    ) {
1038        let result: TestFlexibleDecimal = serde_json::from_str(json).unwrap();
1039        assert_eq!(result.value, expected_value);
1040        assert_eq!(result.optional_value, expected_optional);
1041    }
1042
1043    #[rstest]
1044    fn test_flexible_decimal_roundtrip() {
1045        let original = TestFlexibleDecimal {
1046            value: dec!(123.456),
1047            optional_value: Some(dec!(789.012)),
1048        };
1049
1050        let json = serde_json::to_string(&original).unwrap();
1051        let deserialized: TestFlexibleDecimal = serde_json::from_str(&json).unwrap();
1052
1053        assert_eq!(original.value, deserialized.value);
1054        assert_eq!(original.optional_value, deserialized.optional_value);
1055    }
1056
1057    #[rstest]
1058    fn test_flexible_decimal_scientific_notation() {
1059        // Test that scientific notation from serde_json is handled correctly.
1060        // serde_json outputs very small numbers like 0.00000001 as "1e-8".
1061        // Note: JSON numbers are parsed as f64, so values are limited to ~15 significant digits.
1062        let json = r#"{"value": 0.00000001, "optional_value": 12345678.12345}"#;
1063        let parsed: TestFlexibleDecimal = serde_json::from_str(json).unwrap();
1064        assert_eq!(parsed.value, dec!(0.00000001));
1065        assert_eq!(parsed.optional_value, Some(dec!(12345678.12345)));
1066    }
1067
1068    #[rstest]
1069    fn test_flexible_decimal_empty_string_optional() {
1070        let json = r#"{"value": 100, "optional_value": ""}"#;
1071        let parsed: TestFlexibleDecimal = serde_json::from_str(json).unwrap();
1072        assert_eq!(parsed.value, dec!(100));
1073        assert_eq!(parsed.optional_value, None);
1074    }
1075
1076    // Additional tests for DecimalVisitor edge cases
1077
1078    #[derive(Debug, Deserialize)]
1079    struct TestDecimalOnly {
1080        #[serde(deserialize_with = "deserialize_decimal")]
1081        value: Decimal,
1082    }
1083
1084    #[rstest]
1085    #[case(r#"{"value": "1.5e-8"}"#, dec!(0.000000015))]
1086    #[case(r#"{"value": "1E10"}"#, dec!(10000000000))]
1087    #[case(r#"{"value": "-1.23e5"}"#, dec!(-123000))]
1088    fn test_deserialize_decimal_scientific_string(#[case] json: &str, #[case] expected: Decimal) {
1089        let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1090        assert_eq!(result.value, expected);
1091    }
1092
1093    #[rstest]
1094    #[case(r#"{"value": 9223372036854775807}"#, dec!(9223372036854775807))] // i64::MAX
1095    #[case(r#"{"value": -9223372036854775808}"#, dec!(-9223372036854775808))] // i64::MIN
1096    #[case(r#"{"value": 0}"#, Decimal::ZERO)]
1097    fn test_deserialize_decimal_large_integers(#[case] json: &str, #[case] expected: Decimal) {
1098        let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1099        assert_eq!(result.value, expected);
1100    }
1101
1102    #[rstest]
1103    #[case(r#"{"value": "-123.456789"}"#, dec!(-123.456789))]
1104    #[case(r#"{"value": -999.99}"#, dec!(-999.99))]
1105    fn test_deserialize_decimal_negative(#[case] json: &str, #[case] expected: Decimal) {
1106        let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1107        assert_eq!(result.value, expected);
1108    }
1109
1110    #[rstest]
1111    #[case(r#"{"value": "123456789.123456789012345678"}"#)] // High precision string
1112    fn test_deserialize_decimal_high_precision(#[case] json: &str) {
1113        let result: TestDecimalOnly = serde_json::from_str(json).unwrap();
1114        assert_eq!(result.value, dec!(123456789.123456789012345678));
1115    }
1116
1117    #[derive(Debug, Deserialize)]
1118    struct TestOptionalDecimalOnly {
1119        #[serde(deserialize_with = "deserialize_optional_decimal")]
1120        value: Option<Decimal>,
1121    }
1122
1123    #[rstest]
1124    #[case(r#"{"value": "1.5e-8"}"#, Some(dec!(0.000000015)))]
1125    #[case(r#"{"value": null}"#, None)]
1126    #[case(r#"{"value": ""}"#, None)]
1127    #[case(r#"{"value": 42}"#, Some(dec!(42)))]
1128    #[case(r#"{"value": -100.5}"#, Some(dec!(-100.5)))]
1129    fn test_deserialize_optional_decimal_various(
1130        #[case] json: &str,
1131        #[case] expected: Option<Decimal>,
1132    ) {
1133        let result: TestOptionalDecimalOnly = serde_json::from_str(json).unwrap();
1134        assert_eq!(result.value, expected);
1135    }
1136}