1use serde_json::Value;
2
3use spvirit_codec::spvirit_encode::encode_size_pva;
4use spvirit_codec::spvd_decode::{FieldDesc, FieldType, StructureDesc, TypeCode};
5use spvirit_codec::spvd_encode::encode_structure_desc;
6
7pub fn encode_put_payload(
8 desc: &StructureDesc,
9 input: &Value,
10 is_be: bool,
11) -> Result<Vec<u8>, String> {
12 let root = normalize_root(desc, input)?;
13 let mut bitset: Vec<u8> = Vec::new();
14 let mut data: Vec<u8> = Vec::new();
15 let mut bit_offset = 1usize;
16 encode_structure_partial(desc, &root, &mut bitset, &mut bit_offset, &mut data, is_be)?;
17 if bitset.is_empty() {
18 return Err("no fields selected for PUT".to_string());
19 }
20
21 let mut out = Vec::new();
22 out.extend_from_slice(&encode_size_pva(bitset.len(), is_be));
23 out.extend_from_slice(&bitset);
24 out.extend_from_slice(&data);
25 Ok(out)
26}
27
28fn normalize_root(
29 desc: &StructureDesc,
30 input: &Value,
31) -> Result<serde_json::Map<String, Value>, String> {
32 if let Some(obj) = input.as_object() {
33 return Ok(obj.clone());
34 }
35 if desc.fields.iter().any(|f| f.name == "value") {
36 let mut map = serde_json::Map::new();
37 map.insert("value".to_string(), input.clone());
38 return Ok(map);
39 }
40 Err("non-object input requires a 'value' field in structure".to_string())
41}
42
43fn set_bit(bitset: &mut Vec<u8>, bit: usize) {
44 let idx = bit / 8;
45 let mask = 1u8 << (bit % 8);
46 if bitset.len() <= idx {
47 bitset.resize(idx + 1, 0);
48 }
49 bitset[idx] |= mask;
50}
51
52fn count_structure_fields(desc: &StructureDesc) -> usize {
53 let mut count = 0;
54 for field in &desc.fields {
55 count += 1;
56 if let FieldType::Structure(nested) = &field.field_type {
57 count += count_structure_fields(nested);
58 }
59 }
60 count
61}
62
63fn encode_structure_partial(
64 desc: &StructureDesc,
65 values: &serde_json::Map<String, Value>,
66 bitset: &mut Vec<u8>,
67 bit_offset: &mut usize,
68 data: &mut Vec<u8>,
69 is_be: bool,
70) -> Result<(), String> {
71 for key in values.keys() {
72 if !desc.fields.iter().any(|f| f.name == *key) {
73 return Err(format!("unknown field '{}' in input", key));
74 }
75 }
76 for field in &desc.fields {
77 let current_bit = *bit_offset;
78 *bit_offset += 1;
79
80 match &field.field_type {
81 FieldType::Structure(nested) => {
82 if let Some(val) = values.get(&field.name) {
83 if let Some(obj) = val.as_object() {
84 if obj.is_empty() {
85 return Err(format!("field '{}' object has no entries", field.name));
86 }
87 encode_structure_partial(nested, obj, bitset, bit_offset, data, is_be)?;
88 } else {
89 return Err(format!("field '{}' expects object", field.name));
90 }
91 } else {
92 let child_count = count_structure_fields(nested);
93 *bit_offset += child_count;
94 }
95 }
96 _ => {
97 if let Some(val) = values.get(&field.name) {
98 set_bit(bitset, current_bit);
99 encode_field_value(&field.field_type, val, data, is_be, &field.name)?;
100 }
101 }
102 }
103 }
104 Ok(())
105}
106
107fn encode_structure_full(
108 desc: &StructureDesc,
109 values: &serde_json::Map<String, Value>,
110 data: &mut Vec<u8>,
111 is_be: bool,
112) -> Result<(), String> {
113 for field in &desc.fields {
114 let val = values
115 .get(&field.name)
116 .ok_or_else(|| format!("missing field '{}' for full structure encoding", field.name))?;
117 match &field.field_type {
118 FieldType::Structure(nested) => {
119 let obj = val
120 .as_object()
121 .ok_or_else(|| format!("field '{}' expects object", field.name))?;
122 encode_structure_full(nested, obj, data, is_be)?;
123 }
124 _ => {
125 encode_field_value(&field.field_type, val, data, is_be, &field.name)?;
126 }
127 }
128 }
129 Ok(())
130}
131
132fn encode_field_value(
133 field_type: &FieldType,
134 val: &Value,
135 data: &mut Vec<u8>,
136 is_be: bool,
137 field_name: &str,
138) -> Result<(), String> {
139 match field_type {
140 FieldType::Scalar(tc) => encode_scalar_value(*tc, val, data, is_be, field_name),
141 FieldType::ScalarArray(tc) => encode_scalar_array(*tc, val, data, is_be, field_name),
142 FieldType::String | FieldType::BoundedString(_) => {
143 encode_string_value(val, data, is_be, field_name)
144 }
145 FieldType::StringArray => encode_string_array(val, data, is_be, field_name),
146 FieldType::Structure(_) => Err(format!(
147 "field '{}' expects object (nested structure)",
148 field_name
149 )),
150 FieldType::StructureArray(nested) => {
151 encode_structure_array(nested, val, data, is_be, field_name)
152 }
153 FieldType::Union(fields) => encode_union(fields, val, data, is_be, field_name),
154 FieldType::UnionArray(fields) => encode_union_array(fields, val, data, is_be, field_name),
155 FieldType::Variant => encode_variant(val, data, is_be, field_name),
156 FieldType::VariantArray => encode_variant_array(val, data, is_be, field_name),
157 }
158}
159
160fn encode_string_value(
161 val: &Value,
162 data: &mut Vec<u8>,
163 is_be: bool,
164 field_name: &str,
165) -> Result<(), String> {
166 let s = match val {
167 Value::String(s) => s.clone(),
168 _ => return Err(format!("field '{}' expects string", field_name)),
169 };
170 data.extend_from_slice(&encode_size_pva(s.len(), is_be));
171 data.extend_from_slice(s.as_bytes());
172 Ok(())
173}
174
175fn encode_scalar_value(
176 tc: TypeCode,
177 val: &Value,
178 data: &mut Vec<u8>,
179 is_be: bool,
180 field_name: &str,
181) -> Result<(), String> {
182 match tc {
183 TypeCode::Boolean => {
184 let b = match val {
185 Value::Bool(v) => *v,
186 Value::Number(n) => {
187 let v = n
188 .as_i64()
189 .ok_or_else(|| format!("field '{}' expects boolean", field_name))?;
190 v != 0
191 }
192 _ => return Err(format!("field '{}' expects boolean", field_name)),
193 };
194 data.push(if b { 1 } else { 0 });
195 Ok(())
196 }
197 TypeCode::Int8 => {
198 let v = json_to_int(val, field_name)?;
199 ensure_range_i64(v, i8::MIN as i64, i8::MAX as i64, field_name)?;
200 data.push(v as i8 as u8);
201 Ok(())
202 }
203 TypeCode::Int16 => {
204 let v = json_to_int(val, field_name)?;
205 ensure_range_i64(v, i16::MIN as i64, i16::MAX as i64, field_name)?;
206 let v = v as i16;
207 push_i16(data, v, is_be);
208 Ok(())
209 }
210 TypeCode::Int32 => {
211 let v = json_to_int(val, field_name)?;
212 ensure_range_i64(v, i32::MIN as i64, i32::MAX as i64, field_name)?;
213 let v = v as i32;
214 push_i32(data, v, is_be);
215 Ok(())
216 }
217 TypeCode::Int64 => {
218 let v = json_to_int(val, field_name)?;
219 push_i64(data, v, is_be);
220 Ok(())
221 }
222 TypeCode::UInt8 => {
223 let v = json_to_uint(val, field_name)?;
224 ensure_range_u64(v, u8::MIN as u64, u8::MAX as u64, field_name)?;
225 let v = v as u8;
226 data.push(v);
227 Ok(())
228 }
229 TypeCode::UInt16 => {
230 let v = json_to_uint(val, field_name)?;
231 ensure_range_u64(v, u16::MIN as u64, u16::MAX as u64, field_name)?;
232 let v = v as u16;
233 push_u16(data, v, is_be);
234 Ok(())
235 }
236 TypeCode::UInt32 => {
237 let v = json_to_uint(val, field_name)?;
238 ensure_range_u64(v, u32::MIN as u64, u32::MAX as u64, field_name)?;
239 let v = v as u32;
240 push_u32(data, v, is_be);
241 Ok(())
242 }
243 TypeCode::UInt64 => {
244 let v = json_to_uint(val, field_name)?;
245 push_u64(data, v, is_be);
246 Ok(())
247 }
248 TypeCode::Float32 => {
249 let v = json_to_float(val, field_name)? as f32;
250 push_f32(data, v, is_be);
251 Ok(())
252 }
253 TypeCode::Float64 => {
254 let v = json_to_float(val, field_name)?;
255 push_f64(data, v, is_be);
256 Ok(())
257 }
258 TypeCode::String => encode_string_value(val, data, is_be, field_name),
259 _ => Err(format!(
260 "field '{}' uses unsupported scalar type",
261 field_name
262 )),
263 }
264}
265
266fn encode_scalar_array(
267 tc: TypeCode,
268 val: &Value,
269 data: &mut Vec<u8>,
270 is_be: bool,
271 field_name: &str,
272) -> Result<(), String> {
273 let items = val
274 .as_array()
275 .ok_or_else(|| format!("field '{}' expects array", field_name))?;
276 data.extend_from_slice(&encode_size_pva(items.len(), is_be));
277 for item in items {
278 encode_scalar_value(tc, item, data, is_be, field_name)?;
279 }
280 Ok(())
281}
282
283fn encode_string_array(
284 val: &Value,
285 data: &mut Vec<u8>,
286 is_be: bool,
287 field_name: &str,
288) -> Result<(), String> {
289 let items = val
290 .as_array()
291 .ok_or_else(|| format!("field '{}' expects array", field_name))?;
292 data.extend_from_slice(&encode_size_pva(items.len(), is_be));
293 for item in items {
294 encode_string_value(item, data, is_be, field_name)?;
295 }
296 Ok(())
297}
298
299fn encode_structure_array(
300 desc: &StructureDesc,
301 val: &Value,
302 data: &mut Vec<u8>,
303 is_be: bool,
304 field_name: &str,
305) -> Result<(), String> {
306 let items = val
307 .as_array()
308 .ok_or_else(|| format!("field '{}' expects array", field_name))?;
309 data.extend_from_slice(&encode_size_pva(items.len(), is_be));
310 for item in items {
311 let obj = item
312 .as_object()
313 .ok_or_else(|| format!("field '{}' expects array of objects", field_name))?;
314 encode_structure_full(desc, obj, data, is_be)?;
315 }
316 Ok(())
317}
318
319fn encode_union(
320 fields: &[FieldDesc],
321 val: &Value,
322 data: &mut Vec<u8>,
323 is_be: bool,
324 field_name: &str,
325) -> Result<(), String> {
326 let obj = val
327 .as_object()
328 .ok_or_else(|| format!("field '{}' expects object for union", field_name))?;
329 if obj.len() != 1 {
330 return Err(format!(
331 "field '{}' union expects exactly one selected member",
332 field_name
333 ));
334 }
335 let (selected_name, selected_val) = obj.iter().next().expect("len checked");
336 let Some((index, field_desc)) = fields
337 .iter()
338 .enumerate()
339 .find(|(_, f)| f.name == *selected_name)
340 else {
341 return Err(format!(
342 "field '{}' union member '{}' is unknown",
343 field_name, selected_name
344 ));
345 };
346 data.extend_from_slice(&encode_size_pva(index, is_be));
347 encode_field_value(
348 &field_desc.field_type,
349 selected_val,
350 data,
351 is_be,
352 selected_name,
353 )
354}
355
356fn encode_union_array(
357 fields: &[FieldDesc],
358 val: &Value,
359 data: &mut Vec<u8>,
360 is_be: bool,
361 field_name: &str,
362) -> Result<(), String> {
363 let items = val
364 .as_array()
365 .ok_or_else(|| format!("field '{}' expects array for union[]", field_name))?;
366 data.extend_from_slice(&encode_size_pva(items.len(), is_be));
367 for item in items {
368 encode_union(fields, item, data, is_be, field_name)?;
369 }
370 Ok(())
371}
372
373fn encode_variant(
374 val: &Value,
375 data: &mut Vec<u8>,
376 is_be: bool,
377 field_name: &str,
378) -> Result<(), String> {
379 match val {
380 Value::Null => {
381 data.push(0xFF);
382 Ok(())
383 }
384 Value::Bool(_) => {
385 data.push(TypeCode::Boolean as u8);
386 encode_scalar_value(TypeCode::Boolean, val, data, is_be, field_name)
387 }
388 Value::Number(n) => {
389 if n.as_i64().is_some() || n.as_u64().is_some() {
390 data.push(TypeCode::Int32 as u8);
391 encode_scalar_value(TypeCode::Int32, val, data, is_be, field_name)
392 } else {
393 data.push(TypeCode::Float64 as u8);
394 encode_scalar_value(TypeCode::Float64, val, data, is_be, field_name)
395 }
396 }
397 Value::String(_) => {
398 data.push(TypeCode::String as u8);
399 encode_string_value(val, data, is_be, field_name)
400 }
401 Value::Array(arr) => {
402 if arr.is_empty() {
403 data.push((TypeCode::Float64 as u8) | 0x08);
404 data.extend_from_slice(&encode_size_pva(0, is_be));
405 return Ok(());
406 }
407 if arr.iter().all(Value::is_string) {
408 data.push(0x68); return encode_string_array(val, data, is_be, field_name);
410 }
411 data.push((TypeCode::Float64 as u8) | 0x08);
412 encode_scalar_array(TypeCode::Float64, val, data, is_be, field_name)
413 }
414 Value::Object(map) => {
415 let mut desc_fields = Vec::new();
416 for key in map.keys() {
417 desc_fields.push(FieldDesc {
418 name: key.clone(),
419 field_type: FieldType::String,
420 });
421 }
422 let desc = StructureDesc {
423 struct_id: None,
424 fields: desc_fields,
425 };
426 data.push(0x80);
427 data.extend_from_slice(&encode_structure_desc(&desc, is_be));
428 for key in map.keys() {
429 let v = map.get(key).expect("iter key");
430 let s = match v {
431 Value::String(s) => s.clone(),
432 Value::Number(n) => n.to_string(),
433 Value::Bool(b) => {
434 if *b {
435 "true".to_string()
436 } else {
437 "false".to_string()
438 }
439 }
440 Value::Null => String::new(),
441 _ => v.to_string(),
442 };
443 data.extend_from_slice(&encode_size_pva(s.len(), is_be));
444 data.extend_from_slice(s.as_bytes());
445 }
446 Ok(())
447 }
448 }
449}
450
451fn encode_variant_array(
452 val: &Value,
453 data: &mut Vec<u8>,
454 is_be: bool,
455 field_name: &str,
456) -> Result<(), String> {
457 let items = val
458 .as_array()
459 .ok_or_else(|| format!("field '{}' expects array for any[]", field_name))?;
460 data.extend_from_slice(&encode_size_pva(items.len(), is_be));
461 for item in items {
462 encode_variant(item, data, is_be, field_name)?;
463 }
464 Ok(())
465}
466
467fn json_to_int(val: &Value, field_name: &str) -> Result<i64, String> {
468 match val {
469 Value::Number(n) => {
470 if let Some(i) = n.as_i64() {
471 Ok(i)
472 } else if let Some(u) = n.as_u64() {
473 i64::try_from(u).map_err(|_| format!("field '{}' out of range", field_name))
474 } else if let Some(f) = n.as_f64() {
475 if (f.fract()).abs() > f64::EPSILON {
476 Err(format!("field '{}' expects integer, got {}", field_name, f))
477 } else {
478 Ok(f as i64)
479 }
480 } else {
481 Err(format!("field '{}' expects integer", field_name))
482 }
483 }
484 Value::String(s) => s
485 .parse::<i64>()
486 .map_err(|_| format!("field '{}' expects integer", field_name)),
487 _ => Err(format!("field '{}' expects integer", field_name)),
488 }
489}
490
491fn json_to_uint(val: &Value, field_name: &str) -> Result<u64, String> {
492 match val {
493 Value::Number(n) => {
494 if let Some(u) = n.as_u64() {
495 Ok(u)
496 } else if let Some(i) = n.as_i64() {
497 if i < 0 {
498 Err(format!("field '{}' expects unsigned integer", field_name))
499 } else {
500 Ok(i as u64)
501 }
502 } else if let Some(f) = n.as_f64() {
503 if (f.fract()).abs() > f64::EPSILON || f < 0.0 {
504 Err(format!("field '{}' expects unsigned integer", field_name))
505 } else {
506 Ok(f as u64)
507 }
508 } else {
509 Err(format!("field '{}' expects unsigned integer", field_name))
510 }
511 }
512 Value::String(s) => s
513 .parse::<u64>()
514 .map_err(|_| format!("field '{}' expects unsigned integer", field_name)),
515 _ => Err(format!("field '{}' expects unsigned integer", field_name)),
516 }
517}
518
519fn json_to_float(val: &Value, field_name: &str) -> Result<f64, String> {
520 match val {
521 Value::Number(n) => n
522 .as_f64()
523 .ok_or_else(|| format!("field '{}' expects float", field_name)),
524 Value::String(s) => s
525 .parse::<f64>()
526 .map_err(|_| format!("field '{}' expects float", field_name)),
527 _ => Err(format!("field '{}' expects float", field_name)),
528 }
529}
530
531fn ensure_range_i64(value: i64, min: i64, max: i64, field_name: &str) -> Result<(), String> {
532 if value < min || value > max {
533 Err(format!("field '{}' out of range", field_name))
534 } else {
535 Ok(())
536 }
537}
538
539fn ensure_range_u64(value: u64, min: u64, max: u64, field_name: &str) -> Result<(), String> {
540 if value < min || value > max {
541 Err(format!("field '{}' out of range", field_name))
542 } else {
543 Ok(())
544 }
545}
546
547fn push_i16(data: &mut Vec<u8>, v: i16, is_be: bool) {
548 let bytes = if is_be {
549 v.to_be_bytes()
550 } else {
551 v.to_le_bytes()
552 };
553 data.extend_from_slice(&bytes);
554}
555
556fn push_i32(data: &mut Vec<u8>, v: i32, is_be: bool) {
557 let bytes = if is_be {
558 v.to_be_bytes()
559 } else {
560 v.to_le_bytes()
561 };
562 data.extend_from_slice(&bytes);
563}
564
565fn push_i64(data: &mut Vec<u8>, v: i64, is_be: bool) {
566 let bytes = if is_be {
567 v.to_be_bytes()
568 } else {
569 v.to_le_bytes()
570 };
571 data.extend_from_slice(&bytes);
572}
573
574fn push_u16(data: &mut Vec<u8>, v: u16, is_be: bool) {
575 let bytes = if is_be {
576 v.to_be_bytes()
577 } else {
578 v.to_le_bytes()
579 };
580 data.extend_from_slice(&bytes);
581}
582
583fn push_u32(data: &mut Vec<u8>, v: u32, is_be: bool) {
584 let bytes = if is_be {
585 v.to_be_bytes()
586 } else {
587 v.to_le_bytes()
588 };
589 data.extend_from_slice(&bytes);
590}
591
592fn push_u64(data: &mut Vec<u8>, v: u64, is_be: bool) {
593 let bytes = if is_be {
594 v.to_be_bytes()
595 } else {
596 v.to_le_bytes()
597 };
598 data.extend_from_slice(&bytes);
599}
600
601fn push_f32(data: &mut Vec<u8>, v: f32, is_be: bool) {
602 let bytes = if is_be {
603 v.to_be_bytes()
604 } else {
605 v.to_le_bytes()
606 };
607 data.extend_from_slice(&bytes);
608}
609
610fn push_f64(data: &mut Vec<u8>, v: f64, is_be: bool) {
611 let bytes = if is_be {
612 v.to_be_bytes()
613 } else {
614 v.to_le_bytes()
615 };
616 data.extend_from_slice(&bytes);
617}
618
619#[cfg(test)]
620mod tests {
621 use super::*;
622 use spvirit_codec::spvd_decode::{FieldDesc, FieldType, StructureDesc, TypeCode};
623
624 #[test]
625 fn encode_put_scalar_value_bitset() {
626 let desc = StructureDesc {
627 struct_id: None,
628 fields: vec![FieldDesc {
629 name: "value".to_string(),
630 field_type: FieldType::Scalar(TypeCode::Int32),
631 }],
632 };
633 let input = serde_json::json!(1234);
634 let payload = encode_put_payload(&desc, &input, false).expect("payload");
635 assert_eq!(payload[0], 0x01); assert_eq!(payload[1], 0x02); let val_bytes = &payload[2..6];
638 assert_eq!(val_bytes, &1234i32.to_le_bytes());
639 }
640
641 #[test]
642 fn encode_put_nested_partial() {
643 let nested = StructureDesc {
644 struct_id: None,
645 fields: vec![FieldDesc {
646 name: "unit".to_string(),
647 field_type: FieldType::String,
648 }],
649 };
650 let desc = StructureDesc {
651 struct_id: None,
652 fields: vec![
653 FieldDesc {
654 name: "value".to_string(),
655 field_type: FieldType::Scalar(TypeCode::Float64),
656 },
657 FieldDesc {
658 name: "meta".to_string(),
659 field_type: FieldType::Structure(nested),
660 },
661 ],
662 };
663 let input = serde_json::json!({"value": 1.5, "meta": {"unit": "A"}});
664 let payload = encode_put_payload(&desc, &input, false).expect("payload");
665 assert_eq!(payload[0], 0x01);
666 assert_eq!(payload[1], 0x0A); let mut expected = Vec::new();
668 expected.extend_from_slice(&1.5f64.to_le_bytes());
669 expected.extend_from_slice(&encode_size_pva(1, false));
670 expected.extend_from_slice(b"A");
671 assert_eq!(&payload[2..], expected.as_slice());
672 }
673}