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