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