use std::net::Ipv4Addr; use chrono::{DateTime, Utc}; use uuid::Uuid; pub struct Event { pub time: DateTime, pub id: Uuid, pub event: T, } pub struct Pending{ pub recipient: Ipv4Addr, } #[derive(Default)] pub struct Sending { pub payload: Vec, pub bytes_sent: usize, pub prev: Event, } #[derive(Default)] pub struct Sent { pub ack_req: bool, pub prev: Event, } #[derive(Default)] pub struct Ack { pub data: Vec, pub prev: Event, } #[derive(Default)] pub struct Finished { pub prev: Event, } pub enum Active { Pending(Event), Sending(Event), Sent(Event), Acked(Event), FinishedNoAck(Event>), FinishedAcked(Event>), } pub trait Timestamped { fn time(&self) -> DateTime; } pub trait Chronological { type Prev; type Next; fn prev(&self) -> Self::Prev; fn next(&self) -> Self::Next; } pub trait Elapsed: Chronological { fn elapsed(&self) -> chrono::Duration; } impl Timestamped for Event { fn time(&self) -> DateTime { self.time } } impl Elapsed for T where T: Chronological, P: Timestamped, N: Timestamped { fn elapsed(&self) -> chrono::Duration { self.next().time().signed_duration_since(self.prev().time()) } } impl Elapsed<(), N> for T where T: Chronological { fn elapsed(&self) -> chrono::Duration { chrono::Duration::seconds(0) } } /* impl Timestamped for Active { fn time(&self) -> DateTime { 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, 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> for Active { fn from(pending: Event) -> Active { Active::Pending(pending) } } macro_rules! from_event { ($t:ty, $variant:ident) => { impl From> 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, FinishedNoAck); from_event!(Finished, 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 Default for Event 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); new_with_default!(new_finished_acked, Finished); #[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(x: &T) -> DateTime { 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, 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, 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); } }