nautilus_core/
serialization.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Posei Systems Pty Ltd. All rights reserved.
3//  https://poseitrader.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
18use bytes::Bytes;
19use serde::{
20    Deserializer,
21    de::{Unexpected, Visitor},
22};
23
24struct BoolVisitor;
25use serde::{Deserialize, Serialize};
26
27/// Represents types which are serializable for JSON and `MsgPack` specifications.
28pub trait Serializable: Serialize + for<'de> Deserialize<'de> {
29    /// Deserialize an object from JSON encoded bytes.
30    ///
31    /// # Errors
32    ///
33    /// Returns serialization errors.
34    fn from_json_bytes(data: &[u8]) -> Result<Self, serde_json::Error> {
35        serde_json::from_slice(data)
36    }
37
38    /// Deserialize an object from `MsgPack` encoded bytes.
39    ///
40    /// # Errors
41    ///
42    /// Returns serialization errors.
43    fn from_msgpack_bytes(data: &[u8]) -> Result<Self, rmp_serde::decode::Error> {
44        rmp_serde::from_slice(data)
45    }
46
47    /// Serialize an object to JSON encoded bytes.
48    ///
49    /// # Errors
50    ///
51    /// Returns serialization errors.
52    fn to_json_bytes(&self) -> Result<Bytes, serde_json::Error> {
53        serde_json::to_vec(self).map(Bytes::from)
54    }
55
56    /// Serialize an object to `MsgPack` encoded bytes.
57    ///
58    /// # Errors
59    ///
60    /// Returns serialization errors.
61    fn to_msgpack_bytes(&self) -> Result<Bytes, rmp_serde::encode::Error> {
62        rmp_serde::to_vec_named(self).map(Bytes::from)
63    }
64}
65
66impl Visitor<'_> for BoolVisitor {
67    type Value = u8;
68
69    fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        formatter.write_str("a boolean as u8")
71    }
72
73    fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
74    where
75        E: serde::de::Error,
76    {
77        Ok(u8::from(value))
78    }
79
80    #[allow(clippy::cast_possible_truncation)]
81    fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
82    where
83        E: serde::de::Error,
84    {
85        // Only 0 or 1 are considered valid representations when provided as an
86        // integer. We deliberately reject values outside this range to avoid
87        // silently truncating larger integers into impl-defined boolean
88        // semantics.
89        if value > 1 {
90            Err(E::invalid_value(Unexpected::Unsigned(value), &self))
91        } else {
92            Ok(value as u8)
93        }
94    }
95}
96
97/// Deserialize the boolean value as a `u8`.
98///
99/// # Errors
100///
101/// Returns serialization errors.
102pub fn from_bool_as_u8<'de, D>(deserializer: D) -> Result<u8, D::Error>
103where
104    D: Deserializer<'de>,
105{
106    deserializer.deserialize_any(BoolVisitor)
107}
108
109////////////////////////////////////////////////////////////////////////////////
110// Tests
111////////////////////////////////////////////////////////////////////////////////
112#[cfg(test)]
113mod tests {
114    use rstest::*;
115    use serde::Deserialize;
116
117    use super::from_bool_as_u8;
118
119    #[derive(Deserialize)]
120    pub struct TestStruct {
121        #[serde(deserialize_with = "from_bool_as_u8")]
122        pub value: u8,
123    }
124
125    #[rstest]
126    #[case(r#"{"value": true}"#, 1)]
127    #[case(r#"{"value": false}"#, 0)]
128    fn test_deserialize_bool_as_u8_with_boolean(#[case] json_str: &str, #[case] expected: u8) {
129        let test_struct: TestStruct = serde_json::from_str(json_str).unwrap();
130        assert_eq!(test_struct.value, expected);
131    }
132
133    #[rstest]
134    #[case(r#"{"value": 1}"#, 1)]
135    #[case(r#"{"value": 0}"#, 0)]
136    fn test_deserialize_bool_as_u8_with_u64(#[case] json_str: &str, #[case] expected: u8) {
137        let test_struct: TestStruct = serde_json::from_str(json_str).unwrap();
138        assert_eq!(test_struct.value, expected);
139    }
140
141    #[rstest]
142    fn test_deserialize_bool_as_u8_with_invalid_integer() {
143        // Any integer other than 0/1 is invalid and should error
144        let json = r#"{"value": 2}"#;
145        let result: Result<TestStruct, _> = serde_json::from_str(json);
146        assert!(result.is_err());
147    }
148}