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, Copy, PartialEq, Eq, Hash)]
42pub enum RowStatus {
43 Active = 1,
45 NotInService = 2,
47 NotReady = 3,
49 CreateAndGo = 4,
51 CreateAndWait = 5,
53 Destroy = 6,
55}
56
57impl RowStatus {
58 pub fn from_i32(value: i32) -> Option<Self> {
62 match value {
63 1 => Some(Self::Active),
64 2 => Some(Self::NotInService),
65 3 => Some(Self::NotReady),
66 4 => Some(Self::CreateAndGo),
67 5 => Some(Self::CreateAndWait),
68 6 => Some(Self::Destroy),
69 _ => None,
70 }
71 }
72}
73
74impl From<RowStatus> for Value {
75 fn from(status: RowStatus) -> Self {
76 Value::Integer(status as i32)
77 }
78}
79
80impl std::fmt::Display for RowStatus {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 match self {
83 Self::Active => write!(f, "active"),
84 Self::NotInService => write!(f, "notInService"),
85 Self::NotReady => write!(f, "notReady"),
86 Self::CreateAndGo => write!(f, "createAndGo"),
87 Self::CreateAndWait => write!(f, "createAndWait"),
88 Self::Destroy => write!(f, "destroy"),
89 }
90 }
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
121pub enum StorageType {
122 Other = 1,
124 Volatile = 2,
126 NonVolatile = 3,
128 Permanent = 4,
130 ReadOnly = 5,
132}
133
134impl StorageType {
135 pub fn from_i32(value: i32) -> Option<Self> {
139 match value {
140 1 => Some(Self::Other),
141 2 => Some(Self::Volatile),
142 3 => Some(Self::NonVolatile),
143 4 => Some(Self::Permanent),
144 5 => Some(Self::ReadOnly),
145 _ => None,
146 }
147 }
148}
149
150impl From<StorageType> for Value {
151 fn from(storage: StorageType) -> Self {
152 Value::Integer(storage as i32)
153 }
154}
155
156impl std::fmt::Display for StorageType {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 match self {
159 Self::Other => write!(f, "other"),
160 Self::Volatile => write!(f, "volatile"),
161 Self::NonVolatile => write!(f, "nonVolatile"),
162 Self::Permanent => write!(f, "permanent"),
163 Self::ReadOnly => write!(f, "readOnly"),
164 }
165 }
166}
167
168#[derive(Debug, Clone, PartialEq, Eq, Hash)]
172#[non_exhaustive]
173pub enum Value {
174 Integer(i32),
176
177 OctetString(Bytes),
184
185 Null,
187
188 ObjectIdentifier(Oid),
190
191 IpAddress([u8; 4]),
193
194 Counter32(u32),
196
197 Gauge32(u32),
199
200 TimeTicks(u32),
202
203 Opaque(Bytes),
205
206 Counter64(u64),
216
217 NoSuchObject,
238
239 NoSuchInstance,
254
255 EndOfMibView,
275
276 Unknown { tag: u8, data: Bytes },
278}
279
280impl Value {
281 pub fn as_i32(&self) -> Option<i32> {
301 match self {
302 Value::Integer(v) => Some(*v),
303 _ => None,
304 }
305 }
306
307 pub fn as_u32(&self) -> Option<u32> {
332 match self {
333 Value::Counter32(v) | Value::Gauge32(v) | Value::TimeTicks(v) => Some(*v),
334 Value::Integer(v) if *v >= 0 => Some(*v as u32),
335 _ => None,
336 }
337 }
338
339 pub fn as_u64(&self) -> Option<u64> {
364 match self {
365 Value::Counter64(v) => Some(*v),
366 Value::Counter32(v) | Value::Gauge32(v) | Value::TimeTicks(v) => Some(*v as u64),
367 Value::Integer(v) if *v >= 0 => Some(*v as u64),
368 _ => None,
369 }
370 }
371
372 pub fn as_bytes(&self) -> Option<&[u8]> {
394 match self {
395 Value::OctetString(v) | Value::Opaque(v) => Some(v),
396 _ => None,
397 }
398 }
399
400 pub fn as_str(&self) -> Option<&str> {
424 self.as_bytes().and_then(|b| std::str::from_utf8(b).ok())
425 }
426
427 pub fn as_oid(&self) -> Option<&Oid> {
444 match self {
445 Value::ObjectIdentifier(oid) => Some(oid),
446 _ => None,
447 }
448 }
449
450 pub fn as_ip(&self) -> Option<std::net::Ipv4Addr> {
467 match self {
468 Value::IpAddress(bytes) => Some(std::net::Ipv4Addr::from(*bytes)),
469 _ => None,
470 }
471 }
472
473 pub fn as_f64(&self) -> Option<f64> {
489 match self {
490 Value::Integer(v) => Some(*v as f64),
491 Value::Counter32(v) | Value::Gauge32(v) | Value::TimeTicks(v) => Some(*v as f64),
492 Value::Counter64(v) => Some(*v as f64),
493 _ => None,
494 }
495 }
496
497 pub fn as_f64_wrapped(&self) -> Option<f64> {
521 const MANTISSA_LIMIT: u64 = 1 << 53;
522 match self {
523 Value::Counter64(v) => Some((*v % MANTISSA_LIMIT) as f64),
524 _ => self.as_f64(),
525 }
526 }
527
528 pub fn as_decimal(&self, places: u8) -> Option<f64> {
556 let divisor = 10f64.powi(places as i32);
557 self.as_f64().map(|v| v / divisor)
558 }
559
560 pub fn as_duration(&self) -> Option<std::time::Duration> {
581 match self {
582 Value::TimeTicks(v) => Some(std::time::Duration::from_millis(*v as u64 * 10)),
583 _ => None,
584 }
585 }
586
587 pub fn as_opaque_float(&self) -> Option<f32> {
613 match self {
614 Value::Opaque(data)
615 if data.len() >= 7
616 && data[0] == 0x9f && data[1] == 0x78 && data[2] == 0x04 =>
619 {
620 let bytes: [u8; 4] = data[3..7].try_into().ok()?;
622 Some(f32::from_be_bytes(bytes))
623 }
624 _ => None,
625 }
626 }
627
628 pub fn as_opaque_double(&self) -> Option<f64> {
650 match self {
651 Value::Opaque(data)
652 if data.len() >= 11
653 && data[0] == 0x9f && data[1] == 0x79 && data[2] == 0x08 =>
656 {
657 let bytes: [u8; 8] = data[3..11].try_into().ok()?;
659 Some(f64::from_be_bytes(bytes))
660 }
661 _ => None,
662 }
663 }
664
665 pub fn as_opaque_counter64(&self) -> Option<u64> {
686 self.as_opaque_unsigned(0x76)
687 }
688
689 pub fn as_opaque_i64(&self) -> Option<i64> {
708 match self {
709 Value::Opaque(data)
710 if data.len() >= 4
711 && data[0] == 0x9f && data[1] == 0x7a =>
713 {
714 let len = data[2] as usize;
716 if data.len() < 3 + len || len == 0 || len > 8 {
717 return None;
718 }
719 let bytes = &data[3..3 + len];
720 let is_negative = bytes[0] & 0x80 != 0;
722 let mut value: i64 = if is_negative { -1 } else { 0 };
723 for &byte in bytes {
724 value = (value << 8) | (byte as i64);
725 }
726 Some(value)
727 }
728 _ => None,
729 }
730 }
731
732 pub fn as_opaque_u64(&self) -> Option<u64> {
751 self.as_opaque_unsigned(0x7b)
752 }
753
754 fn as_opaque_unsigned(&self, expected_type: u8) -> Option<u64> {
756 match self {
757 Value::Opaque(data)
758 if data.len() >= 4
759 && data[0] == 0x9f && data[1] == expected_type =>
761 {
762 let len = data[2] as usize;
763 if data.len() < 3 + len || len == 0 || len > 8 {
764 return None;
765 }
766 let bytes = &data[3..3 + len];
767 let mut value: u64 = 0;
768 for &byte in bytes {
769 value = (value << 8) | (byte as u64);
770 }
771 Some(value)
772 }
773 _ => None,
774 }
775 }
776
777 pub fn as_truth_value(&self) -> Option<bool> {
796 match self {
797 Value::Integer(1) => Some(true),
798 Value::Integer(2) => Some(false),
799 _ => None,
800 }
801 }
802
803 pub fn as_row_status(&self) -> Option<RowStatus> {
821 match self {
822 Value::Integer(v) => RowStatus::from_i32(*v),
823 _ => None,
824 }
825 }
826
827 pub fn as_storage_type(&self) -> Option<StorageType> {
845 match self {
846 Value::Integer(v) => StorageType::from_i32(*v),
847 _ => None,
848 }
849 }
850
851 pub fn is_exception(&self) -> bool {
853 matches!(
854 self,
855 Value::NoSuchObject | Value::NoSuchInstance | Value::EndOfMibView
856 )
857 }
858
859 pub(crate) fn ber_encoded_len(&self) -> usize {
861 use crate::ber::{
862 integer_content_len, length_encoded_len, unsigned32_content_len, unsigned64_content_len,
863 };
864
865 match self {
866 Value::Integer(v) => {
867 let content_len = integer_content_len(*v);
868 1 + length_encoded_len(content_len) + content_len
869 }
870 Value::OctetString(data) => {
871 let content_len = data.len();
872 1 + length_encoded_len(content_len) + content_len
873 }
874 Value::Null => 2, Value::ObjectIdentifier(oid) => oid.ber_encoded_len(),
876 Value::IpAddress(_) => 6, Value::Counter32(v) | Value::Gauge32(v) | Value::TimeTicks(v) => {
878 let content_len = unsigned32_content_len(*v);
879 1 + length_encoded_len(content_len) + content_len
880 }
881 Value::Opaque(data) => {
882 let content_len = data.len();
883 1 + length_encoded_len(content_len) + content_len
884 }
885 Value::Counter64(v) => {
886 let content_len = unsigned64_content_len(*v);
887 1 + length_encoded_len(content_len) + content_len
888 }
889 Value::NoSuchObject | Value::NoSuchInstance | Value::EndOfMibView => 2, Value::Unknown { data, .. } => {
891 let content_len = data.len();
892 1 + length_encoded_len(content_len) + content_len
893 }
894 }
895 }
896
897 pub fn format_with_hint(&self, hint: &str) -> Option<String> {
924 match self {
925 Value::OctetString(bytes) => Some(crate::format::display_hint::apply(hint, bytes)),
926 Value::Opaque(bytes) => Some(crate::format::display_hint::apply(hint, bytes)),
927 Value::Integer(v) => crate::format::display_hint::apply_integer(hint, *v),
928 _ => None,
929 }
930 }
931
932 pub fn encode(&self, buf: &mut EncodeBuf) {
934 match self {
935 Value::Integer(v) => buf.push_integer(*v),
936 Value::OctetString(data) => buf.push_octet_string(data),
937 Value::Null => buf.push_null(),
938 Value::ObjectIdentifier(oid) => buf.push_oid(oid),
939 Value::IpAddress(addr) => buf.push_ip_address(*addr),
940 Value::Counter32(v) => buf.push_unsigned32(tag::application::COUNTER32, *v),
941 Value::Gauge32(v) => buf.push_unsigned32(tag::application::GAUGE32, *v),
942 Value::TimeTicks(v) => buf.push_unsigned32(tag::application::TIMETICKS, *v),
943 Value::Opaque(data) => {
944 buf.push_bytes(data);
945 buf.push_length(data.len());
946 buf.push_tag(tag::application::OPAQUE);
947 }
948 Value::Counter64(v) => buf.push_integer64(*v),
949 Value::NoSuchObject => {
950 buf.push_length(0);
951 buf.push_tag(tag::context::NO_SUCH_OBJECT);
952 }
953 Value::NoSuchInstance => {
954 buf.push_length(0);
955 buf.push_tag(tag::context::NO_SUCH_INSTANCE);
956 }
957 Value::EndOfMibView => {
958 buf.push_length(0);
959 buf.push_tag(tag::context::END_OF_MIB_VIEW);
960 }
961 Value::Unknown { tag: t, data } => {
962 buf.push_bytes(data);
963 buf.push_length(data.len());
964 buf.push_tag(*t);
965 }
966 }
967 }
968
969 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
971 let tag = decoder.read_tag()?;
972 let len = decoder.read_length()?;
973
974 match tag {
975 tag::universal::INTEGER => {
976 let value = decoder.read_integer_value(len)?;
977 Ok(Value::Integer(value))
978 }
979 tag::universal::OCTET_STRING => {
980 let data = decoder.read_bytes(len)?;
981 Ok(Value::OctetString(data))
982 }
983 tag::universal::NULL => {
984 if len != 0 {
985 tracing::debug!(target: "async_snmp::value", { offset = decoder.offset(), kind = %DecodeErrorKind::InvalidNull }, "decode error");
986 return Err(Error::MalformedResponse {
987 target: UNKNOWN_TARGET,
988 }
989 .boxed());
990 }
991 Ok(Value::Null)
992 }
993 tag::universal::OBJECT_IDENTIFIER => {
994 let oid = decoder.read_oid_value(len)?;
995 Ok(Value::ObjectIdentifier(oid))
996 }
997 tag::application::IP_ADDRESS => {
998 if len != 4 {
999 tracing::debug!(target: "async_snmp::value", { offset = decoder.offset(), length = len, kind = %DecodeErrorKind::InvalidIpAddressLength { length: len } }, "decode error");
1000 return Err(Error::MalformedResponse {
1001 target: UNKNOWN_TARGET,
1002 }
1003 .boxed());
1004 }
1005 let data = decoder.read_bytes(4)?;
1006 Ok(Value::IpAddress([data[0], data[1], data[2], data[3]]))
1007 }
1008 tag::application::COUNTER32 => {
1009 let value = decoder.read_unsigned32_value(len)?;
1010 Ok(Value::Counter32(value))
1011 }
1012 tag::application::GAUGE32 => {
1013 let value = decoder.read_unsigned32_value(len)?;
1014 Ok(Value::Gauge32(value))
1015 }
1016 tag::application::TIMETICKS => {
1017 let value = decoder.read_unsigned32_value(len)?;
1018 Ok(Value::TimeTicks(value))
1019 }
1020 tag::application::OPAQUE => {
1021 let data = decoder.read_bytes(len)?;
1022 Ok(Value::Opaque(data))
1023 }
1024 tag::application::COUNTER64 => {
1025 let value = decoder.read_integer64_value(len)?;
1026 Ok(Value::Counter64(value))
1027 }
1028 tag::context::NO_SUCH_OBJECT => {
1029 if len != 0 {
1030 let _ = decoder.read_bytes(len)?;
1031 }
1032 Ok(Value::NoSuchObject)
1033 }
1034 tag::context::NO_SUCH_INSTANCE => {
1035 if len != 0 {
1036 let _ = decoder.read_bytes(len)?;
1037 }
1038 Ok(Value::NoSuchInstance)
1039 }
1040 tag::context::END_OF_MIB_VIEW => {
1041 if len != 0 {
1042 let _ = decoder.read_bytes(len)?;
1043 }
1044 Ok(Value::EndOfMibView)
1045 }
1046 tag::universal::OCTET_STRING_CONSTRUCTED => {
1049 tracing::debug!(target: "async_snmp::value", { offset = decoder.offset(), kind = %DecodeErrorKind::ConstructedOctetString }, "decode error");
1050 Err(Error::MalformedResponse {
1051 target: UNKNOWN_TARGET,
1052 }
1053 .boxed())
1054 }
1055 _ => {
1056 let data = decoder.read_bytes(len)?;
1058 Ok(Value::Unknown { tag, data })
1059 }
1060 }
1061 }
1062}
1063
1064impl std::fmt::Display for Value {
1065 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1066 match self {
1067 Value::Integer(v) => write!(f, "{}", v),
1068 Value::OctetString(data) => {
1069 if let Ok(s) = std::str::from_utf8(data) {
1071 write!(f, "{}", s)
1072 } else {
1073 write!(f, "0x{}", hex::encode(data))
1074 }
1075 }
1076 Value::Null => write!(f, "NULL"),
1077 Value::ObjectIdentifier(oid) => write!(f, "{}", oid),
1078 Value::IpAddress(addr) => {
1079 write!(f, "{}.{}.{}.{}", addr[0], addr[1], addr[2], addr[3])
1080 }
1081 Value::Counter32(v) => write!(f, "{}", v),
1082 Value::Gauge32(v) => write!(f, "{}", v),
1083 Value::TimeTicks(v) => {
1084 let secs = v / 100;
1086 let days = secs / 86400;
1087 let hours = (secs % 86400) / 3600;
1088 let mins = (secs % 3600) / 60;
1089 let s = secs % 60;
1090 write!(f, "{}d {}h {}m {}s", days, hours, mins, s)
1091 }
1092 Value::Opaque(data) => write!(f, "Opaque(0x{})", hex::encode(data)),
1093 Value::Counter64(v) => write!(f, "{}", v),
1094 Value::NoSuchObject => write!(f, "noSuchObject"),
1095 Value::NoSuchInstance => write!(f, "noSuchInstance"),
1096 Value::EndOfMibView => write!(f, "endOfMibView"),
1097 Value::Unknown { tag, data } => {
1098 write!(
1099 f,
1100 "Unknown(tag=0x{:02X}, data=0x{})",
1101 tag,
1102 hex::encode(data)
1103 )
1104 }
1105 }
1106 }
1107}
1108
1109impl From<i32> for Value {
1151 fn from(v: i32) -> Self {
1152 Value::Integer(v)
1153 }
1154}
1155
1156impl From<&str> for Value {
1157 fn from(s: &str) -> Self {
1158 Value::OctetString(Bytes::copy_from_slice(s.as_bytes()))
1159 }
1160}
1161
1162impl From<String> for Value {
1163 fn from(s: String) -> Self {
1164 Value::OctetString(Bytes::from(s))
1165 }
1166}
1167
1168impl From<&[u8]> for Value {
1169 fn from(data: &[u8]) -> Self {
1170 Value::OctetString(Bytes::copy_from_slice(data))
1171 }
1172}
1173
1174impl From<Oid> for Value {
1175 fn from(oid: Oid) -> Self {
1176 Value::ObjectIdentifier(oid)
1177 }
1178}
1179
1180impl From<std::net::Ipv4Addr> for Value {
1181 fn from(addr: std::net::Ipv4Addr) -> Self {
1182 Value::IpAddress(addr.octets())
1183 }
1184}
1185
1186impl From<Bytes> for Value {
1187 fn from(data: Bytes) -> Self {
1188 Value::OctetString(data)
1189 }
1190}
1191
1192impl From<u64> for Value {
1193 fn from(v: u64) -> Self {
1194 Value::Counter64(v)
1195 }
1196}
1197
1198impl From<[u8; 4]> for Value {
1199 fn from(addr: [u8; 4]) -> Self {
1200 Value::IpAddress(addr)
1201 }
1202}
1203
1204#[cfg(test)]
1205mod tests {
1206 use super::*;
1207
1208 #[test]
1211 fn test_reject_constructed_octet_string() {
1212 let data = bytes::Bytes::from_static(&[0x24, 0x03, 0x04, 0x01, 0x41]);
1216 let mut decoder = Decoder::new(data);
1217 let result = Value::decode(&mut decoder);
1218
1219 assert!(
1220 result.is_err(),
1221 "constructed OCTET STRING (0x24) should be rejected"
1222 );
1223 let err = result.unwrap_err();
1225 assert!(
1226 matches!(&*err, crate::Error::MalformedResponse { .. }),
1227 "expected MalformedResponse error, got: {:?}",
1228 err
1229 );
1230 }
1231
1232 #[test]
1233 fn test_primitive_octet_string_accepted() {
1234 let data = bytes::Bytes::from_static(&[0x04, 0x03, 0x41, 0x42, 0x43]); let mut decoder = Decoder::new(data);
1237 let result = Value::decode(&mut decoder);
1238
1239 assert!(result.is_ok(), "primitive OCTET STRING should be accepted");
1240 let value = result.unwrap();
1241 assert_eq!(value.as_bytes(), Some(&b"ABC"[..]));
1242 }
1243
1244 fn roundtrip(value: Value) -> Value {
1249 let mut buf = EncodeBuf::new();
1250 value.encode(&mut buf);
1251 let data = buf.finish();
1252 let mut decoder = Decoder::new(data);
1253 Value::decode(&mut decoder).unwrap()
1254 }
1255
1256 #[test]
1257 fn test_integer_positive() {
1258 let value = Value::Integer(42);
1259 assert_eq!(roundtrip(value.clone()), value);
1260 }
1261
1262 #[test]
1263 fn test_integer_negative() {
1264 let value = Value::Integer(-42);
1265 assert_eq!(roundtrip(value.clone()), value);
1266 }
1267
1268 #[test]
1269 fn test_integer_zero() {
1270 let value = Value::Integer(0);
1271 assert_eq!(roundtrip(value.clone()), value);
1272 }
1273
1274 #[test]
1275 fn test_integer_min() {
1276 let value = Value::Integer(i32::MIN);
1277 assert_eq!(roundtrip(value.clone()), value);
1278 }
1279
1280 #[test]
1281 fn test_integer_max() {
1282 let value = Value::Integer(i32::MAX);
1283 assert_eq!(roundtrip(value.clone()), value);
1284 }
1285
1286 #[test]
1287 fn test_octet_string_ascii() {
1288 let value = Value::OctetString(Bytes::from_static(b"hello world"));
1289 assert_eq!(roundtrip(value.clone()), value);
1290 }
1291
1292 #[test]
1293 fn test_octet_string_binary() {
1294 let value = Value::OctetString(Bytes::from_static(&[0x00, 0xFF, 0x80, 0x7F]));
1295 assert_eq!(roundtrip(value.clone()), value);
1296 }
1297
1298 #[test]
1299 fn test_octet_string_empty() {
1300 let value = Value::OctetString(Bytes::new());
1301 assert_eq!(roundtrip(value.clone()), value);
1302 }
1303
1304 #[test]
1305 fn test_null() {
1306 let value = Value::Null;
1307 assert_eq!(roundtrip(value.clone()), value);
1308 }
1309
1310 #[test]
1311 fn test_object_identifier() {
1312 let value = Value::ObjectIdentifier(crate::oid!(1, 3, 6, 1, 2, 1, 1, 1, 0));
1313 assert_eq!(roundtrip(value.clone()), value);
1314 }
1315
1316 #[test]
1317 fn test_ip_address() {
1318 let value = Value::IpAddress([192, 168, 1, 1]);
1319 assert_eq!(roundtrip(value.clone()), value);
1320 }
1321
1322 #[test]
1323 fn test_ip_address_zero() {
1324 let value = Value::IpAddress([0, 0, 0, 0]);
1325 assert_eq!(roundtrip(value.clone()), value);
1326 }
1327
1328 #[test]
1329 fn test_ip_address_broadcast() {
1330 let value = Value::IpAddress([255, 255, 255, 255]);
1331 assert_eq!(roundtrip(value.clone()), value);
1332 }
1333
1334 #[test]
1335 fn test_counter32() {
1336 let value = Value::Counter32(999999);
1337 assert_eq!(roundtrip(value.clone()), value);
1338 }
1339
1340 #[test]
1341 fn test_counter32_zero() {
1342 let value = Value::Counter32(0);
1343 assert_eq!(roundtrip(value.clone()), value);
1344 }
1345
1346 #[test]
1347 fn test_counter32_max() {
1348 let value = Value::Counter32(u32::MAX);
1349 assert_eq!(roundtrip(value.clone()), value);
1350 }
1351
1352 #[test]
1353 fn test_gauge32() {
1354 let value = Value::Gauge32(1000000000);
1355 assert_eq!(roundtrip(value.clone()), value);
1356 }
1357
1358 #[test]
1359 fn test_gauge32_max() {
1360 let value = Value::Gauge32(u32::MAX);
1361 assert_eq!(roundtrip(value.clone()), value);
1362 }
1363
1364 #[test]
1365 fn test_timeticks() {
1366 let value = Value::TimeTicks(123456);
1367 assert_eq!(roundtrip(value.clone()), value);
1368 }
1369
1370 #[test]
1371 fn test_timeticks_max() {
1372 let value = Value::TimeTicks(u32::MAX);
1373 assert_eq!(roundtrip(value.clone()), value);
1374 }
1375
1376 #[test]
1377 fn test_opaque() {
1378 let value = Value::Opaque(Bytes::from_static(&[0xDE, 0xAD, 0xBE, 0xEF]));
1379 assert_eq!(roundtrip(value.clone()), value);
1380 }
1381
1382 #[test]
1383 fn test_opaque_empty() {
1384 let value = Value::Opaque(Bytes::new());
1385 assert_eq!(roundtrip(value.clone()), value);
1386 }
1387
1388 #[test]
1389 fn test_counter64() {
1390 let value = Value::Counter64(123456789012345);
1391 assert_eq!(roundtrip(value.clone()), value);
1392 }
1393
1394 #[test]
1395 fn test_counter64_zero() {
1396 let value = Value::Counter64(0);
1397 assert_eq!(roundtrip(value.clone()), value);
1398 }
1399
1400 #[test]
1401 fn test_counter64_max() {
1402 let value = Value::Counter64(u64::MAX);
1403 assert_eq!(roundtrip(value.clone()), value);
1404 }
1405
1406 #[test]
1407 fn test_no_such_object() {
1408 let value = Value::NoSuchObject;
1409 assert_eq!(roundtrip(value.clone()), value);
1410 }
1411
1412 #[test]
1413 fn test_no_such_instance() {
1414 let value = Value::NoSuchInstance;
1415 assert_eq!(roundtrip(value.clone()), value);
1416 }
1417
1418 #[test]
1419 fn test_end_of_mib_view() {
1420 let value = Value::EndOfMibView;
1421 assert_eq!(roundtrip(value.clone()), value);
1422 }
1423
1424 #[test]
1425 fn test_unknown_tag_preserved() {
1426 let data = Bytes::from_static(&[0x45, 0x03, 0x01, 0x02, 0x03]);
1428 let mut decoder = Decoder::new(data);
1429 let value = Value::decode(&mut decoder).unwrap();
1430
1431 match value {
1432 Value::Unknown { tag, ref data } => {
1433 assert_eq!(tag, 0x45);
1434 assert_eq!(data.as_ref(), &[0x01, 0x02, 0x03]);
1435 }
1436 _ => panic!("expected Unknown variant"),
1437 }
1438
1439 assert_eq!(roundtrip(value.clone()), value);
1441 }
1442
1443 #[test]
1448 fn test_as_i32() {
1449 assert_eq!(Value::Integer(42).as_i32(), Some(42));
1450 assert_eq!(Value::Integer(-42).as_i32(), Some(-42));
1451 assert_eq!(Value::Counter32(100).as_i32(), None);
1452 assert_eq!(Value::Null.as_i32(), None);
1453 }
1454
1455 #[test]
1456 fn test_as_u32() {
1457 assert_eq!(Value::Counter32(100).as_u32(), Some(100));
1458 assert_eq!(Value::Gauge32(200).as_u32(), Some(200));
1459 assert_eq!(Value::TimeTicks(300).as_u32(), Some(300));
1460 assert_eq!(Value::Integer(50).as_u32(), Some(50));
1461 assert_eq!(Value::Integer(-1).as_u32(), None);
1462 assert_eq!(Value::Counter64(100).as_u32(), None);
1463 }
1464
1465 #[test]
1466 fn test_as_u64() {
1467 assert_eq!(Value::Counter64(100).as_u64(), Some(100));
1468 assert_eq!(Value::Counter32(100).as_u64(), Some(100));
1469 assert_eq!(Value::Gauge32(200).as_u64(), Some(200));
1470 assert_eq!(Value::TimeTicks(300).as_u64(), Some(300));
1471 assert_eq!(Value::Integer(50).as_u64(), Some(50));
1472 assert_eq!(Value::Integer(-1).as_u64(), None);
1473 }
1474
1475 #[test]
1476 fn test_as_bytes() {
1477 let s = Value::OctetString(Bytes::from_static(b"test"));
1478 assert_eq!(s.as_bytes(), Some(b"test".as_slice()));
1479
1480 let o = Value::Opaque(Bytes::from_static(b"data"));
1481 assert_eq!(o.as_bytes(), Some(b"data".as_slice()));
1482
1483 assert_eq!(Value::Integer(1).as_bytes(), None);
1484 }
1485
1486 #[test]
1487 fn test_as_str() {
1488 let s = Value::OctetString(Bytes::from_static(b"hello"));
1489 assert_eq!(s.as_str(), Some("hello"));
1490
1491 let invalid = Value::OctetString(Bytes::from_static(&[0xFF, 0xFE]));
1493 assert_eq!(invalid.as_str(), None);
1494
1495 assert_eq!(Value::Integer(1).as_str(), None);
1496 }
1497
1498 #[test]
1499 fn test_as_oid() {
1500 let oid = crate::oid!(1, 3, 6, 1);
1501 let v = Value::ObjectIdentifier(oid.clone());
1502 assert_eq!(v.as_oid(), Some(&oid));
1503
1504 assert_eq!(Value::Integer(1).as_oid(), None);
1505 }
1506
1507 #[test]
1508 fn test_as_ip() {
1509 let v = Value::IpAddress([192, 168, 1, 1]);
1510 assert_eq!(v.as_ip(), Some(std::net::Ipv4Addr::new(192, 168, 1, 1)));
1511
1512 assert_eq!(Value::Integer(1).as_ip(), None);
1513 }
1514
1515 #[test]
1520 fn test_is_exception() {
1521 assert!(Value::NoSuchObject.is_exception());
1522 assert!(Value::NoSuchInstance.is_exception());
1523 assert!(Value::EndOfMibView.is_exception());
1524
1525 assert!(!Value::Integer(1).is_exception());
1526 assert!(!Value::Null.is_exception());
1527 assert!(!Value::OctetString(Bytes::new()).is_exception());
1528 }
1529
1530 #[test]
1535 fn test_display_integer() {
1536 assert_eq!(format!("{}", Value::Integer(42)), "42");
1537 assert_eq!(format!("{}", Value::Integer(-42)), "-42");
1538 }
1539
1540 #[test]
1541 fn test_display_octet_string_utf8() {
1542 let v = Value::OctetString(Bytes::from_static(b"hello"));
1543 assert_eq!(format!("{}", v), "hello");
1544 }
1545
1546 #[test]
1547 fn test_display_octet_string_binary() {
1548 let v = Value::OctetString(Bytes::from_static(&[0xFF, 0xFE]));
1550 assert_eq!(format!("{}", v), "0xfffe");
1551 }
1552
1553 #[test]
1554 fn test_display_null() {
1555 assert_eq!(format!("{}", Value::Null), "NULL");
1556 }
1557
1558 #[test]
1559 fn test_display_ip_address() {
1560 let v = Value::IpAddress([192, 168, 1, 1]);
1561 assert_eq!(format!("{}", v), "192.168.1.1");
1562 }
1563
1564 #[test]
1565 fn test_display_counter32() {
1566 assert_eq!(format!("{}", Value::Counter32(999)), "999");
1567 }
1568
1569 #[test]
1570 fn test_display_gauge32() {
1571 assert_eq!(format!("{}", Value::Gauge32(1000)), "1000");
1572 }
1573
1574 #[test]
1575 fn test_display_timeticks() {
1576 let v = Value::TimeTicks(123456);
1579 assert_eq!(format!("{}", v), "0d 0h 20m 34s");
1580 }
1581
1582 #[test]
1583 fn test_display_opaque() {
1584 let v = Value::Opaque(Bytes::from_static(&[0xBE, 0xEF]));
1585 assert_eq!(format!("{}", v), "Opaque(0xbeef)");
1586 }
1587
1588 #[test]
1589 fn test_display_counter64() {
1590 assert_eq!(format!("{}", Value::Counter64(12345678)), "12345678");
1591 }
1592
1593 #[test]
1594 fn test_display_exceptions() {
1595 assert_eq!(format!("{}", Value::NoSuchObject), "noSuchObject");
1596 assert_eq!(format!("{}", Value::NoSuchInstance), "noSuchInstance");
1597 assert_eq!(format!("{}", Value::EndOfMibView), "endOfMibView");
1598 }
1599
1600 #[test]
1601 fn test_display_unknown() {
1602 let v = Value::Unknown {
1603 tag: 0x99,
1604 data: Bytes::from_static(&[0x01, 0x02]),
1605 };
1606 assert_eq!(format!("{}", v), "Unknown(tag=0x99, data=0x0102)");
1607 }
1608
1609 #[test]
1614 fn test_from_i32() {
1615 let v: Value = 42i32.into();
1616 assert_eq!(v, Value::Integer(42));
1617 }
1618
1619 #[test]
1620 fn test_from_str() {
1621 let v: Value = "hello".into();
1622 assert_eq!(v.as_str(), Some("hello"));
1623 }
1624
1625 #[test]
1626 fn test_from_string() {
1627 let v: Value = String::from("hello").into();
1628 assert_eq!(v.as_str(), Some("hello"));
1629 }
1630
1631 #[test]
1632 fn test_from_bytes_slice() {
1633 let v: Value = (&[1u8, 2, 3][..]).into();
1634 assert_eq!(v.as_bytes(), Some(&[1u8, 2, 3][..]));
1635 }
1636
1637 #[test]
1638 fn test_from_oid() {
1639 let oid = crate::oid!(1, 3, 6, 1);
1640 let v: Value = oid.clone().into();
1641 assert_eq!(v.as_oid(), Some(&oid));
1642 }
1643
1644 #[test]
1645 fn test_from_ipv4addr() {
1646 let addr = std::net::Ipv4Addr::new(10, 0, 0, 1);
1647 let v: Value = addr.into();
1648 assert_eq!(v, Value::IpAddress([10, 0, 0, 1]));
1649 }
1650
1651 #[test]
1652 fn test_from_bytes() {
1653 let data = Bytes::from_static(b"hello");
1654 let v: Value = data.into();
1655 assert_eq!(v.as_bytes(), Some(b"hello".as_slice()));
1656 }
1657
1658 #[test]
1659 fn test_from_u64() {
1660 let v: Value = 12345678901234u64.into();
1661 assert_eq!(v, Value::Counter64(12345678901234));
1662 }
1663
1664 #[test]
1665 fn test_from_ip_array() {
1666 let v: Value = [192u8, 168, 1, 1].into();
1667 assert_eq!(v, Value::IpAddress([192, 168, 1, 1]));
1668 }
1669
1670 #[test]
1675 fn test_value_eq_and_hash() {
1676 use std::collections::HashSet;
1677
1678 let mut set = HashSet::new();
1679 set.insert(Value::Integer(42));
1680 set.insert(Value::Integer(42)); set.insert(Value::Integer(100));
1682
1683 assert_eq!(set.len(), 2);
1684 assert!(set.contains(&Value::Integer(42)));
1685 assert!(set.contains(&Value::Integer(100)));
1686 }
1687
1688 #[test]
1693 fn test_decode_invalid_null_length() {
1694 let data = Bytes::from_static(&[0x05, 0x01, 0x00]); let mut decoder = Decoder::new(data);
1697 let result = Value::decode(&mut decoder);
1698 assert!(result.is_err());
1699 }
1700
1701 #[test]
1702 fn test_decode_invalid_ip_address_length() {
1703 let data = Bytes::from_static(&[0x40, 0x03, 0x01, 0x02, 0x03]); let mut decoder = Decoder::new(data);
1706 let result = Value::decode(&mut decoder);
1707 assert!(result.is_err());
1708 }
1709
1710 #[test]
1711 fn test_decode_exception_with_content_accepted() {
1712 let data = Bytes::from_static(&[0x80, 0x01, 0xFF]); let mut decoder = Decoder::new(data);
1715 let result = Value::decode(&mut decoder);
1716 assert!(result.is_ok());
1717 assert_eq!(result.unwrap(), Value::NoSuchObject);
1718 }
1719
1720 #[test]
1725 fn test_as_f64() {
1726 assert_eq!(Value::Integer(42).as_f64(), Some(42.0));
1727 assert_eq!(Value::Integer(-42).as_f64(), Some(-42.0));
1728 assert_eq!(Value::Counter32(1000).as_f64(), Some(1000.0));
1729 assert_eq!(Value::Gauge32(2000).as_f64(), Some(2000.0));
1730 assert_eq!(Value::TimeTicks(3000).as_f64(), Some(3000.0));
1731 assert_eq!(
1732 Value::Counter64(10_000_000_000).as_f64(),
1733 Some(10_000_000_000.0)
1734 );
1735 assert_eq!(Value::Null.as_f64(), None);
1736 assert_eq!(
1737 Value::OctetString(Bytes::from_static(b"test")).as_f64(),
1738 None
1739 );
1740 }
1741
1742 #[test]
1743 fn test_as_f64_wrapped() {
1744 assert_eq!(Value::Counter64(1000).as_f64_wrapped(), Some(1000.0));
1746 assert_eq!(Value::Counter32(1000).as_f64_wrapped(), Some(1000.0));
1747 assert_eq!(Value::Integer(42).as_f64_wrapped(), Some(42.0));
1748
1749 let mantissa_limit = 1u64 << 53;
1751 assert_eq!(Value::Counter64(mantissa_limit).as_f64_wrapped(), Some(0.0));
1752 assert_eq!(
1753 Value::Counter64(mantissa_limit + 1).as_f64_wrapped(),
1754 Some(1.0)
1755 );
1756 }
1757
1758 #[test]
1759 fn test_as_decimal() {
1760 assert_eq!(Value::Integer(2350).as_decimal(2), Some(23.50));
1761 assert_eq!(Value::Integer(9999).as_decimal(2), Some(99.99));
1762 assert_eq!(Value::Integer(12500).as_decimal(3), Some(12.5));
1763 assert_eq!(Value::Integer(-500).as_decimal(2), Some(-5.0));
1764 assert_eq!(Value::Counter32(1000).as_decimal(1), Some(100.0));
1765 assert_eq!(Value::Null.as_decimal(2), None);
1766 }
1767
1768 #[test]
1769 fn test_as_duration() {
1770 use std::time::Duration;
1771
1772 assert_eq!(
1774 Value::TimeTicks(100).as_duration(),
1775 Some(Duration::from_secs(1))
1776 );
1777 assert_eq!(
1779 Value::TimeTicks(360000).as_duration(),
1780 Some(Duration::from_secs(3600))
1781 );
1782 assert_eq!(
1784 Value::TimeTicks(1).as_duration(),
1785 Some(Duration::from_millis(10))
1786 );
1787
1788 assert_eq!(Value::Integer(100).as_duration(), None);
1790 assert_eq!(Value::Counter32(100).as_duration(), None);
1791 }
1792
1793 #[test]
1798 fn test_as_opaque_float() {
1799 let data = Bytes::from_static(&[0x9f, 0x78, 0x04, 0x40, 0x49, 0x0f, 0xdb]);
1802 let value = Value::Opaque(data);
1803 let pi = value.as_opaque_float().unwrap();
1804 assert!((pi - std::f32::consts::PI).abs() < 0.0001);
1805
1806 assert_eq!(Value::Integer(42).as_opaque_float(), None);
1808
1809 let wrong_type = Bytes::from_static(&[0x9f, 0x79, 0x04, 0x40, 0x49, 0x0f, 0xdb]);
1811 assert_eq!(Value::Opaque(wrong_type).as_opaque_float(), None);
1812
1813 let short = Bytes::from_static(&[0x9f, 0x78, 0x04, 0x40, 0x49]);
1815 assert_eq!(Value::Opaque(short).as_opaque_float(), None);
1816 }
1817
1818 #[test]
1819 fn test_as_opaque_double() {
1820 let data = Bytes::from_static(&[
1823 0x9f, 0x79, 0x08, 0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18,
1824 ]);
1825 let value = Value::Opaque(data);
1826 let pi = value.as_opaque_double().unwrap();
1827 assert!((pi - std::f64::consts::PI).abs() < 1e-10);
1828
1829 assert_eq!(Value::Integer(42).as_opaque_double(), None);
1831 }
1832
1833 #[test]
1834 fn test_as_opaque_counter64() {
1835 let data = Bytes::from_static(&[
1837 0x9f, 0x76, 0x08, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
1838 ]);
1839 let value = Value::Opaque(data);
1840 assert_eq!(value.as_opaque_counter64(), Some(0x0123456789ABCDEF));
1841
1842 let small = Bytes::from_static(&[0x9f, 0x76, 0x01, 0x42]);
1844 assert_eq!(Value::Opaque(small).as_opaque_counter64(), Some(0x42));
1845
1846 let zero = Bytes::from_static(&[0x9f, 0x76, 0x01, 0x00]);
1848 assert_eq!(Value::Opaque(zero).as_opaque_counter64(), Some(0));
1849 }
1850
1851 #[test]
1852 fn test_as_opaque_i64() {
1853 let positive = Bytes::from_static(&[0x9f, 0x7a, 0x02, 0x01, 0x00]);
1855 assert_eq!(Value::Opaque(positive).as_opaque_i64(), Some(256));
1856
1857 let minus_one = Bytes::from_static(&[0x9f, 0x7a, 0x01, 0xFF]);
1859 assert_eq!(Value::Opaque(minus_one).as_opaque_i64(), Some(-1));
1860
1861 let full_neg = Bytes::from_static(&[
1863 0x9f, 0x7a, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1864 ]);
1865 assert_eq!(Value::Opaque(full_neg).as_opaque_i64(), Some(-1));
1866
1867 let min = Bytes::from_static(&[
1869 0x9f, 0x7a, 0x08, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1870 ]);
1871 assert_eq!(Value::Opaque(min).as_opaque_i64(), Some(i64::MIN));
1872 }
1873
1874 #[test]
1875 fn test_as_opaque_u64() {
1876 let data = Bytes::from_static(&[
1878 0x9f, 0x7b, 0x08, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
1879 ]);
1880 let value = Value::Opaque(data);
1881 assert_eq!(value.as_opaque_u64(), Some(0x0123456789ABCDEF));
1882
1883 let max = Bytes::from_static(&[
1885 0x9f, 0x7b, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1886 ]);
1887 assert_eq!(Value::Opaque(max).as_opaque_u64(), Some(u64::MAX));
1888 }
1889
1890 #[test]
1891 fn test_format_with_hint_integer() {
1892 assert_eq!(
1894 Value::Integer(2350).format_with_hint("d-2"),
1895 Some("23.50".into())
1896 );
1897 assert_eq!(
1898 Value::Integer(-500).format_with_hint("d-2"),
1899 Some("-5.00".into())
1900 );
1901
1902 assert_eq!(Value::Integer(255).format_with_hint("x"), Some("ff".into()));
1904 assert_eq!(Value::Integer(8).format_with_hint("o"), Some("10".into()));
1905 assert_eq!(Value::Integer(5).format_with_hint("b"), Some("101".into()));
1906 assert_eq!(Value::Integer(42).format_with_hint("d"), Some("42".into()));
1907
1908 assert_eq!(Value::Integer(42).format_with_hint("invalid"), None);
1910
1911 assert_eq!(Value::Counter32(42).format_with_hint("d-2"), None);
1913 }
1914
1915 #[test]
1916 fn test_as_truth_value() {
1917 assert_eq!(Value::Integer(1).as_truth_value(), Some(true));
1919 assert_eq!(Value::Integer(2).as_truth_value(), Some(false));
1920
1921 assert_eq!(Value::Integer(0).as_truth_value(), None);
1923 assert_eq!(Value::Integer(3).as_truth_value(), None);
1924 assert_eq!(Value::Integer(-1).as_truth_value(), None);
1925
1926 assert_eq!(Value::Null.as_truth_value(), None);
1928 assert_eq!(Value::Counter32(1).as_truth_value(), None);
1929 assert_eq!(Value::Gauge32(1).as_truth_value(), None);
1930 }
1931
1932 #[test]
1937 fn test_row_status_from_i32() {
1938 assert_eq!(RowStatus::from_i32(1), Some(RowStatus::Active));
1939 assert_eq!(RowStatus::from_i32(2), Some(RowStatus::NotInService));
1940 assert_eq!(RowStatus::from_i32(3), Some(RowStatus::NotReady));
1941 assert_eq!(RowStatus::from_i32(4), Some(RowStatus::CreateAndGo));
1942 assert_eq!(RowStatus::from_i32(5), Some(RowStatus::CreateAndWait));
1943 assert_eq!(RowStatus::from_i32(6), Some(RowStatus::Destroy));
1944
1945 assert_eq!(RowStatus::from_i32(0), None);
1947 assert_eq!(RowStatus::from_i32(7), None);
1948 assert_eq!(RowStatus::from_i32(-1), None);
1949 }
1950
1951 #[test]
1952 fn test_row_status_into_value() {
1953 let v: Value = RowStatus::Active.into();
1954 assert_eq!(v, Value::Integer(1));
1955
1956 let v: Value = RowStatus::Destroy.into();
1957 assert_eq!(v, Value::Integer(6));
1958 }
1959
1960 #[test]
1961 fn test_row_status_display() {
1962 assert_eq!(format!("{}", RowStatus::Active), "active");
1963 assert_eq!(format!("{}", RowStatus::NotInService), "notInService");
1964 assert_eq!(format!("{}", RowStatus::NotReady), "notReady");
1965 assert_eq!(format!("{}", RowStatus::CreateAndGo), "createAndGo");
1966 assert_eq!(format!("{}", RowStatus::CreateAndWait), "createAndWait");
1967 assert_eq!(format!("{}", RowStatus::Destroy), "destroy");
1968 }
1969
1970 #[test]
1971 fn test_as_row_status() {
1972 assert_eq!(Value::Integer(1).as_row_status(), Some(RowStatus::Active));
1974 assert_eq!(Value::Integer(6).as_row_status(), Some(RowStatus::Destroy));
1975
1976 assert_eq!(Value::Integer(0).as_row_status(), None);
1978 assert_eq!(Value::Integer(7).as_row_status(), None);
1979
1980 assert_eq!(Value::Null.as_row_status(), None);
1982 assert_eq!(Value::Counter32(1).as_row_status(), None);
1983 }
1984
1985 #[test]
1990 fn test_storage_type_from_i32() {
1991 assert_eq!(StorageType::from_i32(1), Some(StorageType::Other));
1992 assert_eq!(StorageType::from_i32(2), Some(StorageType::Volatile));
1993 assert_eq!(StorageType::from_i32(3), Some(StorageType::NonVolatile));
1994 assert_eq!(StorageType::from_i32(4), Some(StorageType::Permanent));
1995 assert_eq!(StorageType::from_i32(5), Some(StorageType::ReadOnly));
1996
1997 assert_eq!(StorageType::from_i32(0), None);
1999 assert_eq!(StorageType::from_i32(6), None);
2000 assert_eq!(StorageType::from_i32(-1), None);
2001 }
2002
2003 #[test]
2004 fn test_storage_type_into_value() {
2005 let v: Value = StorageType::Volatile.into();
2006 assert_eq!(v, Value::Integer(2));
2007
2008 let v: Value = StorageType::NonVolatile.into();
2009 assert_eq!(v, Value::Integer(3));
2010 }
2011
2012 #[test]
2013 fn test_storage_type_display() {
2014 assert_eq!(format!("{}", StorageType::Other), "other");
2015 assert_eq!(format!("{}", StorageType::Volatile), "volatile");
2016 assert_eq!(format!("{}", StorageType::NonVolatile), "nonVolatile");
2017 assert_eq!(format!("{}", StorageType::Permanent), "permanent");
2018 assert_eq!(format!("{}", StorageType::ReadOnly), "readOnly");
2019 }
2020
2021 #[test]
2022 fn test_as_storage_type() {
2023 assert_eq!(
2025 Value::Integer(2).as_storage_type(),
2026 Some(StorageType::Volatile)
2027 );
2028 assert_eq!(
2029 Value::Integer(3).as_storage_type(),
2030 Some(StorageType::NonVolatile)
2031 );
2032
2033 assert_eq!(Value::Integer(0).as_storage_type(), None);
2035 assert_eq!(Value::Integer(6).as_storage_type(), None);
2036
2037 assert_eq!(Value::Null.as_storage_type(), None);
2039 assert_eq!(Value::Counter32(1).as_storage_type(), None);
2040 }
2041}