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