@@ -12,22 +12,26 @@ pub struct Pending{ | |||||
pub recipient: Ipv4Addr, | pub recipient: Ipv4Addr, | ||||
} | } | ||||
#[derive(Default)] | |||||
pub struct Sending { | pub struct Sending { | ||||
pub payload: Vec<u8>, | pub payload: Vec<u8>, | ||||
pub bytes_sent: usize, | pub bytes_sent: usize, | ||||
pub prev: Event<Ipv4Addr>, | |||||
pub prev: Event<Pending>, | |||||
} | } | ||||
#[derive(Default)] | |||||
pub struct Sent { | pub struct Sent { | ||||
pub ack_req: bool, | pub ack_req: bool, | ||||
pub prev: Event<Sending>, | pub prev: Event<Sending>, | ||||
} | } | ||||
#[derive(Default)] | |||||
pub struct Ack { | pub struct Ack { | ||||
pub data: Vec<u8>, | pub data: Vec<u8>, | ||||
pub prev: Event<Sent>, | pub prev: Event<Sent>, | ||||
} | } | ||||
#[derive(Default)] | |||||
pub struct Finished<T> { | pub struct Finished<T> { | ||||
pub prev: Event<T>, | pub prev: Event<T>, | ||||
} | } | ||||
@@ -79,6 +83,7 @@ impl<N, T> Elapsed<(), N> for T | |||||
} | } | ||||
} | } | ||||
/* | |||||
impl Timestamped for Active { | impl Timestamped for Active { | ||||
fn time(&self) -> DateTime<Utc> { | fn time(&self) -> DateTime<Utc> { | ||||
use Active::*; | use Active::*; | ||||
@@ -92,9 +97,10 @@ impl Timestamped for Active { | |||||
} | } | ||||
} | } | ||||
} | } | ||||
*/ | |||||
macro_rules! event_attr { | macro_rules! event_attr { | ||||
($method:ident, $t:ident, $attr:ident) => { | |||||
($method:ident, $t:ty, $attr:ident) => { | |||||
fn $method(&self) -> $t { | fn $method(&self) -> $t { | ||||
use Active::*; | use Active::*; | ||||
match self { | match self { | ||||
@@ -109,10 +115,33 @@ macro_rules! event_attr { | |||||
} | } | ||||
} | } | ||||
impl Active { | |||||
event_attr!(id, Uuid, id); | |||||
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 { | impl From<Event<Pending>> for Active { | ||||
fn from(pending: Event<Pending>) -> Active { | fn from(pending: Event<Pending>) -> Active { | ||||
@@ -135,6 +164,90 @@ from_event!(Sending, Sending); | |||||
from_event!(Sent, Sent); | from_event!(Sent, Sent); | ||||
from_event!(Ack, Acked); | from_event!(Ack, Acked); | ||||
from_event!(Finished<Sent>, FinishedNoAck); | 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)] | #[allow(unused)] | ||||
#[cfg(test)] | #[cfg(test)] | ||||
@@ -156,5 +269,78 @@ mod tests { | |||||
assert_eq!(pending_active.time(), now); | assert_eq!(pending_active.time(), now); | ||||
assert_eq!(pending_active.id(), id); | 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); | |||||
} | |||||
} |
@@ -1,6 +1,5 @@ | |||||
#![allow(unused)] | |||||
//#![recursion_limit = "1000000000"] | |||||
//! code examples from "Productive Rust: Implementing Traits with Macros" | |||||
//! | |||||
//! # Examples | //! # Examples | ||||
//! | //! | ||||
//! ```compile_fail | //! ```compile_fail | ||||
@@ -15,7 +14,8 @@ | |||||
//! ah_ah_ah_didnt_say_the_magic_word!(x); | //! ah_ah_ah_didnt_say_the_magic_word!(x); | ||||
//! ``` | //! ``` | ||||
//! | //! | ||||
//! Generic `min` won't compile: | |||||
//! generic `min` won't compile: | |||||
//! | |||||
//! | //! | ||||
//! ```compile_fail | //! ```compile_fail | ||||
//! fn min<T>(a: T, b: T) -> T { | //! fn min<T>(a: T, b: T) -> T { | ||||
@@ -23,17 +23,26 @@ | |||||
//! } | //! } | ||||
//! ``` | //! ``` | ||||
//! | //! | ||||
//! you can't add an i32 with a HashMap | |||||
//! | |||||
//! | |||||
//! ```compile_fail | //! ```compile_fail | ||||
//! macro_rules! add_one { ($x:ident) => { $x + 1i32 } } | //! macro_rules! add_one { ($x:ident) => { $x + 1i32 } } | ||||
//! let b: std::collections::HashMap<String, u32> = Default::default(); | //! let b: std::collections::HashMap<String, u32> = Default::default(); | ||||
//! add_one!(b); | //! add_one!(b); | ||||
//! ``` | //! ``` | ||||
//! | //! | ||||
//! you can't match a numeric literal against an $ident | |||||
//! | |||||
//! | |||||
//! ```compile_fail | //! ```compile_fail | ||||
//! macro_rules! add_one { ($x:ident) => { $x + 1i32 } } | //! macro_rules! add_one { ($x:ident) => { $x + 1i32 } } | ||||
//! add_one(42i32); | //! add_one(42i32); | ||||
//! ``` | //! ``` | ||||
//! | //! | ||||
//! you can't generate a dynamic name with an $expr | |||||
//! | |||||
//! | |||||
//! ```compile_fail | //! ```compile_fail | ||||
//! macro_rules! make_adder_fn { | //! macro_rules! make_adder_fn { | ||||
//! ($n:expr) => { | //! ($n:expr) => { | ||||
@@ -45,6 +54,44 @@ | |||||
//! make_adder_fn!(42); | //! make_adder_fn!(42); | ||||
//! ``` | //! ``` | ||||
//! | //! | ||||
//! you can't match a $ty to create a struct | |||||
//! | |||||
//! | |||||
//! ```compile_fail | |||||
//! // ty can't be used to create a type, only to refer to one | |||||
//! macro_rules! make_struct { | |||||
//! ($t:ty, $x:ty, $y:ty) => { | |||||
//! struct $t { | |||||
//! pub x: f64, | |||||
//! pub y: f64, | |||||
//! } | |||||
//! } | |||||
//! } | |||||
//! make_struct!(Point); | |||||
//! ``` | |||||
//! | |||||
//! code order example - with d present, can't compile | |||||
//! | |||||
//! ```compile_fail | |||||
//! fn a() -> i32 { b() } // this is ok, even though `b` is below | |||||
//! | |||||
//! const B: i32 = b(); // also ok, even though `b` is below | |||||
//! | |||||
//! const fn b() -> i32 { 42 } | |||||
//! | |||||
//! d!(); // not ok | |||||
//! | |||||
//! macro_rules! d { | |||||
//! () => { | |||||
//! fn spooky_numbers() -> [usize; 6] { | |||||
//! [4, 8, 15, 16, 23, 42] | |||||
//! } | |||||
//! } | |||||
//! } | |||||
//! ``` | |||||
#![allow(unused)] | |||||
pub mod case_study; | pub mod case_study; | ||||
@@ -86,24 +133,24 @@ mod tests { | |||||
} | } | ||||
#[test] | #[test] | ||||
fn generic_max_macro_compiles_and_works() { | |||||
macro_rules! max { | |||||
fn generic_min_macro_compiles_and_works() { | |||||
macro_rules! min { | |||||
($a:expr, $b:expr) => { | ($a:expr, $b:expr) => { | ||||
if $a > $b { $a } else { $b } | |||||
if $a < $b { $a } else { $b } | |||||
} | } | ||||
} | } | ||||
let a: usize = 1; | let a: usize = 1; | ||||
let b: usize = 2; | let b: usize = 2; | ||||
assert_eq!(max!(a, b), 2); | |||||
assert_eq!(min!(a, b), 1); | |||||
let c = f64::INFINITY; | let c = f64::INFINITY; | ||||
let d = f64::NEG_INFINITY; | let d = f64::NEG_INFINITY; | ||||
assert_eq!(max!(c, d), f64::INFINITY); | |||||
assert_eq!(min!(c, d), f64::NEG_INFINITY); | |||||
} | } | ||||
#[test] | #[test] | ||||
fn min_with_partial_ord_will_compile() { | |||||
fn min_with_partial_ord_will_compile_thats_all_it_needs() { | |||||
fn min_that_compiles<T: PartialOrd>(a: T, b: T) -> T { | fn min_that_compiles<T: PartialOrd>(a: T, b: T) -> T { | ||||
if a < b { a } else { b } | if a < b { a } else { b } | ||||
} | } | ||||
@@ -125,7 +172,7 @@ mod tests { | |||||
} | } | ||||
#[test] | #[test] | ||||
fn im_curious_if_a_randomly_chosen_example_from_little_quote_unquote_black_book_compiles() { | |||||
fn little_book_example_1_does_it_compile() { | |||||
macro_rules! abacus { | macro_rules! abacus { | ||||
((- $($moves:tt)*) -> (+ $($count:tt)*)) => { | ((- $($moves:tt)*) -> (+ $($count:tt)*)) => { | ||||
abacus!(($($moves)*) -> ($($count)*)) | abacus!(($($moves)*) -> ($($count)*)) | ||||
@@ -162,7 +209,7 @@ mod tests { | |||||
} | } | ||||
#[test] | #[test] | ||||
fn how_about_this_example_from_little_book() { | |||||
fn little_book_example_2_does_it_compile() { | |||||
macro_rules! parse_unitary_variants { | macro_rules! parse_unitary_variants { | ||||
(@as_expr $e:expr) => {$e}; | (@as_expr $e:expr) => {$e}; | ||||
(@as_item $($i:item)+) => {$($i)+}; | (@as_item $($i:item)+) => {$($i)+}; | ||||
@@ -250,7 +297,7 @@ mod tests { | |||||
} | } | ||||
#[test] | #[test] | ||||
fn tt_muncher() { | |||||
fn tt_muncher_does_it_compile() { | |||||
macro_rules! mixed_rules { | macro_rules! mixed_rules { | ||||
() => {}; | () => {}; | ||||
(trace $name:ident; $($tail:tt)*) => { | (trace $name:ident; $($tail:tt)*) => { | ||||
@@ -302,6 +349,7 @@ mod tests { | |||||
i_need_display_not_debug(p); | i_need_display_not_debug(p); | ||||
} | } | ||||
// did not end up using this | |||||
#[test] | #[test] | ||||
fn example_for_why_you_should_use_fully_qualified_names() { | fn example_for_why_you_should_use_fully_qualified_names() { | ||||
pub struct A; | pub struct A; | ||||
@@ -360,7 +408,6 @@ mod tests { | |||||
} | } | ||||
assert_eq!(the_upside_down(&a), -42); | assert_eq!(the_upside_down(&a), -42); | ||||
} | } | ||||
#[test] | #[test] | ||||
@@ -378,4 +425,60 @@ mod tests { | |||||
assert_eq!(add_42(42), 84); | assert_eq!(add_42(42), 84); | ||||
} | } | ||||
#[test] | |||||
fn ident_can_be_used_to_create_a_struct() { | |||||
macro_rules! make_struct { | |||||
($t:ident) => { | |||||
struct $t { | |||||
pub x: f64, | |||||
pub y: f64, | |||||
} | |||||
} | |||||
} | |||||
make_struct!(Point); | |||||
} | |||||
#[test] | |||||
fn std_u32_exmple_works_with_macro_that_has_same_sig_as_uint_impl() { | |||||
macro_rules! uint_impl_lookalike { | |||||
($SelfT:ty, $ActualT:ty, $BITS:expr, $MaxV:expr, $Feature:expr, $EndFeature:expr, | |||||
$rot:expr, $rot_op:expr, $rot_result:expr, $swap_op:expr, $swapped:expr, | |||||
$reversed:expr, $le_bytes:expr, $be_bytes:expr, | |||||
$to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr) => { | |||||
assert!(true); | |||||
} | |||||
} | |||||
uint_impl_lookalike! { u32, u32, 32, 4294967295, "", "", 8, "0x10000b3", "0xb301", "0x12345678", | |||||
"0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", "", "" } | |||||
} | |||||
#[test] | |||||
fn code_order_can_matter_everything_compiles_except_d() { | |||||
fn a() -> i32 { b() } // this is ok, even though `b` is below | |||||
const B: i32 = b(); // also ok, even though `b` is below | |||||
const fn b() -> i32 { 42 } | |||||
//d!(); // not ok | |||||
macro_rules! d { | |||||
() => { | |||||
fn spooky_numbers() -> [usize; 6] { | |||||
[4, 8, 15, 16, 23, 42] | |||||
} | |||||
} | |||||
} | |||||
} | |||||
#[test] | |||||
fn optionally_public_event_attr_can_be_used_from_here() { | |||||
let active = crate::case_study::Active::default(); | |||||
let id = active.id(); | |||||
} | |||||
} | } |