• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zhiburt / expectrl / 12118664807

02 Dec 2024 11:46AM UTC coverage: 56.045% (+0.1%) from 55.91%
12118664807

Pull #73

github

web-flow
Merge 136bbd4ca into a0f4f7816
Pull Request #73: fix: typos in documentation files

1488 of 2655 relevant lines covered (56.05%)

3.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

6.67
/src/check_macros.rs
1
//! This module contains a `check!` macros.
2

3
/// Check macros provides a convient way to check if things are available in a stream of a process.
4
///
5
/// It falls into a corresponding branch when a pattern was matched.
6
/// It runs checks from top to bottom.
7
/// It doesn't wait until any of them be available.
8
/// If you want to wait until any of the input available you must use your own approach for example putting it in a loop.
9
///
10
/// You can specify a default branch which will be called if nothing was matched.
11
///
12
/// The macros levareges [`Expect::check`] function, so its just made for convience.
13
///
14
/// # Example
15
/// ```no_run
16
/// # let mut session = expectrl::spawn("cat").unwrap();
17
/// #
18
/// loop {
19
///     expectrl::check!{
20
///         &mut session,
21
///         world = "\r" => {
22
///             // handle end of line
23
///         },
24
///         _ = "Hello World" => {
25
///             // handle Hello World
26
///         },
27
///         default => {
28
///             // handle no matches
29
///         },
30
///     }
31
///     .unwrap();
32
/// }
33
/// ```
34
///
35
/// [`Expect::check`]: crate::Expect::check
36
#[cfg(not(feature = "async"))]
37
#[macro_export]
38
macro_rules! check {
39
    (@check ($($tokens:tt)*) ($session:expr)) => {
40
        $crate::check!(@case $session, ($($tokens)*), (), ())
41
    };
42
    (@check ($session:expr, $($tokens:tt)*) ()) => {
43
        $crate::check!(@check ($($tokens)*) ($session))
44
    };
45
    (@check ($session:expr, $($tokens:tt)*) ($session2:expr)) => {
46
        compile_error!("Wrong number of session arguments")
47
    };
48
    (@check ($($tokens:tt)*) ()) => {
49
        compile_error!("Please provide a session as a first argument")
50
    };
51
    (@check () ($session:expr)) => {
52
        // there's no reason to run 0 checks so we issue a error.
53
        compile_error!("There's no reason in running check with no arguments. Please supply a check branches")
54
    };
55
    (@case $session:expr, ($var:tt = $exp:expr => $body:tt, $($tail:tt)*), ($($head:tt)*), ($($default:tt)*)) => {
56
        // regular case
57
        //
58
        // note: we keep order correct by putting head at the beggining
59
        $crate::check!(@case $session, ($($tail)*), ($($head)* $var = $exp => $body, ), ($($default)*))
60
    };
61
    (@case $session:expr, ($var:tt = $exp:expr => $body:tt $($tail:tt)*), ($($head:tt)*), ($($default:tt)*)) => {
62
        // allow missing comma
63
        //
64
        // note: we keep order correct by putting head at the beggining
65
        $crate::check!(@case $session, ($($tail)*), ($($head)* $var = $exp => $body, ), ($($default)*))
66
    };
67
    (@case $session:expr, (default => $($tail:tt)*), ($($head:tt)*), ($($default:tt)+)) => {
68
        // A repeated default branch
69
        compile_error!("Only 1 default case is allowed")
70
    };
71
    (@case $session:expr, (default => $body:tt, $($tail:tt)*), ($($head:tt)*), ()) => {
72
        // A default branch
73
        $crate::check!(@case $session, ($($tail)*), ($($head)*), ( { $body; #[allow(unreachable_code)] Ok(()) } ))
74
    };
75
    (@case $session:expr, (default => $body:tt $($tail:tt)*), ($($head:tt)*), ()) => {
76
        // A default branch
77
        // allow missed comma `,`
78
        $crate::check!(@case $session, ($($tail)*), ($($head)*), ( { $body; Ok(()) } ))
×
79
    };
80
    (@case $session:expr, (), ($($head:tt)*), ()) => {
81
        // there's no default branch
82
        // so we make up our own.
83
        $crate::check!(@case $session, (), ($($head)*), ( { Ok(()) } ))
×
84
    };
85
    (@case $session:expr, (), ($($tail:tt)*), ($($default:tt)*)) => {
86
        // last point of @case
87
        // call code generation via @branch
88
        $crate::check!(@branch $session, ($($tail)*), ($($default)*))
89
    };
90
    // We need to use a variable for pattern mathing,
91
    // user may chose to drop var name using a placeholder '_',
92
    // in which case we can't call a method on such identificator.
93
    //
94
    // We could use an approach like was described
95
    //
96
    // ```
97
    // Ok(_____random_var_name_which_supposedly_mustnt_be_used) if !_____random_var_name_which_supposedly_mustnt_be_used.is_empty() =>
98
    // {
99
    //      let $var = _____random_var_name_which_supposedly_mustnt_be_used;
100
    // }
101
    // ```
102
    //
103
    // The question is which solution is more effichient.
104
    // I took the following approach because there's no chance we influence user's land via the variable name we pick.
105
    (@branch $session:expr, ($var:tt = $exp:expr => $body:tt, $($tail:tt)*), ($($default:tt)*)) => {
106
        match $crate::Expect::check($session, $exp) {
×
107
            result if result.as_ref().map(|found| !found.is_empty()).unwrap_or(false) => {
10✔
108
                let $var = result.unwrap();
109
                $body;
110
                #[allow(unreachable_code)]
111
                Ok(())
×
112
            }
×
113
            Ok(_) => {
114
                $crate::check!(@branch $session, ($($tail)*), ($($default)*))
115
            }
×
116
            Err(err) => Err(err),
×
117
        }
118
    };
119
    (@branch $session:expr, (), ($default:tt)) => {
120
        // A standart default branch
121
        $default
122
    };
123
    (@branch $session:expr, ($($tail:tt)*), ($($default:tt)*)) => {
124
        compile_error!(
125
            concat!(
126
                "No supported syntax tail=(",
127
                stringify!($($tail,)*),
128
                ") ",
129
                "default=(",
130
                stringify!($($default,)*),
131
                ") ",
132
        ))
133
    };
134
    // Entry point
135
    ($($tokens:tt)*) => {
136
        {
137
            let result: Result::<(), $crate::Error> = $crate::check!(@check ($($tokens)*) ());
×
138
            result
×
139
        }
×
140
    };
141
}
142

143
/// See sync version.
144
///
145
/// Async version of macros use the same approach as sync.
146
/// It doesn't use any future features.
147
/// So it may be better better you to use you own approach how to check which one done first.
148
/// For example you can use `futures_lite::future::race`.
149
///
150
// async version completely the same as sync version expect 2 words '.await' and 'async'
151
// meaning its a COPY && PASTE
152
#[cfg(feature = "async")]
153
#[macro_export]
154
macro_rules! check {
155
    (@check ($($tokens:tt)*) ($session:expr)) => {
156
        $crate::check!(@case $session, ($($tokens)*), (), ())
157
    };
158
    (@check ($session:expr, $($tokens:tt)*) ()) => {
159
        $crate::check!(@check ($($tokens)*) ($session))
160
    };
161
    (@check ($session:expr, $($tokens:tt)*) ($session2:expr)) => {
162
        compile_error!("Wrong number of session arguments")
163
    };
164
    (@check ($($tokens:tt)*) ()) => {
165
        compile_error!("Please provide a session as a first argument")
166
    };
167
    (@check () ($session:expr)) => {
168
        // there's no reason to run 0 checks so we issue a error.
169
        compile_error!("There's no reason in running check with no arguments. Please supply a check branches")
170
    };
171
    (@case $session:expr, ($var:tt = $exp:expr => $body:tt, $($tail:tt)*), ($($head:tt)*), ($($default:tt)*)) => {
172
        // regular case
173
        //
174
        // note: we keep order correct by putting head at the beggining
175
        $crate::check!(@case $session, ($($tail)*), ($($head)* $var = $exp => $body, ), ($($default)*))
176
    };
177
    (@case $session:expr, ($var:tt = $exp:expr => $body:tt $($tail:tt)*), ($($head:tt)*), ($($default:tt)*)) => {
178
        // allow missing comma
179
        //
180
        // note: we keep order correct by putting head at the beggining
181
        $crate::check!(@case $session, ($($tail)*), ($($head)* $var = $exp => $body, ), ($($default)*))
182
    };
183
    (@case $session:expr, (default => $($tail:tt)*), ($($head:tt)*), ($($default:tt)+)) => {
184
        // A repeated default branch
185
        compile_error!("Only 1 default case is allowed")
186
    };
187
    (@case $session:expr, (default => $body:tt, $($tail:tt)*), ($($head:tt)*), ()) => {
188
        // A default branch
189
        $crate::check!(@case $session, ($($tail)*), ($($head)*), ( { $body; #[allow(unreachable_code)] Ok(()) } ))
190
    };
191
    (@case $session:expr, (default => $body:tt $($tail:tt)*), ($($head:tt)*), ()) => {
192
        // A default branch
193
        // allow missed comma `,`
194
        $crate::check!(@case $session, ($($tail)*), ($($head)*), ( { $body; Ok(()) } ))
195
    };
196
    (@case $session:expr, (), ($($head:tt)*), ()) => {
197
        // there's no default branch
198
        // so we make up our own.
199
        $crate::check!(@case $session, (), ($($head)*), ( { Ok(()) } ))
200
    };
201
    (@case $session:expr, (), ($($tail:tt)*), ($($default:tt)*)) => {
202
        // last point of @case
203
        // call code generation via @branch
204
        $crate::check!(@branch $session, ($($tail)*), ($($default)*))
205
    };
206
    // We need to use a variable for pattern mathing,
207
    // user may chose to drop var name using a placeholder '_',
208
    // in which case we can't call a method on such identificator.
209
    //
210
    // We could use an approach like was described
211
    //
212
    // ```
213
    // Ok(_____random_var_name_which_supposedly_mustnt_be_used) if !_____random_var_name_which_supposedly_mustnt_be_used.is_empty() =>
214
    // {
215
    //      let $var = _____random_var_name_which_supposedly_mustnt_be_used;
216
    // }
217
    // ```
218
    //
219
    // The question is which solution is more effichient.
220
    // I took the following approach because there's no chance we influence user's land via the variable name we pick.
221
    (@branch $session:expr, ($var:tt = $exp:expr => $body:tt, $($tail:tt)*), ($($default:tt)*)) => {
222
        match $crate::AsyncExpect::check($session, $exp).await {
223
            Ok(found) => {
224
                if !found.is_empty() {
225
                    let $var = found;
226
                    $body;
227
                    #[allow(unreachable_code)]
228
                    return Ok(())
229
                }
230

231
                $crate::check!(@branch $session, ($($tail)*), ($($default)*))
232
            }
233
            Err(err) => Err(err),
234
        }
235
    };
236
    (@branch $session:expr, (), ($default:tt)) => {
237
        // A standart default branch
238
        $default
239
    };
240
    (@branch $session:expr, ($($tail:tt)*), ($($default:tt)*)) => {
241
        compile_error!(
242
            concat!(
243
                "No supported syntax tail=(",
244
                stringify!($($tail,)*),
245
                ") ",
246
                "default=(",
247
                stringify!($($default,)*),
248
                ") ",
249
        ))
250
    };
251
    // Entry point
252
    ($($tokens:tt)*) => {
253
        async {
254
            let value: Result::<(), $crate::Error> = $crate::check!(@check ($($tokens)*) ());
255
            value
256
        }
257
    };
258
}
259

260
#[cfg(test)]
261
mod tests {
262
    #[allow(unused_variables)]
263
    #[allow(unused_must_use)]
264
    #[test]
265
    #[ignore = "Testing in compile time"]
266
    fn test_check() {
×
267
        let mut session = crate::spawn("").unwrap();
×
268
        crate::check! {
269
            &mut session,
270
            as11d = "zxc" => {},
271
        };
272
        crate::check! {
273
            &mut session,
274
            as11d = "zxc" => {},
275
            asbb = "zxc123" => {},
276
        };
277
        crate::check! { session, };
278

279
        // crate::check! {
280
        //     as11d = "zxc" => {},
281
        //     asbb = "zxc123" => {},
282
        // };
283

284
        // panic on unused session
285
        // crate::check! { session };
286

287
        // trailing commas
288
        crate::check! {
289
            &mut session,
290
            as11d = "zxc" => {}
291
            asbb = "zxc123" => {}
292
        };
293
        crate::check! {
294
            &mut session,
295
            as11d = "zxc" => {}
296
            default => {}
297
        };
298

299
        #[cfg(not(feature = "async"))]
300
        {
301
            crate::check! {
302
                &mut session,
303
                as11d = "zxc" => {},
304
            }
305
            .unwrap();
306
            (crate::check! {
307
                &mut session,
308
                as11d = "zxc" => {},
309
            })
310
            .unwrap();
311
            (crate::check! {
312
                &mut session,
313
                as11d = "zxc" => {},
314
            })
315
            .unwrap();
316
            (crate::check! {
317
                &mut session,
318
                as11d = "zxc" => {
319
                    println!("asd")
×
320
                },
321
            })
322
            .unwrap();
323
        }
324

325
        #[cfg(feature = "async")]
326
        async {
327
            crate::check! {
328
                &mut session,
329
                as11d = "zxc" => {},
330
            }
331
            .await
332
            .unwrap();
333
            (crate::check! {
334
                &mut session,
335
                as11d = "zxc" => {},
336
            })
337
            .await
338
            .unwrap();
339
            (crate::check! {
340
                &mut session,
341
                as11d = "zxc" => {},
342
            })
343
            .await
344
            .unwrap();
345
            (crate::check! {
346
                &mut session,
347
                as11d = "zxc" => {
348
                    println!("asd")
349
                },
350
            })
351
            .await
352
            .unwrap();
353
        };
354
    }
×
355
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc