up_rust/uattributes/
upayloadformat.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 crate::up_core_api::uattributes::UPayloadFormat;
15use mediatype::MediaType;
16use protobuf::EnumFull;
17
18#[derive(Debug)]
19pub enum UPayloadError {
20    SerializationError(String),
21    MediatypeProblem,
22}
23
24impl UPayloadError {
25    pub fn serialization_error<T>(message: T) -> UPayloadError
26    where
27        T: Into<String>,
28    {
29        Self::SerializationError(message.into())
30    }
31
32    pub fn mediatype_error() -> UPayloadError {
33        Self::MediatypeProblem
34    }
35}
36
37impl std::fmt::Display for UPayloadError {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        match self {
40            Self::SerializationError(e) => f.write_fmt(format_args!("Serialization error: {}", e)),
41            Self::MediatypeProblem => {
42                f.write_fmt(format_args!("Mediatype problem unsupported or malformed"))
43            }
44        }
45    }
46}
47
48impl std::error::Error for UPayloadError {}
49
50impl UPayloadFormat {
51    /// Gets the payload format that corresponds to a given media type.
52    ///
53    /// # Errors
54    ///
55    /// Returns an error if the given string is not a valid media type string or is unsupported by uProtocol.
56    ///
57    /// # Examples
58    ///
59    /// ```rust
60    /// use up_rust::UPayloadFormat;
61    ///
62    /// let parse_attempt = UPayloadFormat::from_media_type("application/json; charset=utf-8");
63    /// assert!(parse_attempt.is_ok());
64    /// assert_eq!(parse_attempt.unwrap(), UPayloadFormat::UPAYLOAD_FORMAT_JSON);
65    ///
66    /// let parse_attempt = UPayloadFormat::from_media_type("application/unsupported");
67    /// assert!(parse_attempt.is_err());
68    /// ```
69    pub fn from_media_type(media_type_string: &str) -> Result<Self, UPayloadError> {
70        if let Ok(media_type) = MediaType::parse(media_type_string) {
71            let descriptor = UPayloadFormat::enum_descriptor();
72            return descriptor
73                .values()
74                .find_map(|desc| {
75                    let proto_desc = desc.proto();
76
77                    crate::up_core_api::uoptions::exts::mime_type
78                        .get(proto_desc.options.get_or_default())
79                        .and_then(|mime_type_option_value| {
80                            if let Ok(enum_mime_type) = MediaType::parse(&mime_type_option_value) {
81                                if enum_mime_type.ty == media_type.ty
82                                    && enum_mime_type.subty == media_type.subty
83                                {
84                                    return desc.cast::<Self>();
85                                }
86                            }
87                            None
88                        })
89                })
90                .ok_or(UPayloadError::mediatype_error());
91        }
92        Err(UPayloadError::mediatype_error())
93    }
94
95    /// Gets the media type corresponding to this payload format.
96    ///
97    /// # Returns
98    ///
99    /// None if the payload format is [`UPayloadFormat::UPAYLOAD_FORMAT_UNSPECIFIED`].
100    ///
101    /// # Examples
102    ///
103    /// ```rust
104    /// use up_rust::UPayloadFormat;
105    ///
106    /// assert_eq!(UPayloadFormat::UPAYLOAD_FORMAT_JSON.to_media_type().unwrap(), "application/json");
107    /// assert!(UPayloadFormat::UPAYLOAD_FORMAT_UNSPECIFIED.to_media_type().is_none());
108    /// ```
109    pub fn to_media_type(self) -> Option<String> {
110        let desc = self.descriptor();
111        let desc_proto = desc.proto();
112        crate::up_core_api::uoptions::exts::mime_type.get(desc_proto.options.get_or_default())
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    use test_case::test_case;
121
122    #[test_case("application/json", Ok(UPayloadFormat::UPAYLOAD_FORMAT_JSON); "map from JSON")]
123    #[test_case(
124        "application/json; charset=utf-8",
125        Ok(UPayloadFormat::UPAYLOAD_FORMAT_JSON);
126        "map from JSON with parameter"
127    )]
128    #[test_case("application/protobuf", Ok(UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF); "map from PROTOBUF")]
129    #[test_case(
130        "application/x-protobuf",
131        Ok(UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY); "map from PROTOBUF_WRAPPED"
132    )]
133    #[test_case("application/octet-stream", Ok(UPayloadFormat::UPAYLOAD_FORMAT_RAW); "map from RAW")]
134    #[test_case("application/x-someip", Ok(UPayloadFormat::UPAYLOAD_FORMAT_SOMEIP); "map from SOMEIP")]
135    #[test_case(
136        "application/x-someip_tlv",
137        Ok(UPayloadFormat::UPAYLOAD_FORMAT_SOMEIP_TLV); "map from SOMEIP_TLV"
138    )]
139    #[test_case("text/plain", Ok(UPayloadFormat::UPAYLOAD_FORMAT_TEXT); "map from TEXT")]
140    #[test_case("application/unsupported; foo=bar", Err(UPayloadError::mediatype_error()); "fail for unsupported media type")]
141    fn test_from_media_type(
142        media_type: &str,
143        expected_format: Result<UPayloadFormat, UPayloadError>,
144    ) {
145        let parsing_result = UPayloadFormat::from_media_type(media_type);
146        assert!(parsing_result.is_ok() == expected_format.is_ok());
147        if let Ok(format) = expected_format {
148            assert_eq!(format, parsing_result.unwrap());
149        }
150    }
151
152    #[test_case(UPayloadFormat::UPAYLOAD_FORMAT_JSON, Some("application/json".to_string()); "map JSON format to media type")]
153    #[test_case(UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF, Some("application/protobuf".to_string()); "map PROTOBUF format to media type")]
154    #[test_case(UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY, Some("application/x-protobuf".to_string()); "map PROTOBUF_WRAPPED format to media type")]
155    #[test_case(UPayloadFormat::UPAYLOAD_FORMAT_RAW, Some("application/octet-stream".to_string()); "map RAW format to media type")]
156    #[test_case(UPayloadFormat::UPAYLOAD_FORMAT_SOMEIP, Some("application/x-someip".to_string()); "map SOMEIP format to media type")]
157    #[test_case(UPayloadFormat::UPAYLOAD_FORMAT_SOMEIP_TLV, Some("application/x-someip_tlv".to_string()); "map SOMEIP_TLV format to media type")]
158    #[test_case(UPayloadFormat::UPAYLOAD_FORMAT_TEXT, Some("text/plain".to_string()); "map TEXT format to media type")]
159    #[test_case(UPayloadFormat::UPAYLOAD_FORMAT_UNSPECIFIED, None; "map UNSPECIFIED format to None")]
160    fn test_to_media_type(format: UPayloadFormat, expected_media_type: Option<String>) {
161        assert_eq!(format.to_media_type(), expected_media_type);
162    }
163}