1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#![doc(html_root_url = "https://acmcarther.github.io/cucumber-rs/")]

#![cfg_attr(nightly, feature(custom_derive, plugin))]
#![cfg_attr(nightly, plugin(serde_macros))]

extern crate regex;
extern crate itertools;
extern crate serde;
extern crate serde_json;

/// Low level location of step functions and matcher logic
pub mod state;

/// External facing interface to other Gherkin implementations
pub mod server;

/// Coordinator logic between [server](server/index.html) and
/// [state](state/index.html)
pub mod runner;

/// Business logic for step registration and invoke argument destructuring
pub mod definitions;

/// External facing interface for events
pub mod event;

/// Helpers for regular expressions
pub mod cucumber_regex;

mod launcher;

pub use launcher::{ruby_command, create_config, CucumberConfig};

pub use runner::{CommandRunner, WorldRunner};
pub use definitions::registration::CucumberRegistrar;
pub use state::{Cucumber, SendableStep};
pub use server::Server;
pub use event::request::InvokeArgument;

/// Destructure a vector of
/// [InvokeArgument](event/request/enum.InvokeArgument.html) into a tuple of
/// values, or a bad [InvokeResponse](event/response/enum.InvokeResponse.html),
/// similar to normal try!
///
/// Will either short circult return an InvokeArgument::Fail, describing either
/// improper arg count
/// or improper arg type, or will yield the tuple of values
///
/// Reminder: Tuple of one value is represented as `(t,): (Type,)`
///
/// # Example
///
/// ```
/// #[macro_use]
/// extern crate cucumber;
///
/// use cucumber::{
///   InvokeArgument,
/// };
///
/// fn main() {
///   fn do_work() {
///     let args = vec![
///       InvokeArgument::from_str("1"),
///       InvokeArgument::from_str("2"),
///       InvokeArgument::None
///     ];
///     let (x, y, z): (u32, u32, bool) = try_destructure!(args);
///
///     assert_eq!(x, 1);
///     assert_eq!(y, 2);
///     assert_eq!(z, false);
///   }
/// }
/// ```
#[macro_export]
macro_rules! try_destructure {
  ($r: ident) => ({
    use $crate::definitions::destructuring::{DestructurableSet, InvokeArgSetError};

    match $r.destructure_set() {
      Ok(e) => e,
      Err(error) => {
        match error {
          InvokeArgSetError::TypeMismatch {arg_idx} => {
            panic!("Argument in position [{}] did not have the correct type or was unparseable", arg_idx)
          },
          InvokeArgSetError::ArgCountMismatch {expected, actual} => {
            panic!("Expected [{}] arguments, but found [{}] in step definition", expected, actual)
          }
        }
      }
    }
  })
}

/// Add a Given step to a
/// [CucumberRegistrar](definitions/registration/trait.CucumberRegistrar.html)
///
/// # Example
/// ```
/// #[macro_use]
/// extern crate cucumber;
///
/// use cucumber::{
///   CucumberRegistrar,
///   Cucumber
/// };
///
/// pub fn main () {
///   let mut cucumber: Cucumber<u32> = Cucumber::new();
///
/// Given!(cucumber, "^I have (\\d+) coins$", |_, world: &mut u32,
/// (coin_count,): (u32,)| {
///     *world = coin_count;
///   });
/// }
/// ```
///
#[macro_export]
macro_rules! Given {
  ($cuke:expr, $regex:expr, $body:expr) => {{
    use $crate::cucumber_regex;
    $cuke.given(file!(), line!(), cucumber_regex::build($regex), Box::new(move |cuke, world, args| {
      ($body)(cuke, world, try_destructure!(args))
    }))
  }}
}

/// Add a When step to a
/// [CucumberRegistrar](definitions/registration/trait.CucumberRegistrar.html)
///
/// # Example
/// ```
/// #[macro_use]
/// extern crate cucumber;
///
/// use cucumber::{
///   CucumberRegistrar,
///   Cucumber
/// };
///
/// pub fn main () {
///   let mut cucumber: Cucumber<u32> = Cucumber::new();
///
/// When!(cucumber, "^I spend (\\d+) coins$", |_, world: &mut u32,
/// (coin_count,): (u32,)| {
///     if *world - coin_count < 0 {
///       panic!("Tried to spend more coins than were owned")
///     } else {
///       *world = *world - coin_count;
///     }
///   });
/// }
/// ```
///
#[macro_export]
macro_rules! When {
  ($cuke:expr, $regex:expr, $body:expr) => {{
    use $crate::cucumber_regex;
    $cuke.when(file!(), line!(), cucumber_regex::build($regex), Box::new(move |cuke, world, args| {
      ($body)(cuke, world, try_destructure!(args))
    }))
  }}
}

/// Add a Then step to a
/// [CucumberRegistrar](definitions/registration/trait.CucumberRegistrar.html)
///
/// # Example
/// ```
/// #[macro_use]
/// extern crate cucumber;
///
/// use cucumber::{
///   CucumberRegistrar,
///   Cucumber
/// };
///
/// pub fn main () {
///   let mut cucumber: Cucumber<u32> = Cucumber::new();
///
/// Then!(cucumber, "^I have (\\d+) coins left$", |_, world: &mut u32,
/// (coin_count,): (u32,)| {
///     assert_eq!(*world, coin_count)
///   });
/// }
/// ```
///
#[macro_export]
macro_rules! Then {
  ($cuke:expr, $regex:expr, $body:expr) => {{
    use $crate::cucumber_regex;
    $cuke.then(file!(), line!(), cucumber_regex::build($regex), Box::new(move |cuke, world, args| {
      ($body)(cuke, world, try_destructure!(args))
    }))
  }}
}