Skip to content

Commit a2a55e0

Browse files
authored
fix floating point parsing, other bugs (#238)
* fix floating point parsing * fix leading zero and whitespace handling for none fields for QGPSLOC to work correctly in all cases
1 parent ef726f1 commit a2a55e0

2 files changed

Lines changed: 262 additions & 25 deletions

File tree

serde_at/src/de/mod.rs

Lines changed: 261 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -244,11 +244,7 @@ macro_rules! deserialize_unsigned {
244244

245245
match peek {
246246
b'-' => Err(Error::InvalidNumber),
247-
b'0' => {
248-
$self.eat_char();
249-
$visitor.$visit_uxx(0)
250-
}
251-
b'1'..=b'9' => {
247+
b'0'..=b'9' => {
252248
$self.eat_char();
253249

254250
let mut number = (peek - b'0') as $uxx;
@@ -285,11 +281,7 @@ macro_rules! deserialize_signed {
285281
};
286282

287283
match $self.peek().ok_or(Error::EofWhileParsingValue)? {
288-
b'0' => {
289-
$self.eat_char();
290-
$visitor.$visit_ixx(0)
291-
}
292-
c @ b'1'..=b'9' => {
284+
c @ b'0'..=b'9' => {
293285
$self.eat_char();
294286

295287
let mut number = (c - b'0') as $ixx * if signed { -1 } else { 1 };
@@ -317,19 +309,19 @@ macro_rules! deserialize_fromstr {
317309
let start = $self.index;
318310
loop {
319311
match $self.peek() {
320-
Some(c) => {
321-
if $pattern.iter().find(|&&d| d == c).is_some() {
322-
$self.eat_char();
323-
} else {
324-
let s = unsafe {
325-
// already checked that it contains only ascii
326-
str::from_utf8_unchecked(&$self.slice[start..$self.index])
327-
};
328-
let v = $typ::from_str(s).or(Err(Error::InvalidNumber))?;
329-
return $visitor.$visit_fn(v);
330-
}
312+
Some(c) if $pattern.iter().find(|&&d| d == c).is_some() => {
313+
$self.eat_char();
314+
}
315+
Some(_) | None => {
316+
// either found a character not in the pattern, or reached end of input
317+
// in both cases, parse the accumulated string
318+
let s = unsafe {
319+
// already checked that it contains only ascii
320+
str::from_utf8_unchecked(&$self.slice[start..$self.index])
321+
};
322+
let v = $typ::from_str(s).or(Err(Error::InvalidNumber))?;
323+
return $visitor.$visit_fn(v);
331324
}
332-
None => return Err(Error::EofWhileParsingNumber),
333325
}
334326
}
335327
}};
@@ -1005,4 +997,251 @@ mod tests {
1005997
Bytes::<32>::from_slice(b"{\"cmd\": \"blink\", \"pin\": \"2\"}").unwrap()
1006998
);
1007999
}
1000+
1001+
#[test]
1002+
fn f32_basic_numbers() {
1003+
#[derive(Clone, Debug, Deserialize, PartialEq)]
1004+
pub struct F32Test {
1005+
pub value: f32,
1006+
}
1007+
1008+
assert_eq!(crate::from_str("+TEST: 42"), Ok(F32Test { value: 42.0 }));
1009+
1010+
assert_eq!(crate::from_str("+TEST: -42"), Ok(F32Test { value: -42.0 }));
1011+
1012+
assert_eq!(crate::from_str("+TEST: 0"), Ok(F32Test { value: 0.0 }));
1013+
1014+
assert_eq!(crate::from_str("+TEST: 3.14"), Ok(F32Test { value: 3.14 }));
1015+
1016+
assert_eq!(
1017+
crate::from_str("+TEST: -2.718"),
1018+
Ok(F32Test { value: -2.718 })
1019+
);
1020+
}
1021+
1022+
#[test]
1023+
fn f32_scientific_notation() {
1024+
#[derive(Clone, Debug, Deserialize, PartialEq)]
1025+
pub struct F32Test {
1026+
pub value: f32,
1027+
}
1028+
1029+
assert_eq!(
1030+
crate::from_str("+TEST: 1.23e4"),
1031+
Ok(F32Test { value: 1.23e4 })
1032+
);
1033+
1034+
assert_eq!(
1035+
crate::from_str("+TEST: 1.23E4"),
1036+
Ok(F32Test { value: 1.23E4 })
1037+
);
1038+
1039+
assert_eq!(
1040+
crate::from_str("+TEST: 1.23e-4"),
1041+
Ok(F32Test { value: 1.23e-4 })
1042+
);
1043+
1044+
assert_eq!(
1045+
crate::from_str("+TEST: 1.23e+4"),
1046+
Ok(F32Test { value: 1.23e+4 })
1047+
);
1048+
1049+
assert_eq!(
1050+
crate::from_str("+TEST: -1.23e4"),
1051+
Ok(F32Test { value: -1.23e4 })
1052+
);
1053+
}
1054+
1055+
#[test]
1056+
fn f64_basic_numbers() {
1057+
#[derive(Clone, Debug, Deserialize, PartialEq)]
1058+
pub struct F64Test {
1059+
pub value: f64,
1060+
}
1061+
1062+
assert_eq!(crate::from_str("+TEST: 42"), Ok(F64Test { value: 42.0 }));
1063+
1064+
assert_eq!(crate::from_str("+TEST: -42"), Ok(F64Test { value: -42.0 }));
1065+
1066+
assert_eq!(crate::from_str("+TEST: 0"), Ok(F64Test { value: 0.0 }));
1067+
1068+
assert_eq!(
1069+
crate::from_str("+TEST: 3.141592653589793"),
1070+
Ok(F64Test {
1071+
value: 3.141592653589793
1072+
})
1073+
);
1074+
1075+
assert_eq!(
1076+
crate::from_str("+TEST: -2.718281828459045"),
1077+
Ok(F64Test {
1078+
value: -2.718281828459045
1079+
})
1080+
);
1081+
}
1082+
1083+
#[test]
1084+
fn f64_scientific_notation() {
1085+
#[derive(Clone, Debug, Deserialize, PartialEq)]
1086+
pub struct F64Test {
1087+
pub value: f64,
1088+
}
1089+
1090+
assert_eq!(
1091+
crate::from_str("+TEST: 1.23456789e15"),
1092+
Ok(F64Test {
1093+
value: 1.23456789e15
1094+
})
1095+
);
1096+
1097+
assert_eq!(
1098+
crate::from_str("+TEST: 1.23456789E15"),
1099+
Ok(F64Test {
1100+
value: 1.23456789E15
1101+
})
1102+
);
1103+
1104+
assert_eq!(
1105+
crate::from_str("+TEST: 1.23456789e-15"),
1106+
Ok(F64Test {
1107+
value: 1.23456789e-15
1108+
})
1109+
);
1110+
1111+
assert_eq!(
1112+
crate::from_str("+TEST: 1.23456789e+15"),
1113+
Ok(F64Test {
1114+
value: 1.23456789e+15
1115+
})
1116+
);
1117+
1118+
assert_eq!(
1119+
crate::from_str("+TEST: -1.23456789e15"),
1120+
Ok(F64Test {
1121+
value: -1.23456789e15
1122+
})
1123+
);
1124+
}
1125+
1126+
#[test]
1127+
fn float_edge_cases() {
1128+
#[derive(Clone, Debug, Deserialize, PartialEq)]
1129+
pub struct F32Test {
1130+
pub value: f32,
1131+
}
1132+
1133+
#[derive(Clone, Debug, Deserialize, PartialEq)]
1134+
pub struct F64Test {
1135+
pub value: f64,
1136+
}
1137+
1138+
assert_eq!(crate::from_str("+TEST: .5"), Ok(F32Test { value: 0.5 }));
1139+
1140+
assert_eq!(crate::from_str("+TEST: -.5"), Ok(F32Test { value: -0.5 }));
1141+
1142+
assert_eq!(crate::from_str("+TEST: 42."), Ok(F32Test { value: 42.0 }));
1143+
1144+
assert_eq!(
1145+
crate::from_str("+TEST: 1e-10"),
1146+
Ok(F64Test { value: 1e-10 })
1147+
);
1148+
1149+
assert_eq!(crate::from_str("+TEST: 1e10"), Ok(F64Test { value: 1e10 }));
1150+
}
1151+
1152+
#[test]
1153+
fn float_in_struct_with_multiple_fields() {
1154+
#[derive(Clone, Debug, Deserialize, PartialEq)]
1155+
pub struct MixedTest {
1156+
pub id: u8,
1157+
pub temperature: f32,
1158+
pub pressure: f64,
1159+
pub active: bool,
1160+
}
1161+
1162+
assert_eq!(
1163+
crate::from_str("+SENSOR: 1,23.5,-1.013e5,true"),
1164+
Ok(MixedTest {
1165+
id: 1,
1166+
temperature: 23.5,
1167+
pressure: -1.013e5,
1168+
active: true,
1169+
})
1170+
);
1171+
}
1172+
1173+
#[test]
1174+
fn float_with_whitespace() {
1175+
#[derive(Clone, Debug, Deserialize, PartialEq)]
1176+
pub struct F32Test {
1177+
pub value: f32,
1178+
}
1179+
assert_eq!(
1180+
crate::from_str("+TEST: 3.14"),
1181+
Ok(F32Test { value: 3.14 })
1182+
);
1183+
1184+
assert_eq!(
1185+
crate::from_str("+TEST: \t\n 2.718 "),
1186+
Ok(F32Test { value: 2.718 })
1187+
);
1188+
}
1189+
1190+
#[test]
1191+
fn qgpsloc() {
1192+
#[derive(Clone, Debug, Deserialize, PartialEq)]
1193+
pub struct GpsLocation {
1194+
pub utc: Option<f64>,
1195+
pub latitude: Option<f64>,
1196+
pub longitude: Option<f64>,
1197+
pub hdop: Option<f32>,
1198+
pub altitude: Option<f32>,
1199+
pub fix: Option<i16>,
1200+
pub course_over_ground: Option<f32>,
1201+
pub speed_km: Option<f32>,
1202+
pub speed_knots: Option<f32>,
1203+
pub date: Option<i32>,
1204+
pub nsat: Option<i8>,
1205+
}
1206+
1207+
let res = crate::from_str(
1208+
"+QGPSLOC: 000040.000,30.28653,120.03266,1.2,84.1,3,0.00,0.0,0.0,240725,07",
1209+
);
1210+
1211+
assert_eq!(
1212+
res,
1213+
Ok(GpsLocation {
1214+
utc: Some(40.0),
1215+
latitude: Some(30.28653),
1216+
longitude: Some(120.03266),
1217+
hdop: Some(1.2),
1218+
altitude: Some(84.1),
1219+
fix: Some(3),
1220+
course_over_ground: Some(0.0),
1221+
speed_km: Some(0.0),
1222+
speed_knots: Some(0.0),
1223+
date: Some(240725),
1224+
nsat: Some(7),
1225+
})
1226+
);
1227+
1228+
let res = crate::from_str("+QGPSLOC: , , , , , , , , , ,");
1229+
1230+
assert_eq!(
1231+
res,
1232+
Ok(GpsLocation {
1233+
utc: None,
1234+
latitude: None,
1235+
longitude: None,
1236+
hdop: None,
1237+
altitude: None,
1238+
fix: None,
1239+
course_over_ground: None,
1240+
speed_km: None,
1241+
speed_knots: None,
1242+
date: None,
1243+
nsat: None,
1244+
})
1245+
);
1246+
}
10081247
}

serde_at/src/de/seq.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ impl<'a, 'de> de::SeqAccess<'de> for SeqAccess<'a, 'de> {
3232
match self.de.parse_whitespace() {
3333
Some(b',') => {
3434
self.de.eat_char();
35-
self.de
36-
.parse_whitespace()
37-
.ok_or(Error::EofWhileParsingValue)?;
35+
self.de.parse_whitespace();
3836
}
3937
Some(c) => {
4038
if self.first {

0 commit comments

Comments
 (0)