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