use crate::check::Check;
use crate::error::{Error, Result};
use crate::json::{Meta, Profile, Timing};
use serde::de::DeserializeOwned;
use serde::Serialize;
fn from_json<T: DeserializeOwned>(payload: &[u8]) -> Result<T> {
serde_json::from_slice(payload).map_err(|e| {
Error::new(
Check::MetaJson,
format!("the JSON payload is not a valid object: {e}"),
)
})
}
fn to_json<T: Serialize>(value: &T) -> Result<Vec<u8>> {
serde_json::to_vec(value).map_err(|e| {
Error::new(
Check::MetaJson,
format!("the JSON payload could not be serialized: {e}"),
)
})
}
pub fn parse_meta(payload: &[u8]) -> Result<Meta> {
from_json(payload)
}
pub fn meta_to_bytes(meta: &Meta) -> Result<Vec<u8>> {
to_json(meta)
}
pub fn parse_profile(payload: &[u8]) -> Result<Profile> {
from_json(payload)
}
pub fn profile_to_bytes(profile: &Profile) -> Result<Vec<u8>> {
to_json(profile)
}
pub fn parse_lrov(payload: &[u8]) -> Result<Timing> {
serde_json::from_slice(payload).map_err(|e| {
Error::new(
Check::LrovJson,
format!("the LROV payload is not a JSON object: {e}"),
)
})
}
pub fn lrov_to_bytes(timing: &Timing) -> Result<Vec<u8>> {
serde_json::to_vec(timing).map_err(|e| {
Error::new(
Check::LrovJson,
format!("the LROV payload could not be serialized: {e}"),
)
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn meta_round_trips_and_preserves_unknown_keys() {
let payload = br#"{
"meta_version": 1,
"normal_exposure_ms": 2500,
"bottom_exposure_ms": 30000,
"layer_height_um": 50,
"vendor_extension": {"a": 1},
"sectors": [{"sector_id": 1, "name": "Support", "normal_exposure_ms": 3000}]
}"#;
let meta = parse_meta(payload).unwrap();
assert_eq!(meta.meta_version, Some(1));
assert_eq!(meta.timing.normal_exposure_ms, Some(2500));
assert_eq!(
meta.timing.extra.get("vendor_extension"),
Some(&serde_json::json!({"a": 1}))
);
let sectors = meta.sectors.as_deref().expect("a sectors array");
assert_eq!(sectors[0].sector_id, 1);
assert_eq!(sectors[0].name.as_deref(), Some("Support"));
assert_eq!(sectors[0].timing.normal_exposure_ms, Some(3000));
let bytes = meta_to_bytes(&meta).unwrap();
assert_eq!(parse_meta(&bytes).unwrap(), meta);
}
#[test]
fn every_json_chunk_reports_its_group_on_bad_input() {
assert_eq!(
parse_meta(b"not json").unwrap_err().check(),
Check::MetaJson
);
assert_eq!(parse_profile(b"[").unwrap_err().check(), Check::MetaJson);
assert_eq!(parse_lrov(b"{}x").unwrap_err().check(), Check::LrovJson);
}
#[test]
fn an_lrov_payload_is_a_flat_timing_object() {
let timing = parse_lrov(br#"{"normal_exposure_ms": 2000, "light_pwm": 120}"#).unwrap();
assert_eq!(timing.normal_exposure_ms, Some(2000));
assert_eq!(timing.light_pwm, Some(120));
assert_eq!(timing.bottom_layer_count, None);
assert_eq!(
parse_lrov(&lrov_to_bytes(&timing).unwrap()).unwrap(),
timing
);
}
}