|
- use std::net::Ipv4Addr;
- use chrono::{DateTime, Utc};
- use uuid::Uuid;
-
- pub struct Event<T> {
- pub time: DateTime<Utc>,
- pub id: Uuid,
- pub event: T,
- }
-
- pub struct Pending{
- pub recipient: Ipv4Addr,
- }
-
- #[derive(Default)]
- pub struct Sending {
- pub payload: Vec<u8>,
- pub bytes_sent: usize,
- pub prev: Event<Pending>,
- }
-
- #[derive(Default)]
- pub struct Sent {
- pub ack_req: bool,
- pub prev: Event<Sending>,
- }
-
- #[derive(Default)]
- pub struct Ack {
- pub data: Vec<u8>,
- pub prev: Event<Sent>,
- }
-
- #[derive(Default)]
- pub struct Finished<T> {
- pub prev: Event<T>,
- }
-
- pub enum Active {
- Pending(Event<Pending>),
- Sending(Event<Sending>),
- Sent(Event<Sent>),
- Acked(Event<Ack>),
- FinishedNoAck(Event<Finished<Sent>>),
- FinishedAcked(Event<Finished<Ack>>),
- }
-
- pub trait Timestamped {
- fn time(&self) -> DateTime<Utc>;
- }
-
- pub trait Chronological {
- type Prev;
- type Next;
-
- fn prev(&self) -> Self::Prev;
- fn next(&self) -> Self::Next;
- }
-
- pub trait Elapsed<P, N>: Chronological<Prev = P, Next = N> {
- fn elapsed(&self) -> chrono::Duration;
- }
-
- impl<T> Timestamped for Event<T> {
- fn time(&self) -> DateTime<Utc> { self.time }
- }
-
- impl<P, N, T> Elapsed<P, N> for T
- where T: Chronological<Prev = P, Next = N>,
- P: Timestamped,
- N: Timestamped
- {
- fn elapsed(&self) -> chrono::Duration {
- self.next().time().signed_duration_since(self.prev().time())
- }
- }
-
- impl<N, T> Elapsed<(), N> for T
- where T: Chronological<Prev = (), Next = N>
- {
- fn elapsed(&self) -> chrono::Duration {
- chrono::Duration::seconds(0)
- }
- }
-
- /*
- impl Timestamped for Active {
- fn time(&self) -> DateTime<Utc> {
- use Active::*;
- match self {
- Pending(event) => event.time,
- Sending(event) => event.time,
- Sent(event) => event.time,
- Acked(event) => event.time,
- FinishedNoAck(event) => event.time,
- FinishedAcked(event) => event.time,
- }
- }
- }
- */
-
- macro_rules! event_attr {
- ($method:ident, $t:ty, $attr:ident) => {
- fn $method(&self) -> $t {
- use Active::*;
- match self {
- Pending(event) => event.$attr,
- Sending(event) => event.$attr,
- Sent(event) => event.$attr,
- Acked(event) => event.$attr,
- FinishedNoAck(event) => event.$attr,
- FinishedAcked(event) => event.$attr,
- }
- }
- }
- }
-
- impl Timestamped for Active {
- event_attr!(time, DateTime<Utc>, time);
- }
-
- macro_rules! optionally_public_event_attr {
-
- // note: `vis` is automatically optional, doesn't need
- // this will work as-is if there is nothing there
-
- ($pub:vis $method:ident, $t:ident, $attr:ident) => {
- $pub fn $method(&self) -> $t {
- use Active::*;
- match self {
- Pending(event) => event.$attr,
- Sending(event) => event.$attr,
- Sent(event) => event.$attr,
- Acked(event) => event.$attr,
- FinishedNoAck(event) => event.$attr,
- FinishedAcked(event) => event.$attr,
- }
- }
- }
- }
-
- impl Active {
- optionally_public_event_attr!(pub id, Uuid, id);
- }
-
- impl From<Event<Pending>> for Active {
- fn from(pending: Event<Pending>) -> Active {
- Active::Pending(pending)
- }
- }
-
- macro_rules! from_event {
- ($t:ty, $variant:ident) => {
- impl From<Event<$t>> for Active {
- fn from(event: Event<$t>) -> Active {
- Active::$variant(event)
- }
- }
- }
- }
-
- //from_event!(Pending, Pending);
- from_event!(Sending, Sending);
- from_event!(Sent, Sent);
- from_event!(Ack, Acked);
- from_event!(Finished<Sent>, FinishedNoAck);
- from_event!(Finished<Ack>, FinishedAcked);
-
- macro_rules! variant_check {
- ($f:ident, $variant:ident) => {
- impl Active {
- pub fn $f(&self) -> bool {
- match self {
- Active::$variant(..) => true,
- _ => false,
- }
- }
- }
- }
- }
-
- variant_check!(is_pending, Pending);
- variant_check!(is_sending, Sending);
- variant_check!(is_sent, Sent);
- variant_check!(is_acked, Acked);
- variant_check!(is_finished_noack, FinishedNoAck);
- variant_check!(is_finished_acked, FinishedAcked);
-
- macro_rules! multiple_variant_check {
- ($f:ident; $($variant:ident),*) => {
- impl Active {
- pub fn $f(&self) -> bool {
- match self {
- $( Active::$variant(..) => { true } )*
-
- _ => false,
- }
- }
- }
- }
- }
-
- multiple_variant_check!(is_finished; FinishedNoAck, FinishedAcked);
- multiple_variant_check!(is_still_unfinished; Pending, Sending, Sent, Acked);
- multiple_variant_check!(anything_but_sent; Pending, Sending, Acked, FinishedNoAck, FinishedAcked);
-
- // needed because Ipv4Addr does not implement Default
- // other event type structs derive Default instead
- impl Default for Pending {
- fn default() -> Self {
- Self { recipient: Ipv4Addr::new(127, 0, 0, 1) }
- }
- }
-
- impl<T> Default for Event<T>
- where T: Default
- {
- fn default() -> Self {
- Self {
- time: Utc::now(),
- id: Uuid::new_v4(),
- event: T::default(),
- }
- }
- }
-
- impl Default for Active {
- fn default() -> Self {
- Active::Pending(Default::default())
- }
- }
-
-
- macro_rules! new_with_default {
- ($f:ident, $t:ty) => {
- impl Active {
- pub fn $f() -> Self {
- let inner: Event<$t> = Default::default();
- Self::from(inner)
- }
- }
- }
- }
-
- new_with_default!(new_pending, Pending);
- new_with_default!(new_sending, Sending);
- new_with_default!(new_sent, Sent);
- new_with_default!(new_acked, Ack);
- new_with_default!(new_finished_noack, Finished<Sent>);
- new_with_default!(new_finished_acked, Finished<Ack>);
-
- #[allow(unused)]
- #[cfg(test)]
- mod tests {
- use super::*;
- use std::str::FromStr;
-
- #[test]
- fn event_attr_accessors_work() {
- let pending = Pending { recipient: "0.0.0.0".parse().unwrap() };
- let now = Utc::now();
- let id = Uuid::new_v4();
- let pending_event = Event {
- time: now,
- id,
- event: pending,
- };
- let pending_active = Active::Pending(pending_event);
- assert_eq!(pending_active.time(), now);
- assert_eq!(pending_active.id(), id);
- }
-
- #[test]
- fn check_generated_methods_for_pending() {
- let active = Active::new_pending();
- assert!(active.is_pending());
- assert!(active.is_still_unfinished());
- }
-
- #[test]
- fn generated_method_check_for_variant() {
- macro_rules! check_for_variant {
- ($new:ident, $single:ident, $multiple:ident, $not:ident) => {
- let active = Active::$new();
- assert!(active.$single());
- assert!(active.$multiple());
- assert!( ! active.$not());
- }
- }
-
- check_for_variant!(new_pending, is_pending, is_still_unfinished, is_sent);
- check_for_variant!(new_sending, is_sending, is_still_unfinished, is_acked);
- check_for_variant!(new_sent, is_sent, is_still_unfinished, is_finished_noack);
- check_for_variant!(new_acked, is_acked, is_still_unfinished, is_pending);
- check_for_variant!(new_finished_noack, is_finished_noack, is_finished, is_still_unfinished);
- check_for_variant!(new_finished_acked, is_finished_acked, is_finished, is_still_unfinished);
- }
-
- #[test]
- fn active_impls_timestamped() {
- fn tm_plus_42min<T: Timestamped>(x: &T) -> DateTime<Utc> {
- x.time() + chrono::Duration::minutes(42)
- }
-
- let active = Active::new_sent();
- let t2 = tm_plus_42min(&active);
- assert_eq!(active.time() + chrono::Duration::minutes(42), t2);
- }
-
- #[test]
- fn check_that_vis_can_handle_all_three_possibilities_for_visibility() {
- pub struct A {
- x: i32,
- y: DateTime<Utc>,
- z: f32,
- }
-
- macro_rules! a_attr {
- ($pub:vis $method:ident, $t:ty, $attr:ident) => {
- impl A {
- $pub fn $method(&self) -> $t {
- self.$attr
- }
- }
- }
- }
-
- a_attr!(get_x, i32, x);
- a_attr!(pub(crate) get_y, DateTime<Utc>, y);
- a_attr!(pub get_z, f32, z);
-
- let a = A { x: 0i32, y: Utc::now(), z: 1.234f32 };
-
- assert_eq!(a.get_x(), a.x);
- assert_eq!(a.get_y(), a.y);
- assert_eq!(a.get_z(), a.z);
- }
-
- #[test]
- fn usage_of_variant_check_example() {
- let pending = Pending { recipient: Ipv4Addr::new(127, 0, 0, 1) };
- let event = Event { time: Utc::now(), id: Uuid::new_v4(), event: pending };
- let active = Active::from(event);
- assert_eq!(active.is_pending(), true);
- }
- }
|