ocpp_types/
custom_data.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
39pub struct NoCustomData;
40
41#[cfg(feature = "serde")]
42impl serde::Serialize for NoCustomData {
43 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
44 use serde::ser::SerializeMap;
45
46 serializer.serialize_map(Some(0))?.end()
47 }
48}
49
50#[cfg(feature = "serde")]
51impl<'de> serde::Deserialize<'de> for NoCustomData {
52 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
53 struct Visitor;
54
55 impl<'de> serde::de::Visitor<'de> for Visitor {
56 type Value = NoCustomData;
57
58 fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
59 f.write_str("a customData object")
60 }
61
62 fn visit_map<A: serde::de::MapAccess<'de>>(
63 self,
64 mut map: A,
65 ) -> Result<Self::Value, A::Error> {
66 while map
67 .next_entry::<serde::de::IgnoredAny, serde::de::IgnoredAny>()?
68 .is_some()
69 {}
70
71 Ok(NoCustomData)
72 }
73
74 fn visit_unit<E: serde::de::Error>(self) -> Result<Self::Value, E> {
77 Ok(NoCustomData)
78 }
79 }
80
81 deserializer.deserialize_map(Visitor)
82 }
83}
84
85#[cfg(all(test, feature = "serde"))]
86mod tests {
87 use crate::Action;
88 use crate::v21::HeartbeatRequest;
89 use crate::v21::common::{Component, CustomData};
90
91 #[test]
96 fn the_default_discards_a_populated_custom_data_object() {
97 let json = r#"{"customData":{"vendorId":"com.acme","extra":[1,2,3]},"name":"EVSE"}"#;
98 let (component, _): (Component, _) = serde_json_core::from_str(json).unwrap();
99
100 assert_eq!(component.name.as_str(), "EVSE");
101 assert!(component.custom_data.is_some());
102 }
103
104 #[test]
105 fn the_default_still_accepts_a_message_without_custom_data() {
106 let (component, _): (Component, _) =
107 serde_json_core::from_str(r#"{"name":"EVSE"}"#).unwrap();
108
109 assert!(component.custom_data.is_none());
110 }
111
112 #[test]
115 fn a_caller_can_name_the_specs_custom_data_shape_and_read_the_vendor_id() {
116 let json = r#"{"customData":{"vendorId":"com.acme"},"name":"EVSE"}"#;
117 let (component, _): (Component<CustomData>, _) =
118 serde_json_core::from_str(json).unwrap();
119
120 assert_eq!(component.custom_data.unwrap().vendor_id.as_str(), "com.acme");
121 }
122
123 #[test]
125 fn a_caller_can_supply_an_entirely_custom_shape() {
126 #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
127 struct AcmeExtension {
128 #[serde(rename = "vendorId")]
129 vendor_id: heapless::String<16>,
130 #[serde(rename = "siteId")]
131 site_id: u32,
132 }
133
134 let json = r#"{"customData":{"vendorId":"com.acme","siteId":42},"name":"EVSE"}"#;
135 let (component, _): (Component<AcmeExtension>, _) =
136 serde_json_core::from_str(json).unwrap();
137
138 let extension = component.custom_data.unwrap();
139 assert_eq!(extension.site_id, 42);
140 assert_eq!(extension.vendor_id.as_str(), "com.acme");
141 }
142
143 #[test]
146 fn the_parameter_reaches_the_message_types() {
147 let request: HeartbeatRequest<CustomData> = HeartbeatRequest {
148 custom_data: Some(CustomData {
149 vendor_id: heapless::String::try_from("com.acme").unwrap(),
150 }),
151 };
152
153 assert_eq!(HeartbeatRequest::<CustomData>::ACTION, "Heartbeat");
154 assert!(request.custom_data.is_some());
155 }
156
157 #[test]
162 fn the_default_costs_one_byte_and_the_spec_shape_costs_the_full_field() {
163 assert_eq!(core::mem::size_of::<Option<super::NoCustomData>>(), 1);
164 assert_eq!(core::mem::size_of::<Option<CustomData>>(), 272);
165 }
166}