Browse Source

phew!

master
Jonathan Strong 4 years ago
parent
commit
0dd97740a0
2 changed files with 308 additions and 19 deletions
  1. +191
    -5
      src/case_study.rs
  2. +117
    -14
      src/lib.rs

+ 191
- 5
src/case_study.rs View File

@@ -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);
}
}

+ 117
- 14
src/lib.rs View File

@@ -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();
}

} }

Loading…
Cancel
Save