Skip to main content

ocpp_types/
action.rs

1pub trait Action {
2    const ACTION: &'static str;
3
4    /// Serializes this message as JSON into `buf`, returning the written
5    /// slice. No allocation: the caller owns and sizes the buffer, same as
6    /// every other bounded type in this crate.
7    #[cfg(feature = "serde")]
8    fn to_json_slice<'buf>(
9        &self,
10        buf: &'buf mut [u8],
11    ) -> Result<&'buf [u8], serde_json_core::ser::Error>
12    where
13        Self: serde::Serialize,
14    {
15        let len = serde_json_core::to_slice(self, buf)?;
16        Ok(&buf[..len])
17    }
18
19    /// Same as [`Action::to_json_slice`], but returns the written bytes as
20    /// `&str`. JSON emitted by `serde-json-core` is always valid UTF-8.
21    #[cfg(feature = "serde")]
22    fn to_json_str<'buf>(
23        &self,
24        buf: &'buf mut [u8],
25    ) -> Result<&'buf str, serde_json_core::ser::Error>
26    where
27        Self: serde::Serialize,
28    {
29        let bytes = self.to_json_slice(buf)?;
30        Ok(core::str::from_utf8(bytes).expect("serde-json-core always emits valid UTF-8"))
31    }
32
33    /// Deserializes this message from JSON bytes. No allocation: parsing
34    /// borrows from `data` only transiently: `Self` owns everything via
35    /// `heapless` collections, so nothing outlives this call.
36    #[cfg(feature = "serde")]
37    fn from_json_slice(data: &[u8]) -> Result<Self, serde_json_core::de::Error>
38    where
39        Self: Sized + serde::de::DeserializeOwned,
40    {
41        serde_json_core::from_slice(data).map(|(value, _remainder)| value)
42    }
43
44    /// Same as [`Action::from_json_slice`], but takes a `&str` instead of
45    /// raw bytes (skipping the UTF-8 check `from_json_slice` would
46    /// otherwise need, since `&str` already guarantees it).
47    #[cfg(feature = "serde")]
48    fn from_json_str(data: &str) -> Result<Self, serde_json_core::de::Error>
49    where
50        Self: Sized + serde::de::DeserializeOwned,
51    {
52        serde_json_core::from_str(data).map(|(value, _remainder)| value)
53    }
54}
55
56#[cfg(all(test, feature = "serde"))]
57mod tests {
58    use super::*;
59
60    #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
61    struct SampleRequest {
62        #[serde(rename = "idTag")]
63        id_tag: heapless::String<20>,
64    }
65
66    impl Action for SampleRequest {
67        const ACTION: &'static str = "Sample";
68    }
69
70    fn sample() -> SampleRequest {
71        SampleRequest {
72            id_tag: heapless::String::try_from("ABC123").unwrap(),
73        }
74    }
75
76    #[test]
77    fn to_json_slice_writes_json_into_the_given_buffer() {
78        let mut buf = [0u8; 64];
79        let json = sample().to_json_slice(&mut buf).unwrap();
80
81        assert_eq!(json, br#"{"idTag":"ABC123"}"#);
82    }
83
84    #[test]
85    fn to_json_slice_errors_when_the_buffer_is_too_small() {
86        let mut buf = [0u8; 4];
87
88        assert!(sample().to_json_slice(&mut buf).is_err());
89    }
90
91    #[test]
92    fn to_json_str_writes_json_into_the_given_buffer_as_utf8() {
93        let mut buf = [0u8; 64];
94        let json = sample().to_json_str(&mut buf).unwrap();
95
96        assert_eq!(json, r#"{"idTag":"ABC123"}"#);
97    }
98
99    #[test]
100    fn from_json_slice_parses_bytes_back_into_the_struct() {
101        let parsed = SampleRequest::from_json_slice(br#"{"idTag":"ABC123"}"#).unwrap();
102
103        assert_eq!(parsed, sample());
104    }
105
106    #[test]
107    fn from_json_str_parses_a_str_back_into_the_struct() {
108        let parsed = SampleRequest::from_json_str(r#"{"idTag":"ABC123"}"#).unwrap();
109
110        assert_eq!(parsed, sample());
111    }
112
113    #[test]
114    fn round_trips_through_bytes() {
115        let mut buf = [0u8; 64];
116        let json = sample().to_json_slice(&mut buf).unwrap();
117        let parsed = SampleRequest::from_json_slice(json).unwrap();
118
119        assert_eq!(parsed, sample());
120    }
121}