Preview of the proposed up-rust native-frame-model branch (up-rust b6b99c6d, up-spec f0e9b17) — not released documentation. branch · write-up

up_rust/
protobuf_mappable.rs

1/********************************************************************************
2 * Copyright (c) 2023 Contributors to the Eclipse Foundation
3 *
4 * See the NOTICE file(s) distributed with this work for additional
5 * information regarding copyright ownership.
6 *
7 * This program and the accompanying materials are made available under the
8 * terms of the Apache License Version 2.0 which is available at
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * SPDX-License-Identifier: Apache-2.0
12 ********************************************************************************/
13
14use super::SerializationError;
15use protobuf::{well_known_types::any::Any, Message, MessageFull};
16
17/// *Role: standalone utility letting protobuf-generated types ride as payloads implicitly; blanket-implemented for `protobuf` types — see the [trait map](crate::guide::trait_map).*
18///
19/// A type that can be mapped to and from a protobuf.
20pub trait ProtobufMappable: Sized {
21    /// Parses an instance of this type from the given protobuf bytes.
22    ///
23    /// # Arguments
24    ///
25    /// * `proto` - The protobuf message.
26    ///
27    /// # Errors
28    ///
29    /// Returns an error if the given bytes cannot be parsed as a protobuf.
30    fn parse_from_protobuf_bytes(proto: &[u8]) -> Result<Self, SerializationError>;
31    /// Parses an instance of this type from the given packed protobuf bytes.
32    ///
33    /// # Arguments
34    ///
35    /// * `proto` - The protobuf _Any_ message containing the actual message packed inside.
36    ///
37    /// # Errors
38    ///
39    /// Returns an error if the given bytes cannot be parsed to a protobuf _Any_ message
40    /// or if the contained message cannot be unpacked to the expected type.
41    fn parse_from_packed_protobuf_bytes(proto: &[u8]) -> Result<Self, SerializationError>;
42    /// Serializes this instance to protobuf bytes.
43    ///
44    /// # Errors
45    ///
46    /// Returns an error if this instance cannot be serialized to protobuf bytes.
47    fn write_to_protobuf_bytes(&self) -> Result<Vec<u8>, SerializationError>;
48    /// Serializes this instance to packed protobuf bytes.
49    ///
50    /// # Errors
51    ///
52    /// Returns an error if this instance cannot be serialized to packed protobuf bytes.
53    fn write_to_packed_protobuf_bytes(&self) -> Result<Vec<u8>, SerializationError>;
54}
55
56/// Blanket implementation for all types that have been generated by the
57/// [protobuf](https://crates.io/crates/protobuf) crate.
58impl<T: MessageFull> ProtobufMappable for T {
59    fn parse_from_protobuf_bytes(proto: &[u8]) -> Result<Self, SerializationError> {
60        Ok(T::parse_from_bytes(proto)?)
61    }
62
63    fn parse_from_packed_protobuf_bytes(proto: &[u8]) -> Result<Self, SerializationError> {
64        let any = Any::parse_from_bytes(proto)?;
65        match any.unpack() {
66            Ok(Some(v)) => Ok(v),
67            Ok(None) => Err(SerializationError::new(
68                "cannot unpack protobuf, type mismatch".to_string(),
69            )),
70            Err(e) => Err(SerializationError::from(e)),
71        }
72    }
73
74    fn write_to_protobuf_bytes(&self) -> Result<Vec<u8>, SerializationError> {
75        T::write_to_bytes(self).map_err(SerializationError::from)
76    }
77
78    fn write_to_packed_protobuf_bytes(&self) -> Result<Vec<u8>, SerializationError> {
79        Any::pack(self)
80            .and_then(|any| any.write_to_bytes())
81            .map_err(SerializationError::from)
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88    use protobuf::well_known_types::{timestamp::Timestamp, wrappers::StringValue};
89
90    #[test]
91    fn test_write_to_protobuf_bytes_works_for_message_full() {
92        let payload = StringValue {
93            value: "hello".to_string(),
94            ..Default::default()
95        };
96        let bytes = payload.write_to_protobuf_bytes().unwrap();
97        let deserialized = StringValue::parse_from_protobuf_bytes(&bytes).unwrap();
98        assert_eq!(deserialized.value, "hello");
99    }
100
101    #[test]
102    fn test_write_to_packed_protobuf_bytes_works_for_message_full() {
103        let payload = StringValue {
104            value: "hello".to_string(),
105            ..Default::default()
106        };
107        let bytes = payload.write_to_packed_protobuf_bytes().unwrap();
108        let deserialized = StringValue::parse_from_packed_protobuf_bytes(&bytes).unwrap();
109        assert_eq!(deserialized.value, "hello");
110    }
111
112    #[test]
113    fn test_parse_from_packed_protobuf_bytes_fails_for_wrong_message_type() {
114        let payload = StringValue {
115            value: "hello".to_string(),
116            ..Default::default()
117        };
118        let bytes = payload.write_to_packed_protobuf_bytes().unwrap();
119        assert!(Timestamp::parse_from_packed_protobuf_bytes(&bytes)
120            .is_err_and(|e| { matches!(e, SerializationError { .. }) }));
121    }
122}