use core::fmt;
use core::time::Duration;
use super::super::iana::OptionCode;
use super::super::message_builder::OptBuilder;
use super::super::wire::{Compose, Composer, Parse, ParseError};
use super::{Opt, OptData, ComposeOptData, ParseOptData};
use octseq::builder::OctetsBuilder;
use octseq::octets::Octets;
use octseq::parse::Parser;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct TcpKeepalive(Option<IdleTimeout>);
impl TcpKeepalive {
pub(super) const CODE: OptionCode = OptionCode::TCP_KEEPALIVE;
#[must_use]
pub fn new(timeout: Option<IdleTimeout>) -> Self {
TcpKeepalive(timeout)
}
#[must_use]
pub fn timeout(self) -> Option<IdleTimeout> {
self.0
}
pub fn parse<Octs: AsRef<[u8]>>(
parser: &mut Parser<Octs>
) -> Result<Self, ParseError> {
if parser.remaining() == 0 {
Ok(Self::new(None))
} else {
IdleTimeout::parse(parser).map(|v| Self::new(Some(v)))
}
}
pub(super) fn try_octets_from<E>(src: Self) -> Result<Self, E> {
Ok(src)
}
}
impl OptData for TcpKeepalive {
fn code(&self) -> OptionCode {
OptionCode::TCP_KEEPALIVE
}
}
impl<'a, Octs: AsRef<[u8]>> ParseOptData<'a, Octs> for TcpKeepalive {
fn parse_option(
code: OptionCode,
parser: &mut Parser<'a, Octs>,
) -> Result<Option<Self>, ParseError> {
if code == OptionCode::TCP_KEEPALIVE {
Self::parse(parser).map(Some)
}
else {
Ok(None)
}
}
}
impl ComposeOptData for TcpKeepalive {
fn compose_len(&self) -> u16 {
match self.0 {
Some(_) => IdleTimeout::COMPOSE_LEN,
None => 0,
}
}
fn compose_option<Target: OctetsBuilder + ?Sized>(
&self, target: &mut Target
) -> Result<(), Target::AppendError> {
match self.0 {
Some(v) => v.compose(target),
None => Ok(()),
}
}
}
impl fmt::Display for TcpKeepalive {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
Some(v) => write!(f, "{}", v),
None => write!(f, ""),
}
}
}
impl<Octs: Octets> Opt<Octs> {
pub fn tcp_keepalive(&self) -> Option<TcpKeepalive> {
self.first()
}
}
impl<'a, Target: Composer> OptBuilder<'a, Target> {
pub fn tcp_keepalive(
&mut self, timeout: Option<IdleTimeout>
) -> Result<(), Target::AppendError> {
self.push(&TcpKeepalive::new(timeout))
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct IdleTimeout(u16);
impl IdleTimeout {
const COMPOSE_LEN: u16 = 2;
fn parse<Octs: AsRef<[u8]> + ?Sized>(
parser: &mut Parser<Octs>
) -> Result<Self, ParseError> {
u16::parse(parser).map(Self)
}
fn compose<Target: OctetsBuilder + ?Sized>(
&self, target: &mut Target
) -> Result<(), Target::AppendError> {
self.0.compose(target)
}
}
impl From<u16> for IdleTimeout {
fn from(src: u16) -> Self {
Self(src)
}
}
impl From<IdleTimeout> for u16 {
fn from(src: IdleTimeout) -> u16 {
src.0
}
}
impl TryFrom<Duration> for IdleTimeout {
type Error = FromDurationError;
fn try_from(duration: Duration) -> Result<Self, Self::Error> {
Ok(Self(
u16::try_from(
duration.as_secs().checked_mul(10).ok_or(
FromDurationError(())
)?
+ u64::from(duration.subsec_millis() / 100)
).map_err(|_| FromDurationError(()))?
))
}
}
impl From<IdleTimeout> for Duration {
fn from(src: IdleTimeout) -> Self {
Duration::from_millis(u64::from(src.0) * 100)
}
}
impl fmt::Display for IdleTimeout {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
#[derive(Clone, Copy, Debug)]
pub struct FromDurationError(());
impl fmt::Display for FromDurationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("duration too large")
}
}
#[cfg(feature = "std")]
impl std::error::Error for FromDurationError { }
#[cfg(test)]
#[cfg(all(feature = "std", feature = "bytes"))]
mod test {
use super::*;
use super::super::test::test_option_compose_parse;
#[test]
#[allow(clippy::redundant_closure)] fn tcp_keepalive_compose_parse_none() {
test_option_compose_parse(
&TcpKeepalive::new(None),
|parser| TcpKeepalive::parse(parser)
);
}
#[test]
#[allow(clippy::redundant_closure)] fn tcp_keepalive_compose_parse_some() {
test_option_compose_parse(
&TcpKeepalive::new(Some(12.into())),
|parser| TcpKeepalive::parse(parser)
);
}
}