Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement no_std serialization for NaiveTime #106

Merged
merged 2 commits into from
Aug 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pretty_assertions = "1"
quickcheck = { version = "1", default-features = false }
# criterion 0.5 has clap_builder version which requires at least Rust 1.64
criterion = "0.4"
serde_json = "1.0.105"

[features]
default = ["std", "all-sentences"]
Expand Down
167 changes: 167 additions & 0 deletions src/sentences/gga.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq)]
pub struct GgaData {
#[cfg_attr(
not(feature = "std"),
cfg_attr(feature = "serde", serde(with = "serde_naive_time"))
)]
pub fix_time: Option<NaiveTime>,
pub fix_type: Option<FixType>,
pub latitude: Option<f64>,
Expand Down Expand Up @@ -106,6 +110,81 @@
}
}

#[cfg(not(feature = "std"))]
#[cfg(feature = "serde")]
mod serde_naive_time {
use super::*;
use core::fmt::{self, Write};
use serde::de::Visitor;

pub fn serialize<S>(v: &Option<NaiveTime>, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match v {
Some(time) => {
let mut str: heapless::String<32> = heapless::String::new();
write!(&mut str, "{}", time).map_err(serde::ser::Error::custom)?;
s.serialize_str(&str)

Check warning on line 128 in src/sentences/gga.rs

View check run for this annotation

Codecov / codecov/patch

src/sentences/gga.rs#L124-L128

Added lines #L124 - L128 were not covered by tests
}
None => s.serialize_none(),

Check warning on line 130 in src/sentences/gga.rs

View check run for this annotation

Codecov / codecov/patch

src/sentences/gga.rs#L130

Added line #L130 was not covered by tests
}
}

struct NaiveTimeVisitor;

impl<'de> Visitor<'de> for NaiveTimeVisitor {
type Value = NaiveTime;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("expecting NaiveTime in format H:M:S.f")

Check warning on line 140 in src/sentences/gga.rs

View check run for this annotation

Codecov / codecov/patch

src/sentences/gga.rs#L139-L140

Added lines #L139 - L140 were not covered by tests
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let time =
NaiveTime::parse_from_str(v, "%H:%M:%S%.f").map_err(serde::de::Error::custom)?;
return Ok(time);

Check warning on line 149 in src/sentences/gga.rs

View check run for this annotation

Codecov / codecov/patch

src/sentences/gga.rs#L147-L149

Added lines #L147 - L149 were not covered by tests
}
}

struct OptionVisitor;

impl<'de> Visitor<'de> for OptionVisitor {
type Value = Option<NaiveTime>;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("expecting Option<NaiveTime>")

Check warning on line 159 in src/sentences/gga.rs

View check run for this annotation

Codecov / codecov/patch

src/sentences/gga.rs#L158-L159

Added lines #L158 - L159 were not covered by tests
}

fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(None)

Check warning on line 166 in src/sentences/gga.rs

View check run for this annotation

Codecov / codecov/patch

src/sentences/gga.rs#L166

Added line #L166 was not covered by tests
}

fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: serde::Deserializer<'de>,
{
let time = deserializer
.deserialize_str(NaiveTimeVisitor)
.map_err(serde::de::Error::custom)?;
Ok(Some(time))

Check warning on line 176 in src/sentences/gga.rs

View check run for this annotation

Codecov / codecov/patch

src/sentences/gga.rs#L173-L176

Added lines #L173 - L176 were not covered by tests
}
}

pub fn deserialize<'de, D>(d: D) -> Result<Option<NaiveTime>, D::Error>
where
D: serde::Deserializer<'de>,
{
d.deserialize_option(OptionVisitor)

Check warning on line 184 in src/sentences/gga.rs

View check run for this annotation

Codecov / codecov/patch

src/sentences/gga.rs#L184

Added line #L184 was not covered by tests
}
}

#[cfg(test)]
mod tests {
use approx::assert_relative_eq;
Expand Down Expand Up @@ -162,4 +241,92 @@
let data = parse_gga(sentence).unwrap();
assert_eq!(data.fix_type.unwrap(), FixType::Invalid);
}

#[cfg(feature = "serde")]
#[test]
fn test_serialize_deserialize_gga_data_with_fix_time_milis() {
// hhmmss.sss
let data = parse_gga(NmeaSentence {
talker_id: "GP",
message_id: SentenceType::GGA,
data: "033745.222,5650.82344,N,03548.9778,E,1,07,1.8,101.2,M,14.7,M,,",
checksum: 0x57,
})
.unwrap();

assert_eq!(
data.fix_time,
Some(NaiveTime::from_hms_milli_opt(3, 37, 45, 222).expect("invalid time"))
);

let serialized = serde_json::to_string(&data).unwrap();
let gga: GgaData = serde_json::from_str(&serialized).unwrap();

assert_eq!(data.fix_time, gga.fix_time);
}

#[cfg(feature = "serde")]
#[test]
fn test_serialize_deserialize_gga_data_with_fix_time_nano() {
// hhmmss.sss
let data = parse_gga(NmeaSentence {
talker_id: "GP",
message_id: SentenceType::GGA,
data: "033745.222222222,5650.82344,N,03548.9778,E,1,07,1.8,101.2,M,14.7,M,,",
checksum: 0x57,
})
.unwrap();

assert_eq!(
data.fix_time,
Some(NaiveTime::from_hms_nano_opt(3, 37, 45, 222_222_222).expect("invalid time"))
);

let serialized = serde_json::to_string(&data).unwrap();
let gga: GgaData = serde_json::from_str(&serialized).unwrap();

assert_eq!(data.fix_time, gga.fix_time);
}

#[cfg(feature = "serde")]
#[test]
fn test_serialize_deserialize_gga_data_with_fix_time() {
// hhmmss.sss
let data = parse_gga(NmeaSentence {
talker_id: "GP",
message_id: SentenceType::GGA,
data: "033745.000,5650.82344,N,03548.9778,E,1,07,1.8,101.2,M,14.7,M,,",
checksum: 0x57,
})
.unwrap();

assert_eq!(
data.fix_time,
Some(NaiveTime::from_hms_opt(3, 37, 45).expect("invalid time"))
);

let serialized = serde_json::to_string(&data).unwrap();
let gga: GgaData = serde_json::from_str(&serialized).unwrap();

assert_eq!(data.fix_time, gga.fix_time);
}

#[cfg(feature = "serde")]
#[test]
fn test_serialize_deserialize_gga_data_without_fix_time() {
let data = parse_gga(NmeaSentence {
talker_id: "GP",
message_id: SentenceType::GGA,
data: ",5650.82344,N,03548.9778,E,1,07,1.8,101.2,M,14.7,M,,",
checksum: 0x57,
})
.unwrap();

assert_eq!(data.fix_time, None);

let serialized = serde_json::to_string(&data).unwrap();
let gga: GgaData = serde_json::from_str(&serialized).unwrap();

assert_eq!(data.fix_time, gga.fix_time);
}
}
Loading