1use std::{convert::TryFrom, io};
85
86use byteorder::{ReadBytesExt, WriteBytesExt, BE, LE};
87use serde::{
88 de::{Deserializer, Error as DeError},
89 Deserialize,
90};
91use serde_json::Value;
92
93use crate::{integer::RawIntegerSchema, ByteOrder, Decoder, Encoder, IntegerSchema};
94
95#[derive(Debug, thiserror::Error)]
97pub enum ValidationError {
98 #[error("Invalid bitfield: {0}")]
99 InvalidIntergerSchema(#[from] crate::integer::ValidationError),
100 #[error("The requsted length of {requested} is invalid for floating-point serialization. Either use 4 or 8 or use an integer-based encoding")]
101 InvalidFloatingLength { requested: usize },
102}
103
104#[derive(Debug, thiserror::Error)]
106pub enum EncodingError {
107 #[error("The value '{value}' can not be encoded with a number schema")]
108 InvalidValue { value: String },
109 #[error("Writing to buffer failed: {0}")]
110 WriteFail(#[from] io::Error),
111 #[error("Failed to encode as integer: {0}")]
112 Integer(#[from] crate::integer::EncodingError),
113}
114
115#[derive(Debug, thiserror::Error)]
117pub enum DecodingError {
118 #[error("Reading encoded data failed: {0}")]
119 ReadFail(#[from] io::Error),
120 #[error("Failed to decode underlying integer: {0}")]
121 Integer(#[from] crate::integer::DecodingError),
122}
123
124impl DecodingError {
125 pub fn due_to_eof(&self) -> bool {
126 matches!(self, Self::ReadFail(e) if e.kind() == std::io::ErrorKind::UnexpectedEof)
127 }
128}
129
130#[derive(Debug, Clone, Deserialize)]
132#[serde(rename_all = "lowercase")]
133struct RawNumber {
134 #[serde(flatten, default)]
135 int: RawIntegerSchema,
136 scale: Option<f64>,
137 offset: Option<f64>,
138}
139
140#[derive(Debug, Clone)]
142pub enum NumberSchema {
143 Integer {
144 integer: IntegerSchema,
145 scale: f64,
146 offset: f64,
147 },
148 Float {
149 byteorder: ByteOrder,
150 },
151 Double {
152 byteorder: ByteOrder,
153 },
154}
155
156const DEFAULT_LENGTH: usize = 4;
157const DEFAULT_SCALE: f64 = 1_f64;
158const DEFAULT_OFFSET: f64 = 0_f64;
159
160impl NumberSchema {
161 pub fn default_length() -> usize {
163 DEFAULT_LENGTH
164 }
165 pub fn default_scale() -> f64 {
167 DEFAULT_SCALE
168 }
169 pub fn default_offset() -> f64 {
171 DEFAULT_OFFSET
172 }
173 pub fn to_binary_value(&self, value: f64) -> i64 {
175 match self {
176 NumberSchema::Integer { scale, offset, .. } => {
177 ((value - *offset) / *scale).round() as _
178 }
179 _ => value as _,
180 }
181 }
182 pub fn from_binary_value(&self, value: f64) -> f64 {
184 match self {
185 NumberSchema::Integer { scale, offset, .. } => value * *scale + *offset,
186 _ => value,
187 }
188 }
189 pub fn length(&self) -> usize {
190 match self {
191 NumberSchema::Integer { integer, .. } => integer.length(),
192 NumberSchema::Float { .. } => 4,
193 NumberSchema::Double { .. } => 8,
194 }
195 }
196}
197
198impl TryFrom<RawNumber> for NumberSchema {
199 type Error = ValidationError;
200
201 fn try_from(raw: RawNumber) -> Result<Self, Self::Error> {
202 if raw.scale.is_some()
203 || raw.offset.is_some()
204 || raw.int.bit_offset.is_some()
205 || raw.int.bits.is_some()
206 {
207 let integer = IntegerSchema::try_from(raw.int)?;
208 Ok(NumberSchema::Integer {
209 integer,
210 scale: raw.scale.unwrap_or(DEFAULT_SCALE),
211 offset: raw.offset.unwrap_or(DEFAULT_OFFSET),
212 })
213 } else {
214 match raw.int.length {
215 4 => Ok(NumberSchema::Float {
216 byteorder: raw.int.byteorder,
217 }),
218 8 => Ok(NumberSchema::Double {
219 byteorder: raw.int.byteorder,
220 }),
221 _ => Err(ValidationError::InvalidFloatingLength {
222 requested: raw.int.length,
223 }),
224 }
225 }
226 }
227}
228
229impl<'de> Deserialize<'de> for NumberSchema {
230 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
231 where
232 D: Deserializer<'de>,
233 {
234 let raw = RawNumber::deserialize(deserializer)?;
235 NumberSchema::try_from(raw).map_err(D::Error::custom)
236 }
237}
238
239impl Encoder for NumberSchema {
240 type Error = EncodingError;
241
242 fn encode<W>(&self, target: &mut W, value: &Value) -> Result<usize, Self::Error>
243 where
244 W: io::Write + WriteBytesExt,
245 {
246 let value = value.as_f64().ok_or_else(|| EncodingError::InvalidValue {
247 value: value.to_string(),
248 })?;
249 let length = match self {
250 NumberSchema::Integer { integer, .. } => {
251 let value = self.to_binary_value(value).into();
252 integer.encode(target, &value)?
253 }
254 NumberSchema::Float { byteorder } => {
255 let value = value as f32;
256 match byteorder {
257 ByteOrder::LittleEndian => target.write_f32::<LE>(value)?,
258 ByteOrder::BigEndian => target.write_f32::<BE>(value)?,
259 }
260 4
261 }
262 NumberSchema::Double { byteorder } => {
263 let value = value;
264 match byteorder {
265 ByteOrder::LittleEndian => target.write_f64::<LE>(value)?,
266 ByteOrder::BigEndian => target.write_f64::<BE>(value)?,
267 }
268 8
269 }
270 };
271
272 Ok(length)
273 }
274}
275
276impl Decoder for NumberSchema {
277 type Error = DecodingError;
278
279 fn decode<R>(&self, target: &mut R) -> Result<Value, Self::Error>
280 where
281 R: io::Read + ReadBytesExt,
282 {
283 let value = match self {
284 NumberSchema::Integer { integer, .. } => {
285 let int = integer
286 .decode(target)?
287 .as_f64()
288 .expect("always works on integer schemata");
289 self.from_binary_value(int).into()
290 }
291 NumberSchema::Float { byteorder } => match byteorder {
292 ByteOrder::LittleEndian => target.read_f32::<LE>()?.into(),
293 ByteOrder::BigEndian => target.read_f32::<BE>()?.into(),
294 },
295 NumberSchema::Double { byteorder } => match byteorder {
296 ByteOrder::LittleEndian => target.read_f64::<LE>()?.into(),
297 ByteOrder::BigEndian => target.read_f64::<BE>()?.into(),
298 },
299 };
300
301 Ok(value)
302 }
303}
304
305#[cfg(test)]
306mod test {
307 use super::*;
308 use anyhow::Result;
309 use serde_json::{from_value, json};
310
311 fn eq_fload(f1: f64, f2: f64) -> bool {
313 (f1 - f2).abs() < 0.000_001
314 }
315
316 #[test]
317 fn encode() -> Result<()> {
318 let schema = json!({
319 "scale": 0.01,
320 "offset": 10,
321 "byteorder": "littleendian",
322 "length": 2
323 });
324 let number2int: NumberSchema = from_value(schema)?;
325 let schema = json!({});
326 let number2float: NumberSchema = from_value(schema)?;
327 let schema = json!({
328 "length": 8,
329 "byteorder": "littleendian"
330 });
331 let number2double: NumberSchema = from_value(schema)?;
332 let schema = json!({"length": 3});
333 assert!(from_value::<NumberSchema>(schema).is_err());
334
335 let value = 22.5;
336 let json: Value = value.into();
337 let value_as_bin = 1250_f64;
338 assert!(eq_fload(
339 value_as_bin,
340 number2int.to_binary_value(value) as f64
341 ));
342 let value_int_le = (value_as_bin as i16).to_le_bytes();
343 let value_float_be = (value as f32).to_be_bytes();
344 let value_double_le = value.to_le_bytes();
345 let expected: Vec<u8> = value_int_le
346 .iter()
347 .chain(value_float_be.iter())
348 .chain(value_double_le.iter())
349 .copied()
350 .collect();
351
352 let mut buffer: Vec<u8> = vec![];
353 assert_eq!(2, number2int.encode(&mut buffer, &json)?);
354 assert_eq!(2, buffer.len());
355 assert_eq!(4, number2float.encode(&mut buffer, &json)?);
356 assert_eq!(2 + 4, buffer.len());
357 assert_eq!(8, number2double.encode(&mut buffer, &json)?);
358 assert_eq!(2 + 4 + 8, buffer.len());
359
360 assert_eq!(buffer, expected);
361
362 Ok(())
363 }
364
365 #[test]
367 fn bitfield() -> Result<()> {
368 let schema = json!({
369 "offset": 1.6,
370 "scale": 0.001,
371 "length": 2,
372 "bits": 11,
373 "bitoffset": 5
374 });
375 let voltage = from_value::<NumberSchema>(schema)?;
376 let value1 = 1.6;
377 let json1: Value = value1.into();
378 let value2 = 3.0;
379 let json2: Value = value2.into();
380 let mut buffer = [0; 2];
381
382 assert_eq!(2, voltage.encode(&mut buffer.as_mut(), &json1)?);
383 let res = u16::from_be_bytes(buffer);
384 assert_eq!(0, res);
385
386 assert_eq!(2, voltage.encode(&mut buffer.as_mut(), &json2)?);
387 let res = u16::from_be_bytes(buffer);
388 let diff = 1400 - ((res >> 5) as i16);
389 assert!(diff < 3 && diff > -3);
390
391 Ok(())
392 }
393
394 #[test]
395 fn bitfield2() -> Result<()> {
396 let schema = json!({
397 "type": "number",
398 "offset": -40,
399 "scale": 2,
400 "length": 2,
401 "bits": 5,
402 "bitoffset": 0,
403 "position": 50,
404 "unit": "dBm",
405 "description": "Transmission power in 1m distance."
406 });
407 let schema = from_value::<NumberSchema>(schema)?;
408
409 println!("schema:\n{:#?}", schema);
410
411 Ok(())
412 }
413
414 #[test]
415 fn bitfield3() -> Result<()> {
416 let schema = json!({
417 "type": "number",
418 "offset": 1.6,
419 "scale": 0.001,
420 "length": 2,
421 "bits": 11,
422 "bitoffset": 5,
423 "position": 50,
424 "unit": "volts",
425 "description": "Voltage of the battery powering the RuuviTag."
426 });
427 let schema = from_value::<NumberSchema>(schema)?;
428
429 println!("schema:\n{:#?}", schema);
430
431 Ok(())
432 }
433}