Skip to main content

reliakit_json/
encode.rs

1//! Typed JSON encoding: turn a Rust value into a [`JsonValue`].
2//!
3//! [`JsonEncode`] is the encode half of typed JSON serialization. It is
4//! deliberately small: a value produces a [`JsonValue`], and the existing
5//! [`to_compact_string`](crate::to_compact_string) writer turns that into
6//! deterministic, compact JSON text. Encoding never fails — every supported
7//! value has a JSON representation.
8
9use alloc::string::{String, ToString};
10use alloc::vec::Vec;
11
12use crate::number::JsonNumber;
13use crate::value::JsonValue;
14use crate::write::{to_compact_string, to_compact_vec};
15
16/// A type that can be encoded into a [`JsonValue`].
17///
18/// Pair with [`to_json_string`] for the canonical compact text, or with
19/// [`JsonDecode`](crate::JsonDecode) to round-trip. The derive in
20/// `reliakit-derive` generates implementations of this trait.
21pub trait JsonEncode {
22    /// Encodes `self` into a [`JsonValue`].
23    fn to_json_value(&self) -> JsonValue;
24}
25
26/// Encodes a value to compact, deterministic JSON text.
27pub fn to_json_string<T: JsonEncode + ?Sized>(value: &T) -> String {
28    to_compact_string(&value.to_json_value())
29}
30
31/// Encodes a value to compact, deterministic JSON bytes.
32pub fn to_json_vec<T: JsonEncode + ?Sized>(value: &T) -> Vec<u8> {
33    to_compact_vec(&value.to_json_value())
34}
35
36macro_rules! impl_int {
37    ($($t:ty),* $(,)?) => {$(
38        impl JsonEncode for $t {
39            fn to_json_value(&self) -> JsonValue {
40                // A decimal integer literal is always a valid JSON number.
41                JsonValue::Number(JsonNumber::from_validated(self.to_string()))
42            }
43        }
44    )*};
45}
46impl_int!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);
47
48impl JsonEncode for bool {
49    fn to_json_value(&self) -> JsonValue {
50        JsonValue::Bool(*self)
51    }
52}
53
54impl JsonEncode for str {
55    fn to_json_value(&self) -> JsonValue {
56        JsonValue::String(self.into())
57    }
58}
59
60impl JsonEncode for String {
61    fn to_json_value(&self) -> JsonValue {
62        JsonValue::String(self.clone())
63    }
64}
65
66impl<T: JsonEncode> JsonEncode for Option<T> {
67    fn to_json_value(&self) -> JsonValue {
68        match self {
69            Some(value) => value.to_json_value(),
70            None => JsonValue::Null,
71        }
72    }
73}
74
75impl<T: JsonEncode> JsonEncode for Vec<T> {
76    fn to_json_value(&self) -> JsonValue {
77        JsonValue::Array(self.iter().map(JsonEncode::to_json_value).collect())
78    }
79}
80
81impl<T: JsonEncode> JsonEncode for [T] {
82    fn to_json_value(&self) -> JsonValue {
83        JsonValue::Array(self.iter().map(JsonEncode::to_json_value).collect())
84    }
85}
86
87impl<T: JsonEncode + ?Sized> JsonEncode for &T {
88    fn to_json_value(&self) -> JsonValue {
89        (**self).to_json_value()
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use crate::value::JsonObject;
97
98    #[test]
99    fn scalars_encode_to_exact_text() {
100        assert_eq!(to_json_string(&255u8), "255");
101        assert_eq!(to_json_string(&(-5i32)), "-5");
102        assert_eq!(
103            to_json_string(&u128::MAX),
104            "340282366920938463463374607431768211455"
105        );
106        assert_eq!(to_json_string(&true), "true");
107        assert_eq!(to_json_string(&false), "false");
108        assert_eq!(to_json_string("hi"), "\"hi\"");
109        assert_eq!(to_json_string(&String::from("hi")), "\"hi\"");
110    }
111
112    #[test]
113    fn option_encodes_none_as_null() {
114        assert_eq!(to_json_string(&Option::<u8>::None), "null");
115        assert_eq!(to_json_string(&Some(7u8)), "7");
116    }
117
118    #[test]
119    fn sequences_encode_to_arrays() {
120        assert_eq!(to_json_string(&vec![1u8, 2, 3]), "[1,2,3]");
121        assert_eq!(to_json_string(&Vec::<u8>::new()), "[]");
122        let slice: &[u8] = &[9, 8];
123        assert_eq!(to_json_string(slice), "[9,8]");
124    }
125
126    #[test]
127    fn object_member_order_is_preserved() {
128        // The writer keeps insertion order; encoders rely on this for stable
129        // field ordering.
130        let mut obj = JsonObject::new();
131        obj.insert("b".into(), 2u8.to_json_value());
132        obj.insert("a".into(), 1u8.to_json_value());
133        assert_eq!(
134            to_compact_string(&JsonValue::Object(obj)),
135            r#"{"b":2,"a":1}"#
136        );
137    }
138
139    #[test]
140    fn array_constructor_and_reference_forwarding() {
141        let array = JsonValue::array([1u8.to_json_value(), 2u8.to_json_value()]);
142        assert_eq!(to_compact_string(&array), "[1,2]");
143
144        // The `&T` blanket impl forwards to the inner value.
145        let n = 5u8;
146        assert_eq!(to_json_string(&&n), "5");
147    }
148}