1use crate::{Result, SenMLError, SenMLRecord};
4use serde::{Deserialize, Serialize};
5
6#[cfg(feature = "validation")]
7use validator::Validate;
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15#[serde(transparent)]
16pub struct SenMLPack {
17 pub records: Vec<SenMLRecord>,
19}
20
21#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
23#[cfg_attr(feature = "validation", derive(Validate))]
24pub struct BaseValues {
25 #[serde(skip_serializing_if = "Option::is_none")]
27 pub bn: Option<String>,
28
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub bt: Option<f64>,
32
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub bu: Option<String>,
36
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub bv: Option<f64>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub bs: Option<f64>,
44
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub bver: Option<i32>,
48}
49
50impl SenMLPack {
51 pub fn new() -> Self {
53 Self {
54 records: Vec::new(),
55 }
56 }
57
58 pub fn with_base_values(base: BaseValues) -> Self {
60 let mut base_record = SenMLRecord::default();
61
62 if let Some(bn) = base.bn {
64 base_record.n = Some(bn);
65 }
66
67 Self {
71 records: vec![base_record],
72 }
73 }
74
75 pub fn add_record(&mut self, record: SenMLRecord) {
77 self.records.push(record);
78 }
79
80 pub fn add_records<I>(&mut self, records: I)
82 where
83 I: IntoIterator<Item = SenMLRecord>,
84 {
85 self.records.extend(records);
86 }
87
88 pub fn base_values(&self) -> BaseValues {
90 self.records
91 .first()
92 .map(|record| self.extract_base_values(record))
93 .unwrap_or_default()
94 }
95
96 pub fn has_base_values(&self) -> bool {
98 if let Some(first) = self.records.first() {
99 first.n.as_ref().map_or(false, |n| n.ends_with('/'))
101 || first.t.is_some()
102 || first.u.is_some()
103 } else {
104 false
105 }
106 }
107
108 pub fn len(&self) -> usize {
110 self.records.len()
111 }
112
113 pub fn is_empty(&self) -> bool {
115 self.records.is_empty()
116 }
117
118 pub fn iter(&self) -> impl Iterator<Item = &SenMLRecord> {
120 self.records.iter()
121 }
122
123 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut SenMLRecord> {
125 self.records.iter_mut()
126 }
127
128 pub fn validate(&self) -> Result<()> {
130 if self.records.is_empty() {
131 return Err(SenMLError::validation("SenML pack cannot be empty"));
132 }
133
134 for (i, record) in self.records.iter().enumerate() {
136 record.validate().map_err(|e| {
137 SenMLError::validation(format!("Invalid record at index {}: {}", i, e))
138 })?;
139 }
140
141 let base = self.base_values();
143 if let Some(bt) = base.bt {
144 if !bt.is_finite() {
145 return Err(SenMLError::invalid_field_value("bt", &bt.to_string()));
146 }
147 }
148
149 if let Some(bv) = base.bv {
150 if !bv.is_finite() {
151 return Err(SenMLError::invalid_field_value("bv", &bv.to_string()));
152 }
153 }
154
155 if let Some(bs) = base.bs {
156 if !bs.is_finite() {
157 return Err(SenMLError::invalid_field_value("bs", &bs.to_string()));
158 }
159 }
160
161 Ok(())
162 }
163
164 pub fn normalize(&self) -> crate::normalize::NormalizedPack {
166 crate::normalize::NormalizedPack::from_pack(self)
167 }
168
169 fn extract_base_values(&self, record: &SenMLRecord) -> BaseValues {
171 BaseValues {
172 bn: record.n.clone(),
173 bt: record.t,
174 bu: record.u.clone(),
175 bv: record.v,
176 bs: record.s,
177 bver: None, }
179 }
180}
181
182impl Default for SenMLPack {
183 fn default() -> Self {
184 Self::new()
185 }
186}
187
188impl FromIterator<SenMLRecord> for SenMLPack {
189 fn from_iter<I: IntoIterator<Item = SenMLRecord>>(iter: I) -> Self {
190 Self {
191 records: iter.into_iter().collect(),
192 }
193 }
194}
195
196impl IntoIterator for SenMLPack {
197 type Item = SenMLRecord;
198 type IntoIter = std::vec::IntoIter<SenMLRecord>;
199
200 fn into_iter(self) -> Self::IntoIter {
201 self.records.into_iter()
202 }
203}
204
205impl<'a> IntoIterator for &'a SenMLPack {
206 type Item = &'a SenMLRecord;
207 type IntoIter = std::slice::Iter<'a, SenMLRecord>;
208
209 fn into_iter(self) -> Self::IntoIter {
210 self.records.iter()
211 }
212}
213
214impl SenMLPack {
216 #[cfg(feature = "json")]
218 pub fn to_json(&self) -> Result<String> {
219 serde_json::to_string(self).map_err(|e| SenMLError::serialization(e.to_string()))
220 }
221
222 #[cfg(feature = "json")]
224 pub fn to_json_pretty(&self) -> Result<String> {
225 serde_json::to_string_pretty(self).map_err(|e| SenMLError::serialization(e.to_string()))
226 }
227
228 #[cfg(feature = "json")]
230 pub fn from_json(json: &str) -> Result<Self> {
231 serde_json::from_str(json).map_err(|e| SenMLError::deserialization(e.to_string()))
232 }
233
234 #[cfg(feature = "cbor")]
236 pub fn to_cbor(&self) -> Result<Vec<u8>> {
237 let mut buffer = Vec::new();
238 ciborium::ser::into_writer(self, &mut buffer)
239 .map_err(|e| SenMLError::serialization(e.to_string()))?;
240 Ok(buffer)
241 }
242
243 #[cfg(feature = "cbor")]
245 pub fn from_cbor(bytes: &[u8]) -> Result<Self> {
246 ciborium::de::from_reader(bytes).map_err(|e| SenMLError::deserialization(e.to_string()))
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253 use crate::SenMLRecord;
254
255 #[test]
256 fn test_empty_pack_creation() {
257 let pack = SenMLPack::new();
258 assert!(pack.is_empty());
259 assert_eq!(pack.len(), 0);
260 }
261
262 #[test]
263 fn test_pack_with_records() {
264 let mut pack = SenMLPack::new();
265 pack.add_record(SenMLRecord::with_value("temperature", 22.5));
266 pack.add_record(SenMLRecord::with_value("humidity", 45.0));
267
268 assert_eq!(pack.len(), 2);
269 assert!(!pack.is_empty());
270 }
271
272 #[test]
273 fn test_pack_iteration() {
274 let records = vec![
275 SenMLRecord::with_value("temp", 20.0),
276 SenMLRecord::with_value("humidity", 50.0),
277 ];
278 let pack: SenMLPack = records.into_iter().collect();
279
280 let mut count = 0;
281 for record in &pack {
282 count += 1;
283 assert!(record.has_value());
284 }
285 assert_eq!(count, 2);
286 }
287
288 #[test]
289 fn test_pack_validation() {
290 let mut pack = SenMLPack::new();
291 pack.add_record(SenMLRecord::with_value("temp", 25.0));
292
293 assert!(pack.validate().is_ok());
294
295 let empty_pack = SenMLPack::new();
296 assert!(empty_pack.validate().is_err());
297 }
298
299 #[cfg(feature = "json")]
300 #[test]
301 fn test_json_serialization() {
302 let mut pack = SenMLPack::new();
303 pack.add_record(SenMLRecord::with_value("temperature", 22.5));
304
305 let json = pack.to_json().unwrap();
306 let deserialized = SenMLPack::from_json(&json).unwrap();
307
308 assert_eq!(pack, deserialized);
309 }
310
311 #[cfg(feature = "cbor")]
312 #[test]
313 fn test_cbor_serialization() {
314 let mut pack = SenMLPack::new();
315 pack.add_record(SenMLRecord::with_value("temperature", 22.5));
316
317 let cbor = pack.to_cbor().unwrap();
318 let deserialized = SenMLPack::from_cbor(&cbor).unwrap();
319
320 assert_eq!(pack, deserialized);
321 }
322}