1use crate::ber::{Decoder, EncodeBuf, tag};
6use crate::error::internal::DecodeErrorKind;
7use crate::error::{Error, Result, UNKNOWN_TARGET};
8use crate::format::hex;
9use crate::oid::Oid;
10use bytes::Bytes;
11
12#[derive(Debug, Clone, PartialEq)]
16#[non_exhaustive]
17pub enum Value {
18 Integer(i32),
20
21 OctetString(Bytes),
28
29 Null,
31
32 ObjectIdentifier(Oid),
34
35 IpAddress([u8; 4]),
37
38 Counter32(u32),
40
41 Gauge32(u32),
43
44 TimeTicks(u32),
46
47 Opaque(Bytes),
49
50 Counter64(u64),
60
61 NoSuchObject,
82
83 NoSuchInstance,
98
99 EndOfMibView,
119
120 Unknown { tag: u8, data: Bytes },
122}
123
124impl Value {
125 pub fn as_i32(&self) -> Option<i32> {
145 match self {
146 Value::Integer(v) => Some(*v),
147 _ => None,
148 }
149 }
150
151 pub fn as_u32(&self) -> Option<u32> {
176 match self {
177 Value::Counter32(v) | Value::Gauge32(v) | Value::TimeTicks(v) => Some(*v),
178 Value::Integer(v) if *v >= 0 => Some(*v as u32),
179 _ => None,
180 }
181 }
182
183 pub fn as_u64(&self) -> Option<u64> {
208 match self {
209 Value::Counter64(v) => Some(*v),
210 Value::Counter32(v) | Value::Gauge32(v) | Value::TimeTicks(v) => Some(*v as u64),
211 Value::Integer(v) if *v >= 0 => Some(*v as u64),
212 _ => None,
213 }
214 }
215
216 pub fn as_bytes(&self) -> Option<&[u8]> {
238 match self {
239 Value::OctetString(v) | Value::Opaque(v) => Some(v),
240 _ => None,
241 }
242 }
243
244 pub fn as_str(&self) -> Option<&str> {
268 self.as_bytes().and_then(|b| std::str::from_utf8(b).ok())
269 }
270
271 pub fn as_oid(&self) -> Option<&Oid> {
288 match self {
289 Value::ObjectIdentifier(oid) => Some(oid),
290 _ => None,
291 }
292 }
293
294 pub fn as_ip(&self) -> Option<std::net::Ipv4Addr> {
311 match self {
312 Value::IpAddress(bytes) => Some(std::net::Ipv4Addr::from(*bytes)),
313 _ => None,
314 }
315 }
316
317 pub fn is_exception(&self) -> bool {
319 matches!(
320 self,
321 Value::NoSuchObject | Value::NoSuchInstance | Value::EndOfMibView
322 )
323 }
324
325 pub(crate) fn ber_encoded_len(&self) -> usize {
327 use crate::ber::{
328 integer_content_len, length_encoded_len, unsigned32_content_len, unsigned64_content_len,
329 };
330
331 match self {
332 Value::Integer(v) => {
333 let content_len = integer_content_len(*v);
334 1 + length_encoded_len(content_len) + content_len
335 }
336 Value::OctetString(data) => {
337 let content_len = data.len();
338 1 + length_encoded_len(content_len) + content_len
339 }
340 Value::Null => 2, Value::ObjectIdentifier(oid) => oid.ber_encoded_len(),
342 Value::IpAddress(_) => 6, Value::Counter32(v) | Value::Gauge32(v) | Value::TimeTicks(v) => {
344 let content_len = unsigned32_content_len(*v);
345 1 + length_encoded_len(content_len) + content_len
346 }
347 Value::Opaque(data) => {
348 let content_len = data.len();
349 1 + length_encoded_len(content_len) + content_len
350 }
351 Value::Counter64(v) => {
352 let content_len = unsigned64_content_len(*v);
353 1 + length_encoded_len(content_len) + content_len
354 }
355 Value::NoSuchObject | Value::NoSuchInstance | Value::EndOfMibView => 2, Value::Unknown { data, .. } => {
357 let content_len = data.len();
358 1 + length_encoded_len(content_len) + content_len
359 }
360 }
361 }
362
363 pub fn format_with_hint(&self, hint: &str) -> Option<String> {
381 match self {
382 Value::OctetString(bytes) => Some(crate::format::display_hint::apply(hint, bytes)),
383 Value::Opaque(bytes) => Some(crate::format::display_hint::apply(hint, bytes)),
384 _ => None,
385 }
386 }
387
388 pub fn encode(&self, buf: &mut EncodeBuf) {
390 match self {
391 Value::Integer(v) => buf.push_integer(*v),
392 Value::OctetString(data) => buf.push_octet_string(data),
393 Value::Null => buf.push_null(),
394 Value::ObjectIdentifier(oid) => buf.push_oid(oid),
395 Value::IpAddress(addr) => buf.push_ip_address(*addr),
396 Value::Counter32(v) => buf.push_unsigned32(tag::application::COUNTER32, *v),
397 Value::Gauge32(v) => buf.push_unsigned32(tag::application::GAUGE32, *v),
398 Value::TimeTicks(v) => buf.push_unsigned32(tag::application::TIMETICKS, *v),
399 Value::Opaque(data) => {
400 buf.push_bytes(data);
401 buf.push_length(data.len());
402 buf.push_tag(tag::application::OPAQUE);
403 }
404 Value::Counter64(v) => buf.push_integer64(*v),
405 Value::NoSuchObject => {
406 buf.push_length(0);
407 buf.push_tag(tag::context::NO_SUCH_OBJECT);
408 }
409 Value::NoSuchInstance => {
410 buf.push_length(0);
411 buf.push_tag(tag::context::NO_SUCH_INSTANCE);
412 }
413 Value::EndOfMibView => {
414 buf.push_length(0);
415 buf.push_tag(tag::context::END_OF_MIB_VIEW);
416 }
417 Value::Unknown { tag: t, data } => {
418 buf.push_bytes(data);
419 buf.push_length(data.len());
420 buf.push_tag(*t);
421 }
422 }
423 }
424
425 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
427 let tag = decoder.read_tag()?;
428 let len = decoder.read_length()?;
429
430 match tag {
431 tag::universal::INTEGER => {
432 let value = decoder.read_integer_value(len)?;
433 Ok(Value::Integer(value))
434 }
435 tag::universal::OCTET_STRING => {
436 let data = decoder.read_bytes(len)?;
437 Ok(Value::OctetString(data))
438 }
439 tag::universal::NULL => {
440 if len != 0 {
441 tracing::debug!(target: "async_snmp::value", { offset = decoder.offset(), kind = %DecodeErrorKind::InvalidNull }, "decode error");
442 return Err(Error::MalformedResponse {
443 target: UNKNOWN_TARGET,
444 }
445 .boxed());
446 }
447 Ok(Value::Null)
448 }
449 tag::universal::OBJECT_IDENTIFIER => {
450 let oid = decoder.read_oid_value(len)?;
451 Ok(Value::ObjectIdentifier(oid))
452 }
453 tag::application::IP_ADDRESS => {
454 if len != 4 {
455 tracing::debug!(target: "async_snmp::value", { offset = decoder.offset(), length = len, kind = %DecodeErrorKind::InvalidIpAddressLength { length: len } }, "decode error");
456 return Err(Error::MalformedResponse {
457 target: UNKNOWN_TARGET,
458 }
459 .boxed());
460 }
461 let data = decoder.read_bytes(4)?;
462 Ok(Value::IpAddress([data[0], data[1], data[2], data[3]]))
463 }
464 tag::application::COUNTER32 => {
465 let value = decoder.read_unsigned32_value(len)?;
466 Ok(Value::Counter32(value))
467 }
468 tag::application::GAUGE32 => {
469 let value = decoder.read_unsigned32_value(len)?;
470 Ok(Value::Gauge32(value))
471 }
472 tag::application::TIMETICKS => {
473 let value = decoder.read_unsigned32_value(len)?;
474 Ok(Value::TimeTicks(value))
475 }
476 tag::application::OPAQUE => {
477 let data = decoder.read_bytes(len)?;
478 Ok(Value::Opaque(data))
479 }
480 tag::application::COUNTER64 => {
481 let value = decoder.read_integer64_value(len)?;
482 Ok(Value::Counter64(value))
483 }
484 tag::context::NO_SUCH_OBJECT => {
485 if len != 0 {
486 let _ = decoder.read_bytes(len)?;
487 }
488 Ok(Value::NoSuchObject)
489 }
490 tag::context::NO_SUCH_INSTANCE => {
491 if len != 0 {
492 let _ = decoder.read_bytes(len)?;
493 }
494 Ok(Value::NoSuchInstance)
495 }
496 tag::context::END_OF_MIB_VIEW => {
497 if len != 0 {
498 let _ = decoder.read_bytes(len)?;
499 }
500 Ok(Value::EndOfMibView)
501 }
502 tag::universal::OCTET_STRING_CONSTRUCTED => {
505 tracing::debug!(target: "async_snmp::value", { offset = decoder.offset(), kind = %DecodeErrorKind::ConstructedOctetString }, "decode error");
506 Err(Error::MalformedResponse {
507 target: UNKNOWN_TARGET,
508 }
509 .boxed())
510 }
511 _ => {
512 let data = decoder.read_bytes(len)?;
514 Ok(Value::Unknown { tag, data })
515 }
516 }
517 }
518}
519
520impl std::fmt::Display for Value {
521 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
522 match self {
523 Value::Integer(v) => write!(f, "{}", v),
524 Value::OctetString(data) => {
525 if let Ok(s) = std::str::from_utf8(data) {
527 write!(f, "{}", s)
528 } else {
529 write!(f, "0x{}", hex::encode(data))
530 }
531 }
532 Value::Null => write!(f, "NULL"),
533 Value::ObjectIdentifier(oid) => write!(f, "{}", oid),
534 Value::IpAddress(addr) => {
535 write!(f, "{}.{}.{}.{}", addr[0], addr[1], addr[2], addr[3])
536 }
537 Value::Counter32(v) => write!(f, "{}", v),
538 Value::Gauge32(v) => write!(f, "{}", v),
539 Value::TimeTicks(v) => {
540 let secs = v / 100;
542 let days = secs / 86400;
543 let hours = (secs % 86400) / 3600;
544 let mins = (secs % 3600) / 60;
545 let s = secs % 60;
546 write!(f, "{}d {}h {}m {}s", days, hours, mins, s)
547 }
548 Value::Opaque(data) => write!(f, "Opaque(0x{})", hex::encode(data)),
549 Value::Counter64(v) => write!(f, "{}", v),
550 Value::NoSuchObject => write!(f, "noSuchObject"),
551 Value::NoSuchInstance => write!(f, "noSuchInstance"),
552 Value::EndOfMibView => write!(f, "endOfMibView"),
553 Value::Unknown { tag, data } => {
554 write!(
555 f,
556 "Unknown(tag=0x{:02X}, data=0x{})",
557 tag,
558 hex::encode(data)
559 )
560 }
561 }
562 }
563}
564
565impl From<i32> for Value {
607 fn from(v: i32) -> Self {
608 Value::Integer(v)
609 }
610}
611
612impl From<&str> for Value {
613 fn from(s: &str) -> Self {
614 Value::OctetString(Bytes::copy_from_slice(s.as_bytes()))
615 }
616}
617
618impl From<String> for Value {
619 fn from(s: String) -> Self {
620 Value::OctetString(Bytes::from(s))
621 }
622}
623
624impl From<&[u8]> for Value {
625 fn from(data: &[u8]) -> Self {
626 Value::OctetString(Bytes::copy_from_slice(data))
627 }
628}
629
630impl From<Oid> for Value {
631 fn from(oid: Oid) -> Self {
632 Value::ObjectIdentifier(oid)
633 }
634}
635
636impl From<std::net::Ipv4Addr> for Value {
637 fn from(addr: std::net::Ipv4Addr) -> Self {
638 Value::IpAddress(addr.octets())
639 }
640}
641
642impl From<Bytes> for Value {
643 fn from(data: Bytes) -> Self {
644 Value::OctetString(data)
645 }
646}
647
648impl From<u64> for Value {
649 fn from(v: u64) -> Self {
650 Value::Counter64(v)
651 }
652}
653
654impl From<[u8; 4]> for Value {
655 fn from(addr: [u8; 4]) -> Self {
656 Value::IpAddress(addr)
657 }
658}
659
660#[cfg(test)]
661mod tests {
662 use super::*;
663
664 #[test]
667 fn test_reject_constructed_octet_string() {
668 let data = bytes::Bytes::from_static(&[0x24, 0x03, 0x04, 0x01, 0x41]);
672 let mut decoder = Decoder::new(data);
673 let result = Value::decode(&mut decoder);
674
675 assert!(
676 result.is_err(),
677 "constructed OCTET STRING (0x24) should be rejected"
678 );
679 let err = result.unwrap_err();
681 assert!(
682 matches!(&*err, crate::Error::MalformedResponse { .. }),
683 "expected MalformedResponse error, got: {:?}",
684 err
685 );
686 }
687
688 #[test]
689 fn test_primitive_octet_string_accepted() {
690 let data = bytes::Bytes::from_static(&[0x04, 0x03, 0x41, 0x42, 0x43]); let mut decoder = Decoder::new(data);
693 let result = Value::decode(&mut decoder);
694
695 assert!(result.is_ok(), "primitive OCTET STRING should be accepted");
696 let value = result.unwrap();
697 assert_eq!(value.as_bytes(), Some(&b"ABC"[..]));
698 }
699
700 fn roundtrip(value: Value) -> Value {
705 let mut buf = EncodeBuf::new();
706 value.encode(&mut buf);
707 let data = buf.finish();
708 let mut decoder = Decoder::new(data);
709 Value::decode(&mut decoder).unwrap()
710 }
711
712 #[test]
713 fn test_integer_positive() {
714 let value = Value::Integer(42);
715 assert_eq!(roundtrip(value.clone()), value);
716 }
717
718 #[test]
719 fn test_integer_negative() {
720 let value = Value::Integer(-42);
721 assert_eq!(roundtrip(value.clone()), value);
722 }
723
724 #[test]
725 fn test_integer_zero() {
726 let value = Value::Integer(0);
727 assert_eq!(roundtrip(value.clone()), value);
728 }
729
730 #[test]
731 fn test_integer_min() {
732 let value = Value::Integer(i32::MIN);
733 assert_eq!(roundtrip(value.clone()), value);
734 }
735
736 #[test]
737 fn test_integer_max() {
738 let value = Value::Integer(i32::MAX);
739 assert_eq!(roundtrip(value.clone()), value);
740 }
741
742 #[test]
743 fn test_octet_string_ascii() {
744 let value = Value::OctetString(Bytes::from_static(b"hello world"));
745 assert_eq!(roundtrip(value.clone()), value);
746 }
747
748 #[test]
749 fn test_octet_string_binary() {
750 let value = Value::OctetString(Bytes::from_static(&[0x00, 0xFF, 0x80, 0x7F]));
751 assert_eq!(roundtrip(value.clone()), value);
752 }
753
754 #[test]
755 fn test_octet_string_empty() {
756 let value = Value::OctetString(Bytes::new());
757 assert_eq!(roundtrip(value.clone()), value);
758 }
759
760 #[test]
761 fn test_null() {
762 let value = Value::Null;
763 assert_eq!(roundtrip(value.clone()), value);
764 }
765
766 #[test]
767 fn test_object_identifier() {
768 let value = Value::ObjectIdentifier(crate::oid!(1, 3, 6, 1, 2, 1, 1, 1, 0));
769 assert_eq!(roundtrip(value.clone()), value);
770 }
771
772 #[test]
773 fn test_ip_address() {
774 let value = Value::IpAddress([192, 168, 1, 1]);
775 assert_eq!(roundtrip(value.clone()), value);
776 }
777
778 #[test]
779 fn test_ip_address_zero() {
780 let value = Value::IpAddress([0, 0, 0, 0]);
781 assert_eq!(roundtrip(value.clone()), value);
782 }
783
784 #[test]
785 fn test_ip_address_broadcast() {
786 let value = Value::IpAddress([255, 255, 255, 255]);
787 assert_eq!(roundtrip(value.clone()), value);
788 }
789
790 #[test]
791 fn test_counter32() {
792 let value = Value::Counter32(999999);
793 assert_eq!(roundtrip(value.clone()), value);
794 }
795
796 #[test]
797 fn test_counter32_zero() {
798 let value = Value::Counter32(0);
799 assert_eq!(roundtrip(value.clone()), value);
800 }
801
802 #[test]
803 fn test_counter32_max() {
804 let value = Value::Counter32(u32::MAX);
805 assert_eq!(roundtrip(value.clone()), value);
806 }
807
808 #[test]
809 fn test_gauge32() {
810 let value = Value::Gauge32(1000000000);
811 assert_eq!(roundtrip(value.clone()), value);
812 }
813
814 #[test]
815 fn test_gauge32_max() {
816 let value = Value::Gauge32(u32::MAX);
817 assert_eq!(roundtrip(value.clone()), value);
818 }
819
820 #[test]
821 fn test_timeticks() {
822 let value = Value::TimeTicks(123456);
823 assert_eq!(roundtrip(value.clone()), value);
824 }
825
826 #[test]
827 fn test_timeticks_max() {
828 let value = Value::TimeTicks(u32::MAX);
829 assert_eq!(roundtrip(value.clone()), value);
830 }
831
832 #[test]
833 fn test_opaque() {
834 let value = Value::Opaque(Bytes::from_static(&[0xDE, 0xAD, 0xBE, 0xEF]));
835 assert_eq!(roundtrip(value.clone()), value);
836 }
837
838 #[test]
839 fn test_opaque_empty() {
840 let value = Value::Opaque(Bytes::new());
841 assert_eq!(roundtrip(value.clone()), value);
842 }
843
844 #[test]
845 fn test_counter64() {
846 let value = Value::Counter64(123456789012345);
847 assert_eq!(roundtrip(value.clone()), value);
848 }
849
850 #[test]
851 fn test_counter64_zero() {
852 let value = Value::Counter64(0);
853 assert_eq!(roundtrip(value.clone()), value);
854 }
855
856 #[test]
857 fn test_counter64_max() {
858 let value = Value::Counter64(u64::MAX);
859 assert_eq!(roundtrip(value.clone()), value);
860 }
861
862 #[test]
863 fn test_no_such_object() {
864 let value = Value::NoSuchObject;
865 assert_eq!(roundtrip(value.clone()), value);
866 }
867
868 #[test]
869 fn test_no_such_instance() {
870 let value = Value::NoSuchInstance;
871 assert_eq!(roundtrip(value.clone()), value);
872 }
873
874 #[test]
875 fn test_end_of_mib_view() {
876 let value = Value::EndOfMibView;
877 assert_eq!(roundtrip(value.clone()), value);
878 }
879
880 #[test]
881 fn test_unknown_tag_preserved() {
882 let data = Bytes::from_static(&[0x45, 0x03, 0x01, 0x02, 0x03]);
884 let mut decoder = Decoder::new(data);
885 let value = Value::decode(&mut decoder).unwrap();
886
887 match value {
888 Value::Unknown { tag, ref data } => {
889 assert_eq!(tag, 0x45);
890 assert_eq!(data.as_ref(), &[0x01, 0x02, 0x03]);
891 }
892 _ => panic!("expected Unknown variant"),
893 }
894
895 assert_eq!(roundtrip(value.clone()), value);
897 }
898
899 #[test]
904 fn test_as_i32() {
905 assert_eq!(Value::Integer(42).as_i32(), Some(42));
906 assert_eq!(Value::Integer(-42).as_i32(), Some(-42));
907 assert_eq!(Value::Counter32(100).as_i32(), None);
908 assert_eq!(Value::Null.as_i32(), None);
909 }
910
911 #[test]
912 fn test_as_u32() {
913 assert_eq!(Value::Counter32(100).as_u32(), Some(100));
914 assert_eq!(Value::Gauge32(200).as_u32(), Some(200));
915 assert_eq!(Value::TimeTicks(300).as_u32(), Some(300));
916 assert_eq!(Value::Integer(50).as_u32(), Some(50));
917 assert_eq!(Value::Integer(-1).as_u32(), None);
918 assert_eq!(Value::Counter64(100).as_u32(), None);
919 }
920
921 #[test]
922 fn test_as_u64() {
923 assert_eq!(Value::Counter64(100).as_u64(), Some(100));
924 assert_eq!(Value::Counter32(100).as_u64(), Some(100));
925 assert_eq!(Value::Gauge32(200).as_u64(), Some(200));
926 assert_eq!(Value::TimeTicks(300).as_u64(), Some(300));
927 assert_eq!(Value::Integer(50).as_u64(), Some(50));
928 assert_eq!(Value::Integer(-1).as_u64(), None);
929 }
930
931 #[test]
932 fn test_as_bytes() {
933 let s = Value::OctetString(Bytes::from_static(b"test"));
934 assert_eq!(s.as_bytes(), Some(b"test".as_slice()));
935
936 let o = Value::Opaque(Bytes::from_static(b"data"));
937 assert_eq!(o.as_bytes(), Some(b"data".as_slice()));
938
939 assert_eq!(Value::Integer(1).as_bytes(), None);
940 }
941
942 #[test]
943 fn test_as_str() {
944 let s = Value::OctetString(Bytes::from_static(b"hello"));
945 assert_eq!(s.as_str(), Some("hello"));
946
947 let invalid = Value::OctetString(Bytes::from_static(&[0xFF, 0xFE]));
949 assert_eq!(invalid.as_str(), None);
950
951 assert_eq!(Value::Integer(1).as_str(), None);
952 }
953
954 #[test]
955 fn test_as_oid() {
956 let oid = crate::oid!(1, 3, 6, 1);
957 let v = Value::ObjectIdentifier(oid.clone());
958 assert_eq!(v.as_oid(), Some(&oid));
959
960 assert_eq!(Value::Integer(1).as_oid(), None);
961 }
962
963 #[test]
964 fn test_as_ip() {
965 let v = Value::IpAddress([192, 168, 1, 1]);
966 assert_eq!(v.as_ip(), Some(std::net::Ipv4Addr::new(192, 168, 1, 1)));
967
968 assert_eq!(Value::Integer(1).as_ip(), None);
969 }
970
971 #[test]
976 fn test_is_exception() {
977 assert!(Value::NoSuchObject.is_exception());
978 assert!(Value::NoSuchInstance.is_exception());
979 assert!(Value::EndOfMibView.is_exception());
980
981 assert!(!Value::Integer(1).is_exception());
982 assert!(!Value::Null.is_exception());
983 assert!(!Value::OctetString(Bytes::new()).is_exception());
984 }
985
986 #[test]
991 fn test_display_integer() {
992 assert_eq!(format!("{}", Value::Integer(42)), "42");
993 assert_eq!(format!("{}", Value::Integer(-42)), "-42");
994 }
995
996 #[test]
997 fn test_display_octet_string_utf8() {
998 let v = Value::OctetString(Bytes::from_static(b"hello"));
999 assert_eq!(format!("{}", v), "hello");
1000 }
1001
1002 #[test]
1003 fn test_display_octet_string_binary() {
1004 let v = Value::OctetString(Bytes::from_static(&[0xFF, 0xFE]));
1006 assert_eq!(format!("{}", v), "0xfffe");
1007 }
1008
1009 #[test]
1010 fn test_display_null() {
1011 assert_eq!(format!("{}", Value::Null), "NULL");
1012 }
1013
1014 #[test]
1015 fn test_display_ip_address() {
1016 let v = Value::IpAddress([192, 168, 1, 1]);
1017 assert_eq!(format!("{}", v), "192.168.1.1");
1018 }
1019
1020 #[test]
1021 fn test_display_counter32() {
1022 assert_eq!(format!("{}", Value::Counter32(999)), "999");
1023 }
1024
1025 #[test]
1026 fn test_display_gauge32() {
1027 assert_eq!(format!("{}", Value::Gauge32(1000)), "1000");
1028 }
1029
1030 #[test]
1031 fn test_display_timeticks() {
1032 let v = Value::TimeTicks(123456);
1035 assert_eq!(format!("{}", v), "0d 0h 20m 34s");
1036 }
1037
1038 #[test]
1039 fn test_display_opaque() {
1040 let v = Value::Opaque(Bytes::from_static(&[0xBE, 0xEF]));
1041 assert_eq!(format!("{}", v), "Opaque(0xbeef)");
1042 }
1043
1044 #[test]
1045 fn test_display_counter64() {
1046 assert_eq!(format!("{}", Value::Counter64(12345678)), "12345678");
1047 }
1048
1049 #[test]
1050 fn test_display_exceptions() {
1051 assert_eq!(format!("{}", Value::NoSuchObject), "noSuchObject");
1052 assert_eq!(format!("{}", Value::NoSuchInstance), "noSuchInstance");
1053 assert_eq!(format!("{}", Value::EndOfMibView), "endOfMibView");
1054 }
1055
1056 #[test]
1057 fn test_display_unknown() {
1058 let v = Value::Unknown {
1059 tag: 0x99,
1060 data: Bytes::from_static(&[0x01, 0x02]),
1061 };
1062 assert_eq!(format!("{}", v), "Unknown(tag=0x99, data=0x0102)");
1063 }
1064
1065 #[test]
1070 fn test_from_i32() {
1071 let v: Value = 42i32.into();
1072 assert_eq!(v, Value::Integer(42));
1073 }
1074
1075 #[test]
1076 fn test_from_str() {
1077 let v: Value = "hello".into();
1078 assert_eq!(v.as_str(), Some("hello"));
1079 }
1080
1081 #[test]
1082 fn test_from_string() {
1083 let v: Value = String::from("hello").into();
1084 assert_eq!(v.as_str(), Some("hello"));
1085 }
1086
1087 #[test]
1088 fn test_from_bytes_slice() {
1089 let v: Value = (&[1u8, 2, 3][..]).into();
1090 assert_eq!(v.as_bytes(), Some(&[1u8, 2, 3][..]));
1091 }
1092
1093 #[test]
1094 fn test_from_oid() {
1095 let oid = crate::oid!(1, 3, 6, 1);
1096 let v: Value = oid.clone().into();
1097 assert_eq!(v.as_oid(), Some(&oid));
1098 }
1099
1100 #[test]
1101 fn test_from_ipv4addr() {
1102 let addr = std::net::Ipv4Addr::new(10, 0, 0, 1);
1103 let v: Value = addr.into();
1104 assert_eq!(v, Value::IpAddress([10, 0, 0, 1]));
1105 }
1106
1107 #[test]
1108 fn test_from_bytes() {
1109 let data = Bytes::from_static(b"hello");
1110 let v: Value = data.into();
1111 assert_eq!(v.as_bytes(), Some(b"hello".as_slice()));
1112 }
1113
1114 #[test]
1115 fn test_from_u64() {
1116 let v: Value = 12345678901234u64.into();
1117 assert_eq!(v, Value::Counter64(12345678901234));
1118 }
1119
1120 #[test]
1121 fn test_from_ip_array() {
1122 let v: Value = [192u8, 168, 1, 1].into();
1123 assert_eq!(v, Value::IpAddress([192, 168, 1, 1]));
1124 }
1125
1126 #[test]
1131 fn test_decode_invalid_null_length() {
1132 let data = Bytes::from_static(&[0x05, 0x01, 0x00]); let mut decoder = Decoder::new(data);
1135 let result = Value::decode(&mut decoder);
1136 assert!(result.is_err());
1137 }
1138
1139 #[test]
1140 fn test_decode_invalid_ip_address_length() {
1141 let data = Bytes::from_static(&[0x40, 0x03, 0x01, 0x02, 0x03]); let mut decoder = Decoder::new(data);
1144 let result = Value::decode(&mut decoder);
1145 assert!(result.is_err());
1146 }
1147
1148 #[test]
1149 fn test_decode_exception_with_content_accepted() {
1150 let data = Bytes::from_static(&[0x80, 0x01, 0xFF]); let mut decoder = Decoder::new(data);
1153 let result = Value::decode(&mut decoder);
1154 assert!(result.is_ok());
1155 assert_eq!(result.unwrap(), Value::NoSuchObject);
1156 }
1157}