diff --git a/src/case_study.rs b/src/case_study.rs index d67bd6d..469e198 100644 --- a/src/case_study.rs +++ b/src/case_study.rs @@ -12,22 +12,26 @@ pub struct Pending{ pub recipient: Ipv4Addr, } +#[derive(Default)] pub struct Sending { pub payload: Vec, pub bytes_sent: usize, - pub prev: Event, + 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, } @@ -79,6 +83,7 @@ impl Elapsed<(), N> for T } } +/* impl Timestamped for Active { fn time(&self) -> DateTime { use Active::*; @@ -92,9 +97,10 @@ impl Timestamped for Active { } } } +*/ macro_rules! event_attr { - ($method:ident, $t:ident, $attr:ident) => { + ($method:ident, $t:ty, $attr:ident) => { fn $method(&self) -> $t { use Active::*; 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, 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 { @@ -135,6 +164,90 @@ 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)] @@ -156,5 +269,78 @@ mod tests { 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); + } +} diff --git a/src/lib.rs b/src/lib.rs index e3db6e5..6f142b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ -#![allow(unused)] -//#![recursion_limit = "1000000000"] - +//! code examples from "Productive Rust: Implementing Traits with Macros" +//! //! # Examples //! //! ```compile_fail @@ -15,7 +14,8 @@ //! ah_ah_ah_didnt_say_the_magic_word!(x); //! ``` //! -//! Generic `min` won't compile: +//! generic `min` won't compile: +//! //! //! ```compile_fail //! fn min(a: T, b: T) -> T { @@ -23,17 +23,26 @@ //! } //! ``` //! +//! you can't add an i32 with a HashMap +//! +//! //! ```compile_fail //! macro_rules! add_one { ($x:ident) => { $x + 1i32 } } //! let b: std::collections::HashMap = Default::default(); //! add_one!(b); //! ``` //! +//! you can't match a numeric literal against an $ident +//! +//! //! ```compile_fail //! macro_rules! add_one { ($x:ident) => { $x + 1i32 } } //! add_one(42i32); //! ``` //! +//! you can't generate a dynamic name with an $expr +//! +//! //! ```compile_fail //! macro_rules! make_adder_fn { //! ($n:expr) => { @@ -45,6 +54,44 @@ //! 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; @@ -86,24 +133,24 @@ mod tests { } #[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) => { - if $a > $b { $a } else { $b } + if $a < $b { $a } else { $b } } } let a: usize = 1; let b: usize = 2; - assert_eq!(max!(a, b), 2); + assert_eq!(min!(a, b), 1); let c = f64::INFINITY; let d = f64::NEG_INFINITY; - assert_eq!(max!(c, d), f64::INFINITY); + assert_eq!(min!(c, d), f64::NEG_INFINITY); } #[test] - fn min_with_partial_ord_will_compile() { + fn min_with_partial_ord_will_compile_thats_all_it_needs() { fn min_that_compiles(a: T, b: T) -> T { if a < b { a } else { b } } @@ -125,7 +172,7 @@ mod tests { } #[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 { ((- $($moves:tt)*) -> (+ $($count:tt)*)) => { abacus!(($($moves)*) -> ($($count)*)) @@ -162,7 +209,7 @@ mod tests { } #[test] - fn how_about_this_example_from_little_book() { + fn little_book_example_2_does_it_compile() { macro_rules! parse_unitary_variants { (@as_expr $e:expr) => {$e}; (@as_item $($i:item)+) => {$($i)+}; @@ -250,7 +297,7 @@ mod tests { } #[test] - fn tt_muncher() { + fn tt_muncher_does_it_compile() { macro_rules! mixed_rules { () => {}; (trace $name:ident; $($tail:tt)*) => { @@ -302,6 +349,7 @@ mod tests { i_need_display_not_debug(p); } + // did not end up using this #[test] fn example_for_why_you_should_use_fully_qualified_names() { pub struct A; @@ -360,7 +408,6 @@ mod tests { } assert_eq!(the_upside_down(&a), -42); - } #[test] @@ -378,4 +425,60 @@ mod tests { 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(); + } + }