1use crate::{FieldId, LnmpValue};
4
5#[derive(Debug, Clone, PartialEq)]
7pub struct LnmpField {
8 pub fid: FieldId,
10 pub value: LnmpValue,
12}
13
14#[derive(Debug, Clone, PartialEq, Default)]
16pub struct LnmpRecord {
17 fields: Vec<LnmpField>,
18}
19
20impl LnmpRecord {
21 pub fn new() -> Self {
23 Self::default()
24 }
25
26 pub fn add_field(&mut self, field: LnmpField) {
28 self.fields.push(field);
29 }
30
31 pub fn get_field(&self, fid: FieldId) -> Option<&LnmpField> {
33 self.fields.iter().find(|f| f.fid == fid)
34 }
35
36 pub fn remove_field(&mut self, fid: FieldId) {
38 self.fields.retain(|f| f.fid != fid);
39 }
40
41 pub fn fields(&self) -> &[LnmpField] {
43 &self.fields
44 }
45
46 pub fn into_fields(self) -> Vec<LnmpField> {
48 self.fields
49 }
50
51 pub fn sorted_fields(&self) -> Vec<LnmpField> {
53 let mut sorted = self.fields.clone();
54 sorted.sort_by_key(|f| f.fid);
55 sorted
56 }
57
58 pub fn from_sorted_fields(fields: Vec<LnmpField>) -> Self {
60 Self { fields }
61 }
62
63 pub fn validate_with_limits(
65 &self,
66 limits: &crate::limits::StructuralLimits,
67 ) -> Result<(), crate::limits::StructuralError> {
68 limits.validate_record(self)
69 }
70}
71
72#[cfg(test)]
73#[allow(clippy::approx_constant)]
74mod tests {
75 use super::*;
76
77 #[test]
78 fn test_new_record_is_empty() {
79 let record = LnmpRecord::new();
80 assert_eq!(record.fields().len(), 0);
81 }
82
83 #[test]
84 fn test_add_field() {
85 let mut record = LnmpRecord::new();
86 record.add_field(LnmpField {
87 fid: 12,
88 value: LnmpValue::Int(14532),
89 });
90
91 assert_eq!(record.fields().len(), 1);
92 assert_eq!(record.fields()[0].fid, 12);
93 }
94
95 #[test]
96 fn test_get_field() {
97 let mut record = LnmpRecord::new();
98 record.add_field(LnmpField {
99 fid: 12,
100 value: LnmpValue::Int(14532),
101 });
102 record.add_field(LnmpField {
103 fid: 7,
104 value: LnmpValue::Bool(true),
105 });
106
107 let field = record.get_field(12);
108 assert!(field.is_some());
109 assert_eq!(field.unwrap().value, LnmpValue::Int(14532));
110
111 let missing = record.get_field(99);
112 assert!(missing.is_none());
113 }
114
115 #[test]
116 fn test_get_field_with_duplicates() {
117 let mut record = LnmpRecord::new();
118 record.add_field(LnmpField {
119 fid: 5,
120 value: LnmpValue::String("first".to_string()),
121 });
122 record.add_field(LnmpField {
123 fid: 5,
124 value: LnmpValue::String("second".to_string()),
125 });
126
127 let field = record.get_field(5);
129 assert!(field.is_some());
130 assert_eq!(field.unwrap().value, LnmpValue::String("first".to_string()));
131 }
132
133 #[test]
134 fn test_fields_iteration() {
135 let mut record = LnmpRecord::new();
136 record.add_field(LnmpField {
137 fid: 1,
138 value: LnmpValue::Int(100),
139 });
140 record.add_field(LnmpField {
141 fid: 2,
142 value: LnmpValue::Float(3.14),
143 });
144 record.add_field(LnmpField {
145 fid: 3,
146 value: LnmpValue::Bool(false),
147 });
148
149 let fields = record.fields();
150 assert_eq!(fields.len(), 3);
151 assert_eq!(fields[0].fid, 1);
152 assert_eq!(fields[1].fid, 2);
153 assert_eq!(fields[2].fid, 3);
154 }
155
156 #[test]
157 fn test_into_fields() {
158 let mut record = LnmpRecord::new();
159 record.add_field(LnmpField {
160 fid: 10,
161 value: LnmpValue::String("test".to_string()),
162 });
163
164 let fields = record.into_fields();
165 assert_eq!(fields.len(), 1);
166 assert_eq!(fields[0].fid, 10);
167 }
168
169 #[test]
170 fn test_lnmp_value_equality() {
171 assert_eq!(LnmpValue::Int(42), LnmpValue::Int(42));
172 assert_ne!(LnmpValue::Int(42), LnmpValue::Int(43));
173
174 assert_eq!(LnmpValue::Float(3.14), LnmpValue::Float(3.14));
175
176 assert_eq!(LnmpValue::Bool(true), LnmpValue::Bool(true));
177 assert_ne!(LnmpValue::Bool(true), LnmpValue::Bool(false));
178
179 assert_eq!(
180 LnmpValue::String("hello".to_string()),
181 LnmpValue::String("hello".to_string())
182 );
183
184 assert_eq!(
185 LnmpValue::StringArray(vec!["a".to_string(), "b".to_string()]),
186 LnmpValue::StringArray(vec!["a".to_string(), "b".to_string()])
187 );
188 }
189
190 #[test]
191 fn test_lnmp_value_clone() {
192 let original = LnmpValue::StringArray(vec!["test".to_string()]);
193 let cloned = original.clone();
194 assert_eq!(original, cloned);
195 }
196
197 #[test]
198 fn test_empty_record() {
199 let record = LnmpRecord::new();
200 assert_eq!(record.fields().len(), 0);
201 assert!(record.get_field(1).is_none());
202 }
203
204 #[test]
205 fn test_record_with_all_value_types() {
206 let mut record = LnmpRecord::new();
207
208 record.add_field(LnmpField {
209 fid: 1,
210 value: LnmpValue::Int(-42),
211 });
212 record.add_field(LnmpField {
213 fid: 2,
214 value: LnmpValue::Float(3.14159),
215 });
216 record.add_field(LnmpField {
217 fid: 3,
218 value: LnmpValue::Bool(true),
219 });
220 record.add_field(LnmpField {
221 fid: 4,
222 value: LnmpValue::String("hello world".to_string()),
223 });
224 record.add_field(LnmpField {
225 fid: 5,
226 value: LnmpValue::StringArray(vec!["admin".to_string(), "dev".to_string()]),
227 });
228
229 assert_eq!(record.fields().len(), 5);
230 assert_eq!(record.get_field(1).unwrap().value, LnmpValue::Int(-42));
231 assert_eq!(
232 record.get_field(2).unwrap().value,
233 LnmpValue::Float(3.14159)
234 );
235 assert_eq!(record.get_field(3).unwrap().value, LnmpValue::Bool(true));
236 assert_eq!(
237 record.get_field(4).unwrap().value,
238 LnmpValue::String("hello world".to_string())
239 );
240 assert_eq!(
241 record.get_field(5).unwrap().value,
242 LnmpValue::StringArray(vec!["admin".to_string(), "dev".to_string()])
243 );
244 }
245
246 #[test]
247 fn test_sorted_fields_basic() {
248 let mut record = LnmpRecord::new();
249 record.add_field(LnmpField {
250 fid: 23,
251 value: LnmpValue::Int(3),
252 });
253 record.add_field(LnmpField {
254 fid: 7,
255 value: LnmpValue::Int(2),
256 });
257 record.add_field(LnmpField {
258 fid: 12,
259 value: LnmpValue::Int(1),
260 });
261
262 let sorted = record.sorted_fields();
263 assert_eq!(sorted.len(), 3);
264 assert_eq!(sorted[0].fid, 7);
265 assert_eq!(sorted[1].fid, 12);
266 assert_eq!(sorted[2].fid, 23);
267 }
268
269 #[test]
270 fn test_sorted_fields_preserves_duplicate_order() {
271 let mut record = LnmpRecord::new();
272 record.add_field(LnmpField {
273 fid: 5,
274 value: LnmpValue::String("first".to_string()),
275 });
276 record.add_field(LnmpField {
277 fid: 10,
278 value: LnmpValue::Int(100),
279 });
280 record.add_field(LnmpField {
281 fid: 5,
282 value: LnmpValue::String("second".to_string()),
283 });
284
285 let sorted = record.sorted_fields();
286 assert_eq!(sorted.len(), 3);
287 assert_eq!(sorted[0].fid, 5);
288 assert_eq!(sorted[0].value, LnmpValue::String("first".to_string()));
289 assert_eq!(sorted[1].fid, 5);
290 assert_eq!(sorted[1].value, LnmpValue::String("second".to_string()));
291 assert_eq!(sorted[2].fid, 10);
292 }
293
294 #[test]
295 fn test_sorted_fields_already_sorted() {
296 let mut record = LnmpRecord::new();
297 record.add_field(LnmpField {
298 fid: 1,
299 value: LnmpValue::Int(1),
300 });
301 record.add_field(LnmpField {
302 fid: 2,
303 value: LnmpValue::Int(2),
304 });
305 record.add_field(LnmpField {
306 fid: 3,
307 value: LnmpValue::Int(3),
308 });
309
310 let sorted = record.sorted_fields();
311 assert_eq!(sorted.len(), 3);
312 assert_eq!(sorted[0].fid, 1);
313 assert_eq!(sorted[1].fid, 2);
314 assert_eq!(sorted[2].fid, 3);
315 }
316
317 #[test]
318 fn test_sorted_fields_empty_record() {
319 let record = LnmpRecord::new();
320 let sorted = record.sorted_fields();
321 assert_eq!(sorted.len(), 0);
322 }
323
324 #[test]
325 fn test_sorted_fields_does_not_modify_original() {
326 let mut record = LnmpRecord::new();
327 record.add_field(LnmpField {
328 fid: 23,
329 value: LnmpValue::Int(3),
330 });
331 record.add_field(LnmpField {
332 fid: 7,
333 value: LnmpValue::Int(2),
334 });
335
336 let _sorted = record.sorted_fields();
337
338 assert_eq!(record.fields()[0].fid, 23);
340 assert_eq!(record.fields()[1].fid, 7);
341 }
342}