use embedded_time::Instant;
use no_std_net::SocketAddr;
use toad_msg::{CodeKind, Token};
use super::{log, Step};
use crate::config::Config;
use crate::net::Addrd;
use crate::platform;
use crate::platform::PlatformTypes;
use crate::req::Req;
use crate::resp::Resp;
use crate::time::Millis;
#[derive(PartialEq, Eq, PartialOrd, Clone, Copy)]
pub enum Error<E> {
Inner(E),
MillisSinceEpochWouldOverflow,
}
impl<E: core::fmt::Debug> core::fmt::Debug for Error<E> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
| Self::MillisSinceEpochWouldOverflow => {
f.debug_tuple("MillisSinceEpochWouldOverflow").finish()
},
| Self::Inner(e) => e.fmt(f),
}
}
}
impl<E> super::Error for Error<E> where E: super::Error {}
impl<E> From<E> for Error<E> {
fn from(e: E) -> Self {
Error::Inner(e)
}
}
#[derive(Debug, Clone)]
pub struct ProvisionTokens<Inner> {
inner: Inner,
}
impl<Inner> Default for ProvisionTokens<Inner> where Inner: Default
{
fn default() -> Self {
Self { inner: Default::default() }
}
}
impl<Inner> ProvisionTokens<Inner> {
fn next<P>(&self,
effs: &mut P::Effects,
now: Instant<P::Clock>,
cfg: Config)
-> Result<Token, Error<Inner::Error>>
where P: PlatformTypes,
Inner: Step<P>
{
let now_since_epoch =
Millis::try_from(now.duration_since_epoch()).map_err(|_| {
Error::MillisSinceEpochWouldOverflow
})?;
#[allow(clippy::many_single_char_names)]
let bytes = {
let ([a, b], [c, d, e, f, g, h, i, j]) =
(cfg.msg.token_seed.to_be_bytes(), now_since_epoch.0.to_be_bytes());
[a, b, c, d, e, f, g, h, i, j]
};
let next = Token::opaque(&bytes);
log!(ProvisionTokens::next,
effs,
log::Level::Debug,
"Generated new {:?}",
next);
Ok(next)
}
}
impl<P, E: super::Error, Inner> Step<P> for ProvisionTokens<Inner>
where P: PlatformTypes,
Inner: Step<P, PollReq = Addrd<Req<P>>, PollResp = Addrd<Resp<P>>, Error = E>
{
type PollReq = Addrd<Req<P>>;
type PollResp = Addrd<Resp<P>>;
type Error = Error<E>;
type Inner = Inner;
fn inner(&self) -> &Inner {
&self.inner
}
fn before_message_sent(&self,
snap: &platform::Snapshot<P>,
effs: &mut P::Effects,
msg: &mut Addrd<platform::Message<P>>)
-> Result<(), Self::Error> {
self.inner.before_message_sent(snap, effs, msg)?;
let token = match (msg.data().code.kind(), msg.data().token) {
| (CodeKind::Request, t) if t == Token(Default::default()) => {
self.next(effs, snap.time, snap.config)?
},
| (_, t) => t,
};
msg.data_mut().token = token;
Ok(())
}
fn poll_req(&self,
snap: &platform::Snapshot<P>,
effects: &mut <P as PlatformTypes>::Effects)
-> super::StepOutput<Self::PollReq, Self::Error> {
self.inner
.poll_req(snap, effects)
.map(|r| r.map_err(|e| e.map(Error::Inner)))
}
fn poll_resp(&self,
snap: &platform::Snapshot<P>,
effects: &mut <P as PlatformTypes>::Effects,
token: Token,
addr: SocketAddr)
-> super::StepOutput<Self::PollResp, Self::Error> {
self.inner
.poll_resp(snap, effects, token, addr)
.map(|r| r.map_err(|e| e.map(Error::Inner)))
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::step::test::test_step;
use crate::test::{ClockMock, Snapshot};
type InnerPollReq = Addrd<Req<crate::test::Platform>>;
type InnerPollResp = Addrd<Resp<crate::test::Platform>>;
test_step!(
GIVEN ProvisionTokens::<Dummy> where Dummy: {Step<PollReq = InnerPollReq, PollResp = InnerPollResp, Error = ()>};
WHEN inner_errors [
(inner.poll_req => { Some(Err(nb::Error::Other(()))) }),
(inner.poll_resp => { Some(Err(nb::Error::Other(()))) })
]
THEN this_should_error [
(poll_req(_, _) should satisfy { |out| assert_eq!(out, Some(Err(nb::Error::Other(Error::Inner(()))))) }),
(poll_resp(_, _, _, _) should satisfy { |out| assert_eq!(out, Some(Err(nb::Error::Other(Error::Inner(()))))) })
]
);
test_step!(
GIVEN ProvisionTokens::<Dummy> where Dummy: {Step<PollReq = InnerPollReq, PollResp = InnerPollResp, Error = ()>};
WHEN inner_blocks [
(inner.poll_req => { Some(Err(nb::Error::WouldBlock)) }),
(inner.poll_resp => { Some(Err(nb::Error::WouldBlock)) })
]
THEN this_should_block [
(poll_req(_, _) should satisfy { |out| assert_eq!(out, Some(Err(nb::Error::WouldBlock))) }),
(poll_resp(_, _, _, _) should satisfy { |out| assert_eq!(out, Some(Err(nb::Error::WouldBlock))) })
]
);
test_step!(
GIVEN ProvisionTokens::<Dummy> where Dummy: {Step<PollReq = InnerPollReq, PollResp = InnerPollResp, Error = ()>};
WHEN we_boutta_send_a_request [
(inner.before_message_sent = { |_, _, _| Ok(()) })
]
THEN this_should_make_sure_it_has_a_token [
(before_message_sent(
Snapshot { time: ClockMock::instant(0),
recvd_dgram: Some(Addrd(Default::default(), crate::test::dummy_addr())),
config: Config::default() },
_,
crate::test::msg!(CON GET x.x.x.x:80)
) should satisfy { |m| assert_ne!(m.data().token, Token(Default::default())) })
]
);
test_step!(
GIVEN ProvisionTokens::<Dummy> where Dummy: {Step<PollReq = InnerPollReq, PollResp = InnerPollResp, Error = ()>};
WHEN we_boutta_send_a_response [
(inner.before_message_sent = { |_, _, _| Ok(()) })
]
THEN this_should_make_sure_it_has_a_token [
(before_message_sent(
Snapshot { time: ClockMock::instant(0),
recvd_dgram: Some(Addrd(Default::default(), crate::test::dummy_addr())),
config: Config::default() },
_,
crate::test::msg!(CON {2 . 04} x.x.x.x:80)
) should satisfy { |m| assert_eq!(m.data().token, Token(Default::default())) })
]
);
}