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
/// Pass the provided tokens into each expression, one after the other.
/// Return the first not-`None` result.
///
/// # Examples
///
/// A basic example:
///
/// ```
/// use yap::{ Tokens, IntoTokens };
///
/// let mut tokens = "hello world".into_tokens();
///
/// // The macro expects a mutable reference to your tokens:
/// let ts = &mut tokens;
/// let res = yap::one_of!(ts;
/// ts.tokens("bye".chars()).then(|| 1),
/// ts.tokens("hi".chars()).then(|| 2),
/// ts.tokens("hello".chars()).then(|| 3),
/// ts.tokens("world".chars()).then(|| 4),
/// );
///
/// assert_eq!(res, Some(3));
/// assert_eq!(tokens.remaining(), " world");
/// ```
///
/// You can declare an alias from some expression that's passed in, too.
/// Handy for abbreviating, or in this case, adding the required mut
/// reference:
///
/// ```
/// use yap::{ Tokens, IntoTokens };
///
/// let mut tokens = "hello world".into_tokens();
///
/// let res = yap::one_of!(ts from &mut tokens;
/// ts.tokens("bye".chars()).then(|| 1),
/// ts.tokens("hi".chars()).then(|| 2),
/// ts.tokens("hello".chars()).then(|| 3),
/// ts.tokens("world".chars()).then(|| 4),
/// );
///
/// assert_eq!(res, Some(3));
/// assert_eq!(tokens.remaining(), " world");
/// ```
///
/// If an expression returns `None`, no tokens will be consumed:
///
/// ```
/// use yap::{ Tokens, IntoTokens };
///
/// let mut tokens = "hello world".into_tokens();
///
/// let res: Option<()> = yap::one_of!(ts from &mut tokens;
/// // This explicit iteration will be rewound if None is returned:
/// { ts.next(); ts.next(); None },
/// );
///
/// assert_eq!(tokens.remaining(), "hello world");
/// # assert_eq!(res, None);
/// ```
#[macro_export]
macro_rules! one_of {
($tokens:ident; $( $e:expr ),+ $(,)?) => {{
#[allow(clippy::all)]
loop {
$(
let checkpoint = $tokens.location();
{
let $tokens = &mut *$tokens;
if let Some(res) = $e {
break Some(res);
}
}
$tokens.set_location(checkpoint);
)+
break None;
}
}};
($alias:ident from $tokens:expr; $( $e:expr ),+ $(,)?) => {{
#[allow(clippy::all)]
loop {
$(
let checkpoint = $tokens.location();
{
let $alias = &mut *$tokens;
if let Some(res) = $e {
break Some(res);
}
}
$tokens.set_location(checkpoint);
)+
break None;
}
}};
}
#[cfg(test)]
mod test {
use crate::{ IntoTokens, Tokens };
#[test]
fn should_produce_no_clippy_warnings() {
let mut ts = "abc".into_tokens();
fn produces_result(ts: &mut impl Tokens<Item=char>) -> Result<char, ()> {
if ts.token('a') {
Ok('a')
} else {
Err(())
}
}
one_of!(ts from &mut ts;
produces_result(ts).ok(),
);
}
}