packet_formats/igmp/
types.rs
1use 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(
118 (u64::from(core::u8::MAX) + 1) * 100,
119 )),
120 Err(OverflowError)
121 );
122 }
123
124 #[test]
125 pub fn parse_and_serialize_code_v3() {
126 let r = Duration::from(IgmpResponseTimeV3::from_code(0x80 | 0x01));
127 assert_eq!(r.as_millis(), 13600);
128 let t = IgmpResponseTimeV3::new_lossy(Duration::from_millis((128 + 8) * 100)).unwrap();
129 assert_eq!(t.as_code(), (0x80 | 0x01));
130 for code in 0..=255 {
131 let response = IgmpResponseTimeV3::from_code(code);
132 assert_eq!(response.as_code(), code);
133 let dur = Duration::from(response);
134 let back = IgmpResponseTimeV3::new_lossy(dur).unwrap();
135 assert_eq!(code, back.as_code());
136 }
137
138 assert_eq!(
141 IgmpResponseTimeV3::new_lossy(Duration::from_millis(
142 (IgmpResponseTimeV3::MAX_VALUE as u64 + 1) * 100,
143 )),
144 Err(OverflowError)
145 );
146 }
147}