domain/base/opt/
keepalive.rs1use super::super::iana::OptionCode;
12use super::super::message_builder::OptBuilder;
13use super::super::wire::{Compose, Composer, Parse, ParseError};
14use super::{ComposeOptData, Opt, OptData, ParseOptData};
15use core::fmt;
16use core::time::Duration;
17use octseq::builder::OctetsBuilder;
18use octseq::octets::Octets;
19use octseq::parse::Parser;
20
21#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize))]
33pub struct TcpKeepalive(Option<IdleTimeout>);
34
35impl TcpKeepalive {
36 pub(super) const CODE: OptionCode = OptionCode::TCP_KEEPALIVE;
38
39 #[must_use]
41 pub fn new(timeout: Option<IdleTimeout>) -> Self {
42 TcpKeepalive(timeout)
43 }
44
45 #[must_use]
47 pub fn timeout(self) -> Option<IdleTimeout> {
48 self.0
49 }
50
51 pub fn parse<Octs: AsRef<[u8]>>(
53 parser: &mut Parser<'_, Octs>,
54 ) -> Result<Self, ParseError> {
55 if parser.remaining() == 0 {
56 Ok(Self::new(None))
57 } else {
58 IdleTimeout::parse(parser).map(|v| Self::new(Some(v)))
59 }
60 }
61
62 pub(super) fn try_octets_from<E>(src: Self) -> Result<Self, E> {
66 Ok(src)
67 }
68}
69
70impl OptData for TcpKeepalive {
73 fn code(&self) -> OptionCode {
74 OptionCode::TCP_KEEPALIVE
75 }
76}
77
78impl<'a, Octs: AsRef<[u8]>> ParseOptData<'a, Octs> for TcpKeepalive {
79 fn parse_option(
80 code: OptionCode,
81 parser: &mut Parser<'a, Octs>,
82 ) -> Result<Option<Self>, ParseError> {
83 if code == OptionCode::TCP_KEEPALIVE {
84 Self::parse(parser).map(Some)
85 } else {
86 Ok(None)
87 }
88 }
89}
90
91impl ComposeOptData for TcpKeepalive {
92 fn compose_len(&self) -> u16 {
93 match self.0 {
94 Some(_) => IdleTimeout::COMPOSE_LEN,
95 None => 0,
96 }
97 }
98
99 fn compose_option<Target: OctetsBuilder + ?Sized>(
100 &self,
101 target: &mut Target,
102 ) -> Result<(), Target::AppendError> {
103 match self.0 {
104 Some(v) => v.compose(target),
105 None => Ok(()),
106 }
107 }
108}
109
110impl fmt::Display for TcpKeepalive {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 match self.0 {
115 Some(v) => write!(f, "{}", v),
116 None => write!(f, ""),
117 }
118 }
119}
120
121impl<Octs: Octets> Opt<Octs> {
124 pub fn tcp_keepalive(&self) -> Option<TcpKeepalive> {
129 self.first()
130 }
131}
132
133impl<Target: Composer> OptBuilder<'_, Target> {
134 pub fn tcp_keepalive(
135 &mut self,
136 timeout: Option<IdleTimeout>,
137 ) -> Result<(), Target::AppendError> {
138 self.push(&TcpKeepalive::new(timeout))
139 }
140}
141
142#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
150#[cfg_attr(feature = "serde", derive(serde::Serialize))]
151pub struct IdleTimeout(u16);
152
153impl IdleTimeout {
154 const COMPOSE_LEN: u16 = 2;
156
157 fn parse<Octs: AsRef<[u8]> + ?Sized>(
159 parser: &mut Parser<'_, Octs>,
160 ) -> Result<Self, ParseError> {
161 u16::parse(parser).map(Self)
162 }
163
164 fn compose<Target: OctetsBuilder + ?Sized>(
166 &self,
167 target: &mut Target,
168 ) -> Result<(), Target::AppendError> {
169 self.0.compose(target)
170 }
171}
172
173impl From<u16> for IdleTimeout {
176 fn from(src: u16) -> Self {
177 Self(src)
178 }
179}
180
181impl From<IdleTimeout> for u16 {
182 fn from(src: IdleTimeout) -> u16 {
183 src.0
184 }
185}
186
187impl TryFrom<Duration> for IdleTimeout {
188 type Error = FromDurationError;
189
190 fn try_from(duration: Duration) -> Result<Self, Self::Error> {
191 Ok(Self(
192 u16::try_from(
193 duration
194 .as_secs()
195 .checked_mul(10)
196 .ok_or(FromDurationError(()))?
197 + u64::from(duration.subsec_millis() / 100),
198 )
199 .map_err(|_| FromDurationError(()))?,
200 ))
201 }
202}
203
204impl From<IdleTimeout> for Duration {
205 fn from(src: IdleTimeout) -> Self {
206 Duration::from_millis(u64::from(src.0) * 100)
207 }
208}
209
210impl fmt::Display for IdleTimeout {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 self.0.fmt(f)
215 }
216}
217
218#[derive(Clone, Copy, Debug)]
222pub struct FromDurationError(());
223
224impl fmt::Display for FromDurationError {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 f.write_str("duration too large")
227 }
228}
229
230#[cfg(feature = "std")]
231impl std::error::Error for FromDurationError {}
232
233#[cfg(test)]
236#[cfg(all(feature = "std", feature = "bytes"))]
237mod test {
238 use super::super::test::test_option_compose_parse;
239 use super::*;
240
241 #[test]
242 #[allow(clippy::redundant_closure)] fn tcp_keepalive_compose_parse_none() {
244 test_option_compose_parse(&TcpKeepalive::new(None), |parser| {
245 TcpKeepalive::parse(parser)
246 });
247 }
248
249 #[test]
250 #[allow(clippy::redundant_closure)] fn tcp_keepalive_compose_parse_some() {
252 test_option_compose_parse(
253 &TcpKeepalive::new(Some(12.into())),
254 |parser| TcpKeepalive::parse(parser),
255 );
256 }
257}