islabtech_upw_sensor_v1/measurements/
serde_impl.rs1use serde::{
3 de::{self, Visitor},
4 ser::SerializeStruct,
5 Deserialize, Serialize,
6};
7
8use super::{Conductivity, Measurement, SuccessfulMeasurement, Temperature};
9
10macro_rules! impl_ser {
11 ($t:ty) => {
12 impl Serialize for $t {
13 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
14 where
15 S: serde::Serializer,
16 {
17 let mut state = serializer.serialize_struct(stringify!($t), 4)?;
18 state.serialize_field("epoch_timestamp", &self.timestamp.timestamp())?;
19 state.serialize_field(
20 "epoch_microseconds",
21 &self.timestamp.timestamp_subsec_micros(),
22 )?;
23 state.serialize_field("conductivity", &self.conductivity)?;
24 state.serialize_field("temperature", &self.temperature)?;
25 state.end()
26 }
27 }
28 };
29}
30impl_ser!(Measurement);
31impl_ser!(SuccessfulMeasurement);
32
33macro_rules! impl_de {
34 ($t:ident, $cond_t: ty, $temp_t: ty) => {
35 impl<'de> Deserialize<'de> for $t {
36 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
37 where
38 D: serde::Deserializer<'de>,
39 {
40 #[derive(Deserialize)]
41 #[serde(field_identifier, rename_all = "snake_case")]
42 enum Field {
43 EpochTimestamp,
44 EpochMicroseconds,
45 Conductivity,
46 Temperature,
47 #[serde(other)]
48 Ignore,
49 }
50 struct MeasurementVisitor;
51 impl<'de> Visitor<'de> for MeasurementVisitor {
52 type Value = $t;
53 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
54 formatter.write_str("struct Measurement")
55 }
56 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
57 where
58 A: serde::de::MapAccess<'de>,
59 {
60 let mut seconds: Option<i64> = None;
61 let mut micros: Option<u32> = None;
62 let mut cond: Option<$cond_t> = None;
63 let mut temp: Option<$temp_t> = None;
64 while let Some(key) = map.next_key()? {
65 match key {
66 Field::EpochTimestamp => seconds = Some(map.next_value()?),
67 Field::EpochMicroseconds => micros = Some(map.next_value()?),
68 Field::Conductivity => cond = Some(map.next_value()?),
69 Field::Temperature => temp = Some(map.next_value()?),
70 Field::Ignore => {
71 let _: serde::de::IgnoredAny = map.next_value()?;
72 }
73 }
74 }
75 Ok($t {
76 timestamp: chrono::DateTime::from_timestamp(
77 seconds
78 .ok_or_else(|| de::Error::missing_field("epoch_timestamp"))?,
79 micros.ok_or_else(|| {
80 de::Error::missing_field("epoch_microseconds")
81 })? * 1000,
82 )
83 .ok_or_else(|| de::Error::custom("invalid timestamp"))?,
84 conductivity: cond
85 .ok_or_else(|| de::Error::missing_field("conductivity"))?,
86 temperature: temp
87 .ok_or_else(|| de::Error::missing_field("temperature"))?,
88 })
89 }
90 }
91
92 deserializer.deserialize_map(MeasurementVisitor)
93 }
94 }
95 };
96}
97
98impl_de!(Measurement, Option<Conductivity>, Option<Temperature>);
99impl_de!(SuccessfulMeasurement, Conductivity, Temperature);
100
101#[cfg(test)]
102mod tests {
103 use crate::measurements::{Measurement, SuccessfulMeasurement};
104 fn example_timestamp() -> chrono::DateTime<chrono::Utc> {
105 chrono::DateTime::from_timestamp(123, 456789 * 1000).unwrap()
106 }
107 fn json(cond: &str, temp: &str) -> String {
108 format!("{{\"epoch_timestamp\":123,\"epoch_microseconds\":456789,\"conductivity\":{cond},\"temperature\":{temp}}}")
109 }
110 fn serde_pairs() -> Vec<(String, Measurement, Option<SuccessfulMeasurement>)> {
111 vec![
112 (
113 json("1.234", "12.34"),
114 Measurement {
115 timestamp: example_timestamp(),
116 conductivity: Some(1.234.into()),
117 temperature: Some(12.34.into()),
118 },
119 Some(SuccessfulMeasurement {
120 timestamp: example_timestamp(),
121 conductivity: 1.234.into(),
122 temperature: 12.34.into(),
123 }),
124 ),
125 (
126 json("null", "null"),
127 Measurement {
128 timestamp: example_timestamp(),
129 conductivity: None,
130 temperature: None,
131 },
132 None,
133 ),
134 (
135 json("1.234", "null"),
136 Measurement {
137 timestamp: example_timestamp(),
138 conductivity: Some(1.234.into()),
139 temperature: None,
140 },
141 None,
142 ),
143 (
144 json("null", "12.34"),
145 Measurement {
146 timestamp: example_timestamp(),
147 conductivity: None,
148 temperature: Some(12.34.into()),
149 },
150 None,
151 ),
152 ]
153 }
154
155 #[test]
156 fn serialize_measurement() {
157 for (json, measurement, successful_measurement) in serde_pairs().iter() {
158 assert_eq!(
159 serde_json::to_string(measurement).expect("can not serialize Measurement"),
160 *json,
161 );
162 if let Some(successful_measurement) = successful_measurement {
163 assert_eq!(
164 serde_json::to_string(successful_measurement)
165 .expect("can not serialize SuccessfulMeasurement"),
166 *json
167 );
168 }
169 }
170 }
171
172 #[test]
173 fn deserialize_measurement() {
174 for (serialized, measurement, successful_measurement) in serde_pairs().iter() {
175 assert_eq!(
176 serde_json::from_str::<Measurement>(serialized)
177 .expect("can not deserialize Measurement"),
178 *measurement,
179 "deserializing `Measurement`"
180 );
181 assert_eq!(
182 serde_json::from_str::<SuccessfulMeasurement>(serialized).ok(),
183 *successful_measurement,
184 "deserializing `Measurement`"
185 );
186 }
187 }
188
189 #[test]
190 fn deserialize_incomplete_measurement() {
191 let incomplete_json = vec![
192 (
193 r#"{"epoch_timestamp":123, "epoch_microseconds":456789, "conductivity": null }"#,
194 "missing temperature",
195 ),
196 (
197 r#"{"epoch_timestamp":123, "epoch_microseconds":456789, "temperature": null }"#,
198 "missing conductivity",
199 ),
200 (
201 r#"{"epoch_timestamp":123, "conductivity":null, "temperature": null }"#,
202 "missing epoch microseconds",
203 ),
204 (
205 r#"{"epoch_microseconds":123, "conductivity":null, "temperature": null }"#,
206 "missing epoch seconds",
207 ),
208 ];
209 for (json, description) in incomplete_json.iter() {
210 let err = serde_json::from_str::<Measurement>(json).expect_err(
211 format!("Measurement {description} succeeded but it should not").as_str(),
212 );
213 assert!(
214 err.is_data(),
215 "Measurement {description} threw wrong error type"
216 );
217 }
218 }
219}