cur 0.5.0

The tool that will hunt for your regular expression.
Documentation
use {core::iter::FromIterator, cur::*, test_log::test};

macro_rules! assert_catch {
    ($cur:ident, $id:expr => ($begin:expr, $end:expr, $s:expr)) => {
        if let Some(catch) = $cur.next() {
            if let Some(find) = catch.get($id) {
                assert_eq!(
                    find.begin(),
                    $begin,
                    "expected find begin {}, got {}",
                    $begin,
                    find.begin()
                );
                assert_eq!(
                    find.end(),
                    $end,
                    "expected find end {}, got {}",
                    $end,
                    find.end()
                );
                assert_eq!(
                    find.as_str(),
                    $s,
                    "expected find '{}', got '{:?}'",
                    $s,
                    find.as_str()
                );
            } else {
                panic!("Expected mark of `{}` was not found", $id);
            }
        } else {
            panic!("Expected catch was not found");
        }
    };
}

fn surround_odor(odor: Odor) -> Odor {
    Odor::from_iter(vec![
        Scent::Repetition(Odor::from_iter(vec![Scent::Range('\u{0}', '\u{10ffff}')])),
        Scent::Marked(odor),
        Scent::Repetition(Odor::from_iter(vec![Scent::Range('\u{0}', '\u{10ffff}')])),
    ])
}

#[test]
fn empty() {
    let odor = surround_odor(Odor::default().mark("id").unwrap());
    let mut cur = Cur::new(&odor);

    cur.set_search("");
    assert_catch!(cur, "id" => (0, 0, ""));
    assert!(cur.next().is_none());

    cur.set_search("a");
    assert_catch!(cur, "id" => (0, 0, ""));
    assert_catch!(cur, "id" => (1, 1, ""));
    assert!(cur.next().is_none());
}

#[test]
fn char() {
    let odor = surround_odor(Odor::from_iter(vec![Scent::Char('a')]).mark("id").unwrap());
    let mut cur = Cur::new(&odor);

    cur.set_search("a");
    assert_catch!(cur, "id" => (0, 1, "a"));
    assert!(cur.next().is_none());

    cur.set_search("ab");
    assert_catch!(cur, "id" => (0, 1, "a"));
    assert!(cur.next().is_none());

    cur.set_search("ba");
    assert_catch!(cur, "id" => (1, 2, "a"));
    assert!(cur.next().is_none());

    cur.set_search("bbbab");
    assert_catch!(cur, "id" => (3, 4, "a"));
    assert!(cur.next().is_none());

    cur.set_search("");
    assert!(cur.next().is_none());

    cur.set_search("b");
    assert!(cur.next().is_none());
}

#[test]
fn range() {
    let odor = surround_odor(
        Odor::from_iter(vec![Scent::Range('b', 'd')])
            .mark("id")
            .unwrap(),
    );
    let mut cur = Cur::new(&odor);

    cur.set_search("b");
    assert_catch!(cur, "id" => (0, 1, "b"));
    assert!(cur.next().is_none());

    cur.set_search("cc");
    assert_catch!(cur, "id" => (0, 1, "c"));
    assert_catch!(cur, "id" => (1, 2, "c"));
    assert!(cur.next().is_none());

    cur.set_search("de");
    assert_catch!(cur, "id" => (0, 1, "d"));
    assert!(cur.next().is_none());

    cur.set_search("ad");
    assert_catch!(cur, "id" => (1, 2, "d"));
    assert!(cur.next().is_none());

    cur.set_search("");
    assert!(cur.next().is_none());

    cur.set_search("a");
    assert!(cur.next().is_none());

    cur.set_search("e");
    assert!(cur.next().is_none());
}

#[test]
fn union_of_chars() {
    let odor = surround_odor(
        Odor::from_iter(vec![Scent::Union(MultipleOdors::new(
            Odor::from_iter(vec![Scent::Char('a')]),
            Odor::from_iter(vec![Scent::Char('b')]),
            vec![Odor::from_iter(vec![Scent::Char('c')])],
        ))])
        .mark("id")
        .unwrap(),
    );
    let mut cur = Cur::new(&odor);

    cur.set_search("a");
    assert_catch!(cur, "id" => (0, 1, "a"));
    assert!(cur.next().is_none());

    cur.set_search("bb");
    assert_catch!(cur, "id" => (0, 1, "b"));
    assert_catch!(cur, "id" => (1, 2, "b"));
    assert!(cur.next().is_none());

    cur.set_search("cd");
    assert_catch!(cur, "id" => (0, 1, "c"));
    assert!(cur.next().is_none());

    cur.set_search("ea");
    assert_catch!(cur, "id" => (1, 2, "a"));
    assert!(cur.next().is_none());

    cur.set_search("");
    assert!(cur.next().is_none());

    cur.set_search("d");
    assert!(cur.next().is_none());
}

#[test]
fn sequence_of_chars() {
    let odor = surround_odor(
        Odor::from_iter(vec![Scent::Char('a'), Scent::Char('b'), Scent::Char('c')])
            .mark("id")
            .unwrap(),
    );
    let mut cur = Cur::new(&odor);

    cur.set_search("abc");
    assert_catch!(cur, "id" => (0, 3, "abc"));
    assert!(cur.next().is_none());

    cur.set_search("abcd");
    assert_catch!(cur, "id" => (0, 3, "abc"));
    assert!(cur.next().is_none());

    cur.set_search("dabc");
    assert_catch!(cur, "id" => (1, 4, "abc"));
    assert!(cur.next().is_none());

    cur.set_search("");
    assert!(cur.next().is_none());

    cur.set_search("a");
    assert!(cur.next().is_none());

    cur.set_search("ab");
    assert!(cur.next().is_none());

    cur.set_search("aaa");
    assert!(cur.next().is_none());
}

#[test]
fn union_sequences() {
    let odor = surround_odor(
        Odor::from_iter(vec![Scent::Union(MultipleOdors::new(
            Odor::from_iter(vec![Scent::Char('a'), Scent::Char('b'), Scent::Char('c')]),
            Odor::from_iter(vec![Scent::Char('d'), Scent::Char('e')]),
            vec![Odor::from_iter(vec![Scent::Char('f')])],
        ))])
        .mark("id")
        .unwrap(),
    );
    let mut cur = Cur::new(&odor);

    cur.set_search("abc");
    assert_catch!(cur, "id" => (0, 3, "abc"));
    assert!(cur.next().is_none());

    cur.set_search("de");
    assert_catch!(cur, "id" => (0, 2, "de"));
    assert!(cur.next().is_none());

    cur.set_search("f");
    assert_catch!(cur, "id" => (0, 1, "f"));
    assert!(cur.next().is_none());

    cur.set_search("fd");
    assert_catch!(cur, "id" => (0, 1, "f"));
    assert!(cur.next().is_none());

    cur.set_search("df");
    assert_catch!(cur, "id" => (1, 2, "f"));
    assert!(cur.next().is_none());

    cur.set_search("");
    assert!(cur.next().is_none());

    cur.set_search("ab");
    assert!(cur.next().is_none());

    cur.set_search("d");
    assert!(cur.next().is_none());
}

#[test]
fn repetition() {
    let odor = surround_odor(
        Odor::from_iter(vec![Scent::Repetition(Odor::from_iter(vec![Scent::Char(
            'a',
        )]))])
        .mark("id")
        .unwrap(),
    );
    let mut cur = Cur::new(&odor);

    cur.set_search("");
    assert_catch!(cur, "id" => (0, 0, ""));
    assert!(cur.next().is_none());

    cur.set_search("a");
    assert_catch!(cur, "id" => (0, 0, ""));
    assert_catch!(cur, "id" => (0, 1, "a"));
    assert_catch!(cur, "id" => (1, 1, ""));
    assert!(cur.next().is_none());

    cur.set_search("aa");
    assert_catch!(cur, "id" => (0, 0, ""));
    assert_catch!(cur, "id" => (0, 1, "a"));
    assert_catch!(cur, "id" => (0, 2, "aa"));
    assert_catch!(cur, "id" => (1, 1, ""));
    assert_catch!(cur, "id" => (1, 2, "a"));
    assert_catch!(cur, "id" => (2, 2, ""));
    assert!(cur.next().is_none());

    cur.set_search("b");
    assert_catch!(cur, "id" => (0, 0, ""));
    assert_catch!(cur, "id" => (1, 1, ""));
    assert!(cur.next().is_none());
}

#[test]
fn sequence_repetition_and_repeat() {
    let odor = surround_odor(
        Odor::from_iter(vec![
            Scent::Char('b'),
            Scent::Repetition(Odor::from_iter(vec![Scent::Char('a')])),
            Scent::Char('b'),
        ])
        .mark("id")
        .unwrap(),
    );
    let mut cur = Cur::new(&odor);

    cur.set_search("bb");
    assert_catch!(cur, "id" => (0, 2, "bb"));
    assert!(cur.next().is_none());

    cur.set_search("bab");
    assert_catch!(cur, "id" => (0, 3, "bab"));
    assert!(cur.next().is_none());

    cur.set_search("baab");
    assert_catch!(cur, "id" => (0, 4, "baab"));
    assert!(cur.next().is_none());

    cur.set_search("bba");
    assert_catch!(cur, "id" => (0, 2, "bb"));
    assert!(cur.next().is_none());

    cur.set_search("abab");
    assert_catch!(cur, "id" => (1, 4, "bab"));
    assert!(cur.next().is_none());

    cur.set_search("");
    assert!(cur.next().is_none());

    cur.set_search("a");
    assert!(cur.next().is_none());

    cur.set_search("b");
    assert!(cur.next().is_none());

    cur.set_search("ba");
    assert!(cur.next().is_none());

    cur.set_search("baa");
    assert!(cur.next().is_none());
}