1use crate::ptp::codes::{DevicePropertyCode, PropertyDataType};
10use crate::ptp::pack::{
11 pack_i16, pack_i32, pack_i64, pack_i8, pack_string, pack_u16, pack_u32, pack_u64, pack_u8,
12 unpack_i16, unpack_i32, unpack_i64, unpack_i8, unpack_string, unpack_u16, unpack_u64,
13 unpack_u8,
14};
15
16#[derive(Debug, Clone, PartialEq)]
23pub enum PropertyValue {
24 Int8(i8),
26 Uint8(u8),
28 Int16(i16),
30 Uint16(u16),
32 Int32(i32),
34 Uint32(u32),
36 Int64(i64),
38 Uint64(u64),
40 String(String),
42}
43
44impl PropertyValue {
45 pub fn to_bytes(&self) -> Vec<u8> {
47 match self {
48 PropertyValue::Int8(v) => pack_i8(*v).to_vec(),
49 PropertyValue::Uint8(v) => pack_u8(*v).to_vec(),
50 PropertyValue::Int16(v) => pack_i16(*v).to_vec(),
51 PropertyValue::Uint16(v) => pack_u16(*v).to_vec(),
52 PropertyValue::Int32(v) => pack_i32(*v).to_vec(),
53 PropertyValue::Uint32(v) => pack_u32(*v).to_vec(),
54 PropertyValue::Int64(v) => pack_i64(*v).to_vec(),
55 PropertyValue::Uint64(v) => pack_u64(*v).to_vec(),
56 PropertyValue::String(v) => pack_string(v),
57 }
58 }
59
60 pub fn from_bytes(
64 buf: &[u8],
65 data_type: PropertyDataType,
66 ) -> Result<(Self, usize), crate::Error> {
67 match data_type {
68 PropertyDataType::Int8 => {
69 let val = unpack_i8(buf)?;
70 Ok((PropertyValue::Int8(val), 1))
71 }
72 PropertyDataType::Uint8 => {
73 let val = unpack_u8(buf)?;
74 Ok((PropertyValue::Uint8(val), 1))
75 }
76 PropertyDataType::Int16 => {
77 let val = unpack_i16(buf)?;
78 Ok((PropertyValue::Int16(val), 2))
79 }
80 PropertyDataType::Uint16 => {
81 let val = unpack_u16(buf)?;
82 Ok((PropertyValue::Uint16(val), 2))
83 }
84 PropertyDataType::Int32 => {
85 let val = unpack_i32(buf)?;
86 Ok((PropertyValue::Int32(val), 4))
87 }
88 PropertyDataType::Uint32 => {
89 let val = crate::ptp::pack::unpack_u32(buf)?;
90 Ok((PropertyValue::Uint32(val), 4))
91 }
92 PropertyDataType::Int64 => {
93 let val = unpack_i64(buf)?;
94 Ok((PropertyValue::Int64(val), 8))
95 }
96 PropertyDataType::Uint64 => {
97 let val = unpack_u64(buf)?;
98 Ok((PropertyValue::Uint64(val), 8))
99 }
100 PropertyDataType::String => {
101 let (val, consumed) = unpack_string(buf)?;
102 Ok((PropertyValue::String(val), consumed))
103 }
104 PropertyDataType::Undefined
105 | PropertyDataType::Int128
106 | PropertyDataType::Uint128
107 | PropertyDataType::Unknown(_) => Err(crate::Error::invalid_data(format!(
108 "unsupported property data type: {:?}",
109 data_type
110 ))),
111 }
112 }
113
114 #[must_use]
116 pub fn data_type(&self) -> PropertyDataType {
117 match self {
118 PropertyValue::Int8(_) => PropertyDataType::Int8,
119 PropertyValue::Uint8(_) => PropertyDataType::Uint8,
120 PropertyValue::Int16(_) => PropertyDataType::Int16,
121 PropertyValue::Uint16(_) => PropertyDataType::Uint16,
122 PropertyValue::Int32(_) => PropertyDataType::Int32,
123 PropertyValue::Uint32(_) => PropertyDataType::Uint32,
124 PropertyValue::Int64(_) => PropertyDataType::Int64,
125 PropertyValue::Uint64(_) => PropertyDataType::Uint64,
126 PropertyValue::String(_) => PropertyDataType::String,
127 }
128 }
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
137pub enum PropertyFormType {
138 #[default]
140 None,
141 Range,
143 Enumeration,
145 Unknown(u8),
147}
148
149impl PropertyFormType {
150 #[must_use]
152 pub fn from_code(code: u8) -> Self {
153 match code {
154 0x00 => PropertyFormType::None,
155 0x01 => PropertyFormType::Range,
156 0x02 => PropertyFormType::Enumeration,
157 _ => PropertyFormType::Unknown(code),
158 }
159 }
160
161 #[must_use]
163 pub fn to_code(self) -> u8 {
164 match self {
165 PropertyFormType::None => 0x00,
166 PropertyFormType::Range => 0x01,
167 PropertyFormType::Enumeration => 0x02,
168 PropertyFormType::Unknown(code) => code,
169 }
170 }
171}
172
173#[derive(Debug, Clone, PartialEq)]
179pub struct PropertyRange {
180 pub min: PropertyValue,
182 pub max: PropertyValue,
184 pub step: PropertyValue,
186}
187
188impl PropertyRange {
189 pub fn from_bytes(
193 buf: &[u8],
194 data_type: PropertyDataType,
195 ) -> Result<(Self, usize), crate::Error> {
196 let mut offset = 0;
197
198 let (min, consumed) = PropertyValue::from_bytes(&buf[offset..], data_type)?;
199 offset += consumed;
200
201 let (max, consumed) = PropertyValue::from_bytes(&buf[offset..], data_type)?;
202 offset += consumed;
203
204 let (step, consumed) = PropertyValue::from_bytes(&buf[offset..], data_type)?;
205 offset += consumed;
206
207 Ok((PropertyRange { min, max, step }, offset))
208 }
209
210 pub fn to_bytes(&self) -> Vec<u8> {
212 let mut buf = Vec::new();
213 buf.extend_from_slice(&self.min.to_bytes());
214 buf.extend_from_slice(&self.max.to_bytes());
215 buf.extend_from_slice(&self.step.to_bytes());
216 buf
217 }
218}
219
220#[derive(Debug, Clone)]
229pub struct DevicePropDesc {
230 pub property_code: DevicePropertyCode,
232 pub data_type: PropertyDataType,
234 pub writable: bool,
236 pub default_value: PropertyValue,
238 pub current_value: PropertyValue,
240 pub form_type: PropertyFormType,
242 pub enum_values: Option<Vec<PropertyValue>>,
244 pub range: Option<PropertyRange>,
246}
247
248impl DevicePropDesc {
249 pub fn from_bytes(buf: &[u8]) -> Result<Self, crate::Error> {
254 let mut offset = 0;
255
256 let property_code = DevicePropertyCode::from(unpack_u16(&buf[offset..])?);
258 offset += 2;
259
260 let data_type = PropertyDataType::from(unpack_u16(&buf[offset..])?);
262 offset += 2;
263
264 if buf.len() <= offset {
266 return Err(crate::Error::invalid_data(
267 "DevicePropDesc: insufficient bytes for GetSet",
268 ));
269 }
270 let writable = buf[offset] != 0x00;
271 offset += 1;
272
273 let (default_value, consumed) = PropertyValue::from_bytes(&buf[offset..], data_type)?;
275 offset += consumed;
276
277 let (current_value, consumed) = PropertyValue::from_bytes(&buf[offset..], data_type)?;
279 offset += consumed;
280
281 if buf.len() <= offset {
283 return Err(crate::Error::invalid_data(
284 "DevicePropDesc: insufficient bytes for FormFlag",
285 ));
286 }
287 let form_type = PropertyFormType::from_code(buf[offset]);
288 offset += 1;
289
290 let (enum_values, range) = match form_type {
292 PropertyFormType::None | PropertyFormType::Unknown(_) => (None, None),
293 PropertyFormType::Range => {
294 let (range, _consumed) = PropertyRange::from_bytes(&buf[offset..], data_type)?;
295 (None, Some(range))
296 }
297 PropertyFormType::Enumeration => {
298 let count = unpack_u16(&buf[offset..])? as usize;
300 offset += 2;
301
302 let mut values = Vec::with_capacity(count);
303 for _ in 0..count {
304 let (val, consumed) = PropertyValue::from_bytes(&buf[offset..], data_type)?;
305 values.push(val);
306 offset += consumed;
307 }
308 (Some(values), None)
309 }
310 };
311
312 Ok(DevicePropDesc {
313 property_code,
314 data_type,
315 writable,
316 default_value,
317 current_value,
318 form_type,
319 enum_values,
320 range,
321 })
322 }
323}
324
325#[cfg(test)]
326mod tests {
327 use super::*;
328 use crate::ptp::pack::{pack_i16, pack_u16};
329 use proptest::prelude::*;
330
331 #[test]
334 fn property_value_roundtrip() {
335 let values = [
336 PropertyValue::Int8(-42),
337 PropertyValue::Uint8(100),
338 PropertyValue::Int16(-1000),
339 PropertyValue::Uint16(5000),
340 PropertyValue::Int32(-100000),
341 PropertyValue::Uint32(100000),
342 PropertyValue::Int64(-1_000_000_000),
343 PropertyValue::Uint64(1_000_000_000),
344 PropertyValue::String("Test".to_string()),
345 ];
346 for val in &values {
347 let bytes = val.to_bytes();
348 let (parsed, _) = PropertyValue::from_bytes(&bytes, val.data_type()).unwrap();
349 assert_eq!(&parsed, val);
350 }
351 }
352
353 #[test]
354 fn property_value_data_type() {
355 let cases: &[(PropertyValue, PropertyDataType)] = &[
356 (PropertyValue::Int8(0), PropertyDataType::Int8),
357 (PropertyValue::Uint8(0), PropertyDataType::Uint8),
358 (PropertyValue::Int16(0), PropertyDataType::Int16),
359 (PropertyValue::Uint16(0), PropertyDataType::Uint16),
360 (PropertyValue::Int32(0), PropertyDataType::Int32),
361 (PropertyValue::Uint32(0), PropertyDataType::Uint32),
362 (PropertyValue::Int64(0), PropertyDataType::Int64),
363 (PropertyValue::Uint64(0), PropertyDataType::Uint64),
364 (PropertyValue::String("".into()), PropertyDataType::String),
365 ];
366 for (val, expected) in cases {
367 assert_eq!(val.data_type(), *expected);
368 }
369 }
370
371 #[test]
372 fn property_value_from_bytes_unsupported_type() {
373 for dt in [
374 PropertyDataType::Undefined,
375 PropertyDataType::Int128,
376 PropertyDataType::Uint128,
377 PropertyDataType::Unknown(0x99),
378 ] {
379 assert!(PropertyValue::from_bytes(&[0x00], dt).is_err());
380 }
381 }
382
383 #[test]
384 fn property_value_from_bytes_insufficient_bytes() {
385 assert!(PropertyValue::from_bytes(&[], PropertyDataType::Int8).is_err());
386 assert!(PropertyValue::from_bytes(&[0x00], PropertyDataType::Int16).is_err());
387 assert!(PropertyValue::from_bytes(&[0x00, 0x00], PropertyDataType::Int32).is_err());
388 assert!(PropertyValue::from_bytes(&[0x00; 7], PropertyDataType::Int64).is_err());
389 }
390
391 #[test]
394 fn property_form_type_from_code() {
395 for (code, expected) in [
396 (0x00, PropertyFormType::None),
397 (0x01, PropertyFormType::Range),
398 (0x02, PropertyFormType::Enumeration),
399 ] {
400 assert_eq!(PropertyFormType::from_code(code), expected);
401 assert_eq!(expected.to_code(), code);
402 }
403 assert_eq!(
404 PropertyFormType::from_code(0x99),
405 PropertyFormType::Unknown(0x99)
406 );
407 }
408
409 #[test]
410 fn property_form_type_to_code() {
411 assert_eq!(PropertyFormType::None.to_code(), 0x00);
412 assert_eq!(PropertyFormType::Range.to_code(), 0x01);
413 assert_eq!(PropertyFormType::Enumeration.to_code(), 0x02);
414 assert_eq!(PropertyFormType::Unknown(0x99).to_code(), 0x99);
415 }
416
417 #[test]
418 fn property_form_type_roundtrip() {
419 for f in [
420 PropertyFormType::None,
421 PropertyFormType::Range,
422 PropertyFormType::Enumeration,
423 ] {
424 assert_eq!(PropertyFormType::from_code(f.to_code()), f);
425 }
426 }
427
428 #[test]
431 fn property_range_from_bytes_uint8() {
432 let buf = vec![0x00, 0x64, 0x01]; let (range, consumed) = PropertyRange::from_bytes(&buf, PropertyDataType::Uint8).unwrap();
434 assert_eq!(range.min, PropertyValue::Uint8(0));
435 assert_eq!(range.max, PropertyValue::Uint8(100));
436 assert_eq!(range.step, PropertyValue::Uint8(1));
437 assert_eq!(consumed, 3);
438 }
439
440 #[test]
441 fn property_range_from_bytes_uint16() {
442 let buf = vec![0x64, 0x00, 0x00, 0x19, 0x64, 0x00]; let (range, consumed) = PropertyRange::from_bytes(&buf, PropertyDataType::Uint16).unwrap();
444 assert_eq!(range.min, PropertyValue::Uint16(100));
445 assert_eq!(range.max, PropertyValue::Uint16(6400));
446 assert_eq!(range.step, PropertyValue::Uint16(100));
447 assert_eq!(consumed, 6);
448 }
449
450 #[test]
451 fn property_range_to_bytes() {
452 let range = PropertyRange {
453 min: PropertyValue::Uint8(0),
454 max: PropertyValue::Uint8(100),
455 step: PropertyValue::Uint8(1),
456 };
457 assert_eq!(range.to_bytes(), vec![0x00, 0x64, 0x01]);
458 }
459
460 #[test]
461 fn property_range_roundtrip() {
462 let range = PropertyRange {
463 min: PropertyValue::Uint16(100),
464 max: PropertyValue::Uint16(6400),
465 step: PropertyValue::Uint16(100),
466 };
467 let bytes = range.to_bytes();
468 let (parsed, _) = PropertyRange::from_bytes(&bytes, PropertyDataType::Uint16).unwrap();
469 assert_eq!(parsed.min, range.min);
470 assert_eq!(parsed.max, range.max);
471 assert_eq!(parsed.step, range.step);
472 }
473
474 fn build_battery_level_prop_desc(current: u8) -> Vec<u8> {
477 let mut buf = Vec::new();
478 buf.extend_from_slice(&pack_u16(0x5001)); buf.extend_from_slice(&pack_u16(0x0002)); buf.push(0x00); buf.push(100); buf.push(current); buf.push(0x01); buf.extend([0, 100, 1]); buf
486 }
487
488 #[test]
489 fn device_prop_desc_parse_battery_level() {
490 let desc = DevicePropDesc::from_bytes(&build_battery_level_prop_desc(75)).unwrap();
491 assert_eq!(desc.property_code, DevicePropertyCode::BatteryLevel);
492 assert_eq!(desc.data_type, PropertyDataType::Uint8);
493 assert!(!desc.writable);
494 assert_eq!(desc.current_value, PropertyValue::Uint8(75));
495 assert_eq!(desc.form_type, PropertyFormType::Range);
496 let range = desc.range.unwrap();
497 assert_eq!(range.min, PropertyValue::Uint8(0));
498 assert_eq!(range.max, PropertyValue::Uint8(100));
499 }
500
501 fn build_iso_prop_desc() -> Vec<u8> {
502 let mut buf = Vec::new();
503 buf.extend_from_slice(&pack_u16(0x500F)); buf.extend_from_slice(&pack_u16(0x0004)); buf.push(0x01); buf.extend_from_slice(&pack_u16(400)); buf.extend_from_slice(&pack_u16(800)); buf.push(0x02); buf.extend_from_slice(&pack_u16(4)); for iso in [100u16, 200, 400, 800] {
511 buf.extend_from_slice(&pack_u16(iso));
512 }
513 buf
514 }
515
516 #[test]
517 fn device_prop_desc_parse_iso_enumeration() {
518 let desc = DevicePropDesc::from_bytes(&build_iso_prop_desc()).unwrap();
519 assert_eq!(desc.property_code, DevicePropertyCode::ExposureIndex);
520 assert!(desc.writable);
521 assert_eq!(desc.current_value, PropertyValue::Uint16(800));
522 assert_eq!(desc.form_type, PropertyFormType::Enumeration);
523 let values = desc.enum_values.unwrap();
524 assert_eq!(
525 values,
526 vec![
527 PropertyValue::Uint16(100),
528 PropertyValue::Uint16(200),
529 PropertyValue::Uint16(400),
530 PropertyValue::Uint16(800),
531 ]
532 );
533 }
534
535 fn build_datetime_prop_desc() -> Vec<u8> {
536 let mut buf = Vec::new();
537 buf.extend_from_slice(&pack_u16(0x5011)); buf.extend_from_slice(&pack_u16(0xFFFF)); buf.push(0x01); buf.push(0x00); buf.extend_from_slice(&pack_string("20240315T120000"));
542 buf.push(0x00); buf
544 }
545
546 #[test]
547 fn device_prop_desc_parse_datetime_no_form() {
548 let desc = DevicePropDesc::from_bytes(&build_datetime_prop_desc()).unwrap();
549 assert_eq!(desc.property_code, DevicePropertyCode::DateTime);
550 assert_eq!(desc.data_type, PropertyDataType::String);
551 assert_eq!(
552 desc.current_value,
553 PropertyValue::String("20240315T120000".into())
554 );
555 assert_eq!(desc.form_type, PropertyFormType::None);
556 }
557
558 fn build_exposure_bias_prop_desc() -> Vec<u8> {
559 let mut buf = Vec::new();
560 buf.extend_from_slice(&pack_u16(0x5010)); buf.extend_from_slice(&pack_u16(0x0003)); buf.push(0x01);
563 buf.extend_from_slice(&pack_i16(0)); buf.extend_from_slice(&pack_i16(-1000)); buf.push(0x01); buf.extend_from_slice(&pack_i16(-3000));
567 buf.extend_from_slice(&pack_i16(3000));
568 buf.extend_from_slice(&pack_i16(333));
569 buf
570 }
571
572 #[test]
573 fn device_prop_desc_parse_exposure_bias_signed() {
574 let desc = DevicePropDesc::from_bytes(&build_exposure_bias_prop_desc()).unwrap();
575 assert_eq!(
576 desc.property_code,
577 DevicePropertyCode::ExposureBiasCompensation
578 );
579 assert_eq!(desc.current_value, PropertyValue::Int16(-1000));
580 let range = desc.range.unwrap();
581 assert_eq!(range.min, PropertyValue::Int16(-3000));
582 assert_eq!(range.max, PropertyValue::Int16(3000));
583 }
584
585 #[test]
586 fn device_prop_desc_parse_insufficient_bytes() {
587 assert!(DevicePropDesc::from_bytes(&[0x01]).is_err());
588 assert!(DevicePropDesc::from_bytes(&[0x01, 0x50]).is_err());
589 }
590
591 #[test]
592 fn device_prop_desc_minimum_valid() {
593 assert!(DevicePropDesc::from_bytes(&[]).is_err());
594 assert!(DevicePropDesc::from_bytes(&[0; 4]).is_err());
595 }
596
597 proptest! {
600 #[test]
601 fn prop_property_form_type_known_roundtrip(code in 0u8..=2u8) {
602 let form = PropertyFormType::from_code(code);
603 prop_assert_eq!(form.to_code(), code);
604 }
605
606 #[test]
607 fn prop_property_form_type_unknown_preserves_code(code in 3u8..=u8::MAX) {
608 let form = PropertyFormType::from_code(code);
609 prop_assert_eq!(form, PropertyFormType::Unknown(code));
610 prop_assert_eq!(form.to_code(), code);
611 }
612
613 #[test]
614 fn prop_property_value_int8_roundtrip(val: i8) {
615 let pv = PropertyValue::Int8(val);
616 let (parsed, _) = PropertyValue::from_bytes(&pv.to_bytes(), PropertyDataType::Int8).unwrap();
617 prop_assert_eq!(parsed, pv);
618 }
619
620 #[test]
621 fn prop_property_value_int16_roundtrip(val: i16) {
622 let pv = PropertyValue::Int16(val);
623 let (parsed, _) = PropertyValue::from_bytes(&pv.to_bytes(), PropertyDataType::Int16).unwrap();
624 prop_assert_eq!(parsed, pv);
625 }
626
627 #[test]
628 fn prop_property_value_int32_roundtrip(val: i32) {
629 let pv = PropertyValue::Int32(val);
630 let (parsed, _) = PropertyValue::from_bytes(&pv.to_bytes(), PropertyDataType::Int32).unwrap();
631 prop_assert_eq!(parsed, pv);
632 }
633
634 #[test]
635 fn prop_property_value_int64_roundtrip(val: i64) {
636 let pv = PropertyValue::Int64(val);
637 let (parsed, _) = PropertyValue::from_bytes(&pv.to_bytes(), PropertyDataType::Int64).unwrap();
638 prop_assert_eq!(parsed, pv);
639 }
640
641 #[test]
642 fn prop_property_value_uint16_roundtrip(val: u16) {
643 let pv = PropertyValue::Uint16(val);
644 let (parsed, _) = PropertyValue::from_bytes(&pv.to_bytes(), PropertyDataType::Uint16).unwrap();
645 prop_assert_eq!(parsed, pv);
646 }
647
648 #[test]
649 fn prop_property_value_uint32_roundtrip(val: u32) {
650 let pv = PropertyValue::Uint32(val);
651 let (parsed, _) = PropertyValue::from_bytes(&pv.to_bytes(), PropertyDataType::Uint32).unwrap();
652 prop_assert_eq!(parsed, pv);
653 }
654
655 #[test]
656 fn prop_property_value_uint64_roundtrip(val: u64) {
657 let pv = PropertyValue::Uint64(val);
658 let (parsed, _) = PropertyValue::from_bytes(&pv.to_bytes(), PropertyDataType::Uint64).unwrap();
659 prop_assert_eq!(parsed, pv);
660 }
661
662 #[test]
663 fn prop_property_range_uint8_roundtrip(min: u8, max: u8, step: u8) {
664 let range = PropertyRange {
665 min: PropertyValue::Uint8(min),
666 max: PropertyValue::Uint8(max),
667 step: PropertyValue::Uint8(step),
668 };
669 let (parsed, _) = PropertyRange::from_bytes(&range.to_bytes(), PropertyDataType::Uint8).unwrap();
670 prop_assert_eq!(parsed.min, range.min);
671 prop_assert_eq!(parsed.max, range.max);
672 prop_assert_eq!(parsed.step, range.step);
673 }
674
675 #[test]
676 fn prop_property_range_uint16_roundtrip(min: u16, max: u16, step: u16) {
677 let range = PropertyRange {
678 min: PropertyValue::Uint16(min),
679 max: PropertyValue::Uint16(max),
680 step: PropertyValue::Uint16(step),
681 };
682 let (parsed, _) = PropertyRange::from_bytes(&range.to_bytes(), PropertyDataType::Uint16).unwrap();
683 prop_assert_eq!(parsed.min, range.min);
684 prop_assert_eq!(parsed.max, range.max);
685 }
686
687 #[test]
688 fn prop_property_range_int16_roundtrip(min: i16, max: i16, step: i16) {
689 let range = PropertyRange {
690 min: PropertyValue::Int16(min),
691 max: PropertyValue::Int16(max),
692 step: PropertyValue::Int16(step),
693 };
694 let (parsed, _) = PropertyRange::from_bytes(&range.to_bytes(), PropertyDataType::Int16).unwrap();
695 prop_assert_eq!(parsed.min, range.min);
696 prop_assert_eq!(parsed.max, range.max);
697 }
698
699 #[test]
700 fn prop_property_range_uint32_roundtrip(min: u32, max: u32, step: u32) {
701 let range = PropertyRange {
702 min: PropertyValue::Uint32(min),
703 max: PropertyValue::Uint32(max),
704 step: PropertyValue::Uint32(step),
705 };
706 let (parsed, _) = PropertyRange::from_bytes(&range.to_bytes(), PropertyDataType::Uint32).unwrap();
707 prop_assert_eq!(parsed.min, range.min);
708 prop_assert_eq!(parsed.max, range.max);
709 }
710 }
711
712 fn valid_property_string() -> impl Strategy<Value = String> {
715 prop::collection::vec(
716 prop::char::range('\u{0000}', '\u{D7FF}')
717 .prop_union(prop::char::range('\u{E000}', '\u{FFFF}')),
718 0..50,
719 )
720 .prop_map(|chars| chars.into_iter().collect::<String>())
721 }
722
723 proptest! {
724 #[test]
725 fn prop_property_value_string_roundtrip(s in valid_property_string()) {
726 let s = if s.chars().count() > 254 { s.chars().take(254).collect() } else { s };
727 let pv = PropertyValue::String(s.clone());
728 let (parsed, _) = PropertyValue::from_bytes(&pv.to_bytes(), PropertyDataType::String).unwrap();
729 prop_assert_eq!(parsed, PropertyValue::String(s));
730 }
731
732 #[test]
733 fn fuzz_property_value_truncated(data_type_code in 1u16..=8u16, bytes in prop::collection::vec(any::<u8>(), 0..20)) {
734 let _ = PropertyValue::from_bytes(&bytes, PropertyDataType::from(data_type_code));
735 }
736
737 #[test]
738 fn fuzz_property_value_empty(data_type_code in 1u16..=8u16) {
739 prop_assert!(PropertyValue::from_bytes(&[], PropertyDataType::from(data_type_code)).is_err());
740 }
741
742 #[test]
743 fn fuzz_property_value_string_garbage(bytes in prop::collection::vec(any::<u8>(), 0..50)) {
744 let _ = PropertyValue::from_bytes(&bytes, PropertyDataType::String);
745 }
746
747 #[test]
748 fn fuzz_property_value_unsupported(bytes in prop::collection::vec(any::<u8>(), 0..20)) {
749 prop_assert!(PropertyValue::from_bytes(&bytes, PropertyDataType::Undefined).is_err());
750 prop_assert!(PropertyValue::from_bytes(&bytes, PropertyDataType::Int128).is_err());
751 prop_assert!(PropertyValue::from_bytes(&bytes, PropertyDataType::Uint128).is_err());
752 }
753
754 #[test]
755 fn fuzz_property_value_unknown_type(unknown_code in 11u16..=0xFFFEu16, bytes in prop::collection::vec(any::<u8>(), 0..20)) {
756 prop_assert!(PropertyValue::from_bytes(&bytes, PropertyDataType::Unknown(unknown_code)).is_err());
757 }
758
759 #[test]
760 fn fuzz_property_range_truncated(data_type_code in 1u16..=8u16, bytes in prop::collection::vec(any::<u8>(), 0..20)) {
761 let _ = PropertyRange::from_bytes(&bytes, PropertyDataType::from(data_type_code));
762 }
763
764 #[test]
765 fn fuzz_property_range_wrong_type(bytes in prop::collection::vec(any::<u8>(), 0..20)) {
766 prop_assert!(PropertyRange::from_bytes(&bytes, PropertyDataType::Undefined).is_err());
767 prop_assert!(PropertyRange::from_bytes(&bytes, PropertyDataType::Unknown(0x1234)).is_err());
768 }
769 }
770
771 crate::fuzz_bytes!(fuzz_device_prop_desc, DevicePropDesc, 200);
772}