lumen/chunks/
json_chunks.rs1use crate::check::Check;
9use crate::error::{Error, Result};
10use crate::json::{Meta, Profile, Timing};
11use serde::de::DeserializeOwned;
12use serde::Serialize;
13
14fn from_json<T: DeserializeOwned>(payload: &[u8]) -> Result<T> {
20 serde_json::from_slice(payload).map_err(|e| {
21 Error::new(
22 Check::MetaJson,
23 format!("the JSON payload is not a valid object: {e}"),
24 )
25 })
26}
27
28fn to_json<T: Serialize>(value: &T) -> Result<Vec<u8>> {
30 serde_json::to_vec(value).map_err(|e| {
31 Error::new(
32 Check::MetaJson,
33 format!("the JSON payload could not be serialized: {e}"),
34 )
35 })
36}
37
38pub fn parse_meta(payload: &[u8]) -> Result<Meta> {
40 from_json(payload)
41}
42
43pub fn meta_to_bytes(meta: &Meta) -> Result<Vec<u8>> {
45 to_json(meta)
46}
47
48pub fn parse_profile(payload: &[u8]) -> Result<Profile> {
50 from_json(payload)
51}
52
53pub fn profile_to_bytes(profile: &Profile) -> Result<Vec<u8>> {
55 to_json(profile)
56}
57
58pub fn parse_lrov(payload: &[u8]) -> Result<Timing> {
63 serde_json::from_slice(payload).map_err(|e| {
64 Error::new(
65 Check::LrovJson,
66 format!("the LROV payload is not a JSON object: {e}"),
67 )
68 })
69}
70
71pub fn lrov_to_bytes(timing: &Timing) -> Result<Vec<u8>> {
73 serde_json::to_vec(timing).map_err(|e| {
74 Error::new(
75 Check::LrovJson,
76 format!("the LROV payload could not be serialized: {e}"),
77 )
78 })
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn meta_round_trips_and_preserves_unknown_keys() {
87 let payload = br#"{
88 "meta_version": 1,
89 "normal_exposure_ms": 2500,
90 "bottom_exposure_ms": 30000,
91 "layer_height_um": 50,
92 "vendor_extension": {"a": 1},
93 "sectors": [{"sector_id": 1, "name": "Support", "normal_exposure_ms": 3000}]
94 }"#;
95 let meta = parse_meta(payload).unwrap();
96 assert_eq!(meta.meta_version, Some(1));
97 assert_eq!(meta.timing.normal_exposure_ms, Some(2500));
98 assert_eq!(
100 meta.timing.extra.get("vendor_extension"),
101 Some(&serde_json::json!({"a": 1}))
102 );
103
104 let sectors = meta.sectors.as_deref().expect("a sectors array");
105 assert_eq!(sectors[0].sector_id, 1);
106 assert_eq!(sectors[0].name.as_deref(), Some("Support"));
107 assert_eq!(sectors[0].timing.normal_exposure_ms, Some(3000));
108
109 let bytes = meta_to_bytes(&meta).unwrap();
110 assert_eq!(parse_meta(&bytes).unwrap(), meta);
111 }
112
113 #[test]
114 fn every_json_chunk_reports_its_group_on_bad_input() {
115 assert_eq!(
116 parse_meta(b"not json").unwrap_err().check(),
117 Check::MetaJson
118 );
119 assert_eq!(parse_profile(b"[").unwrap_err().check(), Check::MetaJson);
120 assert_eq!(parse_lrov(b"{}x").unwrap_err().check(), Check::LrovJson);
121 }
122
123 #[test]
124 fn an_lrov_payload_is_a_flat_timing_object() {
125 let timing = parse_lrov(br#"{"normal_exposure_ms": 2000, "light_pwm": 120}"#).unwrap();
126 assert_eq!(timing.normal_exposure_ms, Some(2000));
127 assert_eq!(timing.light_pwm, Some(120));
128 assert_eq!(timing.bottom_layer_count, None);
129 assert_eq!(
130 parse_lrov(&lrov_to_bytes(&timing).unwrap()).unwrap(),
131 timing
132 );
133 }
134}