Skip to main content

buffa_types/
wrapper_ext.rs

1//! Ergonomic `From`/`Into` impls for the `google.protobuf` wrapper types.
2//!
3//! Each wrapper message has a single `value` field.  These impls allow seamless
4//! conversion between the wrapper message and the underlying primitive.
5
6use alloc::string::{String, ToString};
7use alloc::vec::Vec;
8
9use crate::google::protobuf::{
10    BoolValue, BytesValue, DoubleValue, FloatValue, Int32Value, Int64Value, StringValue,
11    UInt32Value, UInt64Value,
12};
13
14macro_rules! impl_wrapper {
15    ($wrapper:ty, $inner:ty) => {
16        impl From<$inner> for $wrapper {
17            #[doc = concat!("Wraps `", stringify!($inner), "` in [`", stringify!($wrapper), "`].")]
18            fn from(v: $inner) -> Self {
19                Self {
20                    value: v,
21                    ..Default::default()
22                }
23            }
24        }
25
26        impl From<$wrapper> for $inner {
27            #[doc = concat!("Extracts the inner `", stringify!($inner), "` from [`", stringify!($wrapper), "`].")]
28            fn from(w: $wrapper) -> Self {
29                w.value
30            }
31        }
32    };
33}
34
35impl_wrapper!(BoolValue, bool);
36impl_wrapper!(DoubleValue, f64);
37impl_wrapper!(FloatValue, f32);
38impl_wrapper!(Int32Value, i32);
39impl_wrapper!(Int64Value, i64);
40impl_wrapper!(UInt32Value, u32);
41impl_wrapper!(UInt64Value, u64);
42impl_wrapper!(StringValue, String);
43impl_wrapper!(BytesValue, Vec<u8>);
44
45impl From<&str> for StringValue {
46    /// Converts a string slice into a [`StringValue`], allocating a new `String`.
47    fn from(s: &str) -> Self {
48        StringValue {
49            value: s.to_string(),
50            ..Default::default()
51        }
52    }
53}
54
55impl From<&[u8]> for BytesValue {
56    /// Converts a byte slice into a [`BytesValue`], copying the bytes.
57    fn from(b: &[u8]) -> Self {
58        BytesValue {
59            value: b.to_vec(),
60            ..Default::default()
61        }
62    }
63}
64
65impl AsRef<str> for StringValue {
66    /// Borrows the inner string slice, allowing `StringValue` to be passed to
67    /// any function that accepts `&str` (e.g. via `.as_ref()`).
68    fn as_ref(&self) -> &str {
69        &self.value
70    }
71}
72
73impl AsRef<[u8]> for BytesValue {
74    /// Borrows the inner byte slice, allowing `BytesValue` to be passed to
75    /// any function that accepts `&[u8]` (e.g. via `.as_ref()`).
76    fn as_ref(&self) -> &[u8] {
77        &self.value
78    }
79}
80
81// ── serde impls ──────────────────────────────────────────────────────────────
82
83/// Macro for wrapper types whose inner type's default serde is correct for
84/// proto JSON (bool, i32, u32, String).
85macro_rules! impl_wrapper_serde_simple {
86    ($wrapper:ty, $inner:ty) => {
87        #[cfg(feature = "json")]
88        impl serde::Serialize for $wrapper {
89            fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
90                serde::Serialize::serialize(&self.value, s)
91            }
92        }
93
94        #[cfg(feature = "json")]
95        impl<'de> serde::Deserialize<'de> for $wrapper {
96            fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
97                <$inner as serde::Deserialize>::deserialize(d).map(Self::from)
98            }
99        }
100    };
101}
102
103impl_wrapper_serde_simple!(BoolValue, bool);
104impl_wrapper_serde_simple!(Int32Value, i32);
105impl_wrapper_serde_simple!(UInt32Value, u32);
106impl_wrapper_serde_simple!(StringValue, String);
107
108// Int64Value: quoted decimal string per proto3 JSON spec.
109#[cfg(feature = "json")]
110impl serde::Serialize for Int64Value {
111    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
112        buffa::json_helpers::int64::serialize(&self.value, s)
113    }
114}
115
116#[cfg(feature = "json")]
117impl<'de> serde::Deserialize<'de> for Int64Value {
118    fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
119        buffa::json_helpers::int64::deserialize(d).map(Self::from)
120    }
121}
122
123// UInt64Value: quoted decimal string per proto3 JSON spec.
124#[cfg(feature = "json")]
125impl serde::Serialize for UInt64Value {
126    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
127        buffa::json_helpers::uint64::serialize(&self.value, s)
128    }
129}
130
131#[cfg(feature = "json")]
132impl<'de> serde::Deserialize<'de> for UInt64Value {
133    fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
134        buffa::json_helpers::uint64::deserialize(d).map(Self::from)
135    }
136}
137
138// FloatValue: number, or "NaN" / "Infinity" / "-Infinity" per proto3 JSON spec.
139#[cfg(feature = "json")]
140impl serde::Serialize for FloatValue {
141    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
142        buffa::json_helpers::float::serialize(&self.value, s)
143    }
144}
145
146#[cfg(feature = "json")]
147impl<'de> serde::Deserialize<'de> for FloatValue {
148    fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
149        buffa::json_helpers::float::deserialize(d).map(Self::from)
150    }
151}
152
153// DoubleValue: number, or "NaN" / "Infinity" / "-Infinity" per proto3 JSON spec.
154#[cfg(feature = "json")]
155impl serde::Serialize for DoubleValue {
156    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
157        buffa::json_helpers::double::serialize(&self.value, s)
158    }
159}
160
161#[cfg(feature = "json")]
162impl<'de> serde::Deserialize<'de> for DoubleValue {
163    fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
164        buffa::json_helpers::double::deserialize(d).map(Self::from)
165    }
166}
167
168// BytesValue: base64-encoded string per proto3 JSON spec.
169#[cfg(feature = "json")]
170impl serde::Serialize for BytesValue {
171    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
172        buffa::json_helpers::bytes::serialize(&self.value, s)
173    }
174}
175
176#[cfg(feature = "json")]
177impl<'de> serde::Deserialize<'de> for BytesValue {
178    fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
179        // The helper is generic over T: From<Vec<u8>>; pin T = Vec<u8> since
180        // BytesValue has multiple From impls that would otherwise be ambiguous.
181        buffa::json_helpers::bytes::deserialize::<Vec<u8>, _>(d).map(Self::from)
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    #[test]
190    fn bool_value_roundtrip() {
191        let w = BoolValue::from(true);
192        assert!(w.value);
193        let back: bool = w.into();
194        assert!(back);
195    }
196
197    #[test]
198    fn double_value_roundtrip() {
199        let w = DoubleValue::from(3.14_f64);
200        assert_eq!(w.value, 3.14);
201        let back: f64 = w.into();
202        assert_eq!(back, 3.14);
203    }
204
205    #[test]
206    fn float_value_roundtrip() {
207        let w = FloatValue::from(1.5_f32);
208        assert_eq!(w.value, 1.5);
209        let back: f32 = w.into();
210        assert_eq!(back, 1.5);
211    }
212
213    #[test]
214    fn int32_value_roundtrip() {
215        let w = Int32Value::from(-42_i32);
216        assert_eq!(w.value, -42);
217        let back: i32 = w.into();
218        assert_eq!(back, -42);
219    }
220
221    #[test]
222    fn int64_value_roundtrip() {
223        let w = Int64Value::from(i64::MAX);
224        assert_eq!(w.value, i64::MAX);
225        let back: i64 = w.into();
226        assert_eq!(back, i64::MAX);
227    }
228
229    #[test]
230    fn uint32_value_roundtrip() {
231        let w = UInt32Value::from(100_u32);
232        assert_eq!(w.value, 100);
233        let back: u32 = w.into();
234        assert_eq!(back, 100);
235    }
236
237    #[test]
238    fn uint64_value_roundtrip() {
239        let w = UInt64Value::from(u64::MAX);
240        assert_eq!(w.value, u64::MAX);
241        let back: u64 = w.into();
242        assert_eq!(back, u64::MAX);
243    }
244
245    #[test]
246    fn string_value_roundtrip() {
247        let w = StringValue::from("hello".to_string());
248        assert_eq!(w.value, "hello");
249        let back: String = w.into();
250        assert_eq!(back, "hello");
251    }
252
253    #[test]
254    fn string_value_from_str_ref() {
255        let w = StringValue::from("hello");
256        assert_eq!(w.value, "hello");
257    }
258
259    #[test]
260    fn string_value_as_ref_str() {
261        let w = StringValue::from("world".to_string());
262        // AsRef<str> allows passing to functions that accept &str.
263        fn takes_str(s: &str) -> usize {
264            s.len()
265        }
266        assert_eq!(takes_str(w.as_ref()), 5);
267    }
268
269    #[test]
270    fn bytes_value_roundtrip() {
271        let w = BytesValue::from(vec![1_u8, 2, 3]);
272        assert_eq!(w.value, vec![1, 2, 3]);
273        let back: Vec<u8> = w.into();
274        assert_eq!(back, vec![1, 2, 3]);
275    }
276
277    #[test]
278    fn bytes_value_from_slice() {
279        let w = BytesValue::from(&[4_u8, 5, 6][..]);
280        assert_eq!(w.value, vec![4, 5, 6]);
281    }
282
283    #[test]
284    fn bytes_value_as_ref_u8_slice() {
285        let w = BytesValue::from(vec![10_u8, 20, 30]);
286        fn takes_bytes(b: &[u8]) -> usize {
287            b.len()
288        }
289        assert_eq!(takes_bytes(w.as_ref()), 3);
290    }
291
292    #[cfg(feature = "json")]
293    mod serde_tests {
294        use super::*;
295
296        #[test]
297        fn bool_value_serde_roundtrip() {
298            let w = BoolValue::from(true);
299            let json = serde_json::to_string(&w).unwrap();
300            assert_eq!(json, "true");
301            let back: BoolValue = serde_json::from_str(&json).unwrap();
302            assert!(back.value);
303        }
304
305        #[test]
306        fn int32_value_serde_roundtrip() {
307            let w = Int32Value::from(-42_i32);
308            let json = serde_json::to_string(&w).unwrap();
309            assert_eq!(json, "-42");
310            let back: Int32Value = serde_json::from_str(&json).unwrap();
311            assert_eq!(back.value, -42);
312        }
313
314        #[test]
315        fn uint32_value_serde_roundtrip() {
316            let w = UInt32Value::from(100_u32);
317            let json = serde_json::to_string(&w).unwrap();
318            assert_eq!(json, "100");
319            let back: UInt32Value = serde_json::from_str(&json).unwrap();
320            assert_eq!(back.value, 100);
321        }
322
323        #[test]
324        fn string_value_serde_roundtrip() {
325            let w = StringValue::from("hello".to_string());
326            let json = serde_json::to_string(&w).unwrap();
327            assert_eq!(json, r#""hello""#);
328            let back: StringValue = serde_json::from_str(&json).unwrap();
329            assert_eq!(back.value, "hello");
330        }
331
332        #[test]
333        fn int64_value_serializes_as_quoted_string() {
334            let w = Int64Value::from(i64::MAX);
335            let json = serde_json::to_string(&w).unwrap();
336            assert_eq!(json, r#""9223372036854775807""#);
337            let back: Int64Value = serde_json::from_str(&json).unwrap();
338            assert_eq!(back.value, i64::MAX);
339        }
340
341        #[test]
342        fn uint64_value_serializes_as_quoted_string() {
343            let w = UInt64Value::from(u64::MAX);
344            let json = serde_json::to_string(&w).unwrap();
345            assert_eq!(json, r#""18446744073709551615""#);
346            let back: UInt64Value = serde_json::from_str(&json).unwrap();
347            assert_eq!(back.value, u64::MAX);
348        }
349
350        #[test]
351        fn float_value_serde_roundtrip() {
352            let w = FloatValue::from(1.5_f32);
353            let json = serde_json::to_string(&w).unwrap();
354            let back: FloatValue = serde_json::from_str(&json).unwrap();
355            assert_eq!(back.value, 1.5_f32);
356        }
357
358        #[test]
359        fn float_value_nan_as_string() {
360            let w = FloatValue::from(f32::NAN);
361            let json = serde_json::to_string(&w).unwrap();
362            assert_eq!(json, r#""NaN""#);
363        }
364
365        #[test]
366        fn double_value_serde_roundtrip() {
367            let w = DoubleValue::from(3.14_f64);
368            let json = serde_json::to_string(&w).unwrap();
369            let back: DoubleValue = serde_json::from_str(&json).unwrap();
370            assert!((back.value - 3.14).abs() < 1e-10);
371        }
372
373        #[test]
374        fn double_value_infinity_as_string() {
375            let w = DoubleValue::from(f64::INFINITY);
376            let json = serde_json::to_string(&w).unwrap();
377            assert_eq!(json, r#""Infinity""#);
378        }
379
380        #[test]
381        fn bytes_value_serde_as_base64() {
382            let w = BytesValue::from(vec![1_u8, 2, 3]);
383            let json = serde_json::to_string(&w).unwrap();
384            // base64 of [1, 2, 3] is "AQID"
385            assert_eq!(json, r#""AQID""#);
386            let back: BytesValue = serde_json::from_str(&json).unwrap();
387            assert_eq!(back.value, vec![1_u8, 2, 3]);
388        }
389    }
390}