packet_formats/igmp/
types.rs1use core::time::Duration;
8
9use super::IgmpMaxRespCode;
10use crate::gmp::{ExactConversionError, LinExpConversion, OverflowError};
11
12#[derive(Debug, PartialEq)]
19pub struct IgmpResponseTimeV2(u8);
20
21impl IgmpMaxRespCode for IgmpResponseTimeV2 {
22 fn as_code(&self) -> u8 {
23 self.0
24 }
25
26 fn from_code(code: u8) -> Self {
27 Self(code)
28 }
29}
30
31impl TryFrom<Duration> for IgmpResponseTimeV2 {
32 type Error = OverflowError;
33
34 fn try_from(value: Duration) -> Result<Self, Self::Error> {
35 let tenths = value.as_millis() / 100;
36 Ok(Self(tenths.try_into().map_err(|_| OverflowError)?))
37 }
38}
39
40impl From<IgmpResponseTimeV2> for Duration {
41 fn from(value: IgmpResponseTimeV2) -> Duration {
42 let v: u64 = value.0.into();
43 Self::from_millis(v * 100)
44 }
45}
46
47#[derive(Debug, PartialEq, Copy, Clone)]
53pub struct IgmpResponseTimeV3(u8);
54
55impl IgmpMaxRespCode for IgmpResponseTimeV3 {
56 fn as_code(&self) -> u8 {
57 self.0
58 }
59
60 fn from_code(code: u8) -> Self {
61 Self(code)
62 }
63}
64
65impl LinExpConversion<Duration> for IgmpResponseTimeV3 {
66 const NUM_MANT_BITS: u8 = 4;
67 const NUM_EXP_BITS: u8 = 3;
68
69 fn lossy_try_from(value: Duration) -> Result<Self, OverflowError> {
70 let tenths: u32 = (value.as_millis() / 100).try_into().map_err(|_| OverflowError)?;
71 let code = Self::lossy_try_from_expanded(tenths)?.try_into().map_err(|_| OverflowError)?;
72 Ok(Self(code))
73 }
74}
75
76impl IgmpResponseTimeV3 {
77 pub fn new_lossy(value: Duration) -> Result<Self, OverflowError> {
80 Self::lossy_try_from(value)
81 }
82
83 pub fn new_exact(value: Duration) -> Result<Self, ExactConversionError> {
86 Self::exact_try_from(value)
87 }
88}
89
90impl From<IgmpResponseTimeV3> for Duration {
91 fn from(IgmpResponseTimeV3(value): IgmpResponseTimeV3) -> Duration {
92 let tenths: u64 = IgmpResponseTimeV3::to_expanded(value.into()).into();
95 Duration::from_millis(tenths * 100)
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 pub fn parse_and_serialize_code_v2() {
105 for code in 0..=255 {
106 let response = IgmpResponseTimeV2::from_code(code);
107 assert_eq!(response.as_code(), code);
108 let dur = Duration::from(response);
109 let back = IgmpResponseTimeV2::try_from(dur).unwrap();
110 assert_eq!(dur.as_millis(), u128::from(code) * 100);
111 assert_eq!(code, back.as_code());
112 }
113
114 assert_eq!(
117 IgmpResponseTimeV2::try_from(Duration::from_millis((u64::from(u8::MAX) + 1) * 100,)),
118 Err(OverflowError)
119 );
120 }
121
122 #[test]
123 pub fn parse_and_serialize_code_v3() {
124 let r = Duration::from(IgmpResponseTimeV3::from_code(0x80 | 0x01));
125 assert_eq!(r.as_millis(), 13600);
126 let t = IgmpResponseTimeV3::new_lossy(Duration::from_millis((128 + 8) * 100)).unwrap();
127 assert_eq!(t.as_code(), (0x80 | 0x01));
128 for code in 0..=255 {
129 let response = IgmpResponseTimeV3::from_code(code);
130 assert_eq!(response.as_code(), code);
131 let dur = Duration::from(response);
132 let back = IgmpResponseTimeV3::new_lossy(dur).unwrap();
133 assert_eq!(code, back.as_code());
134 }
135
136 assert_eq!(
139 IgmpResponseTimeV3::new_lossy(Duration::from_millis(
140 (IgmpResponseTimeV3::MAX_VALUE as u64 + 1) * 100,
141 )),
142 Err(OverflowError)
143 );
144 }
145}