diff --git a/src/matchers.rs b/src/matchers.rs index c985a8d..bbe4cce 100644 --- a/src/matchers.rs +++ b/src/matchers.rs @@ -27,3 +27,52 @@ macro_rules! text_token { } }}; } + +macro_rules! until { + ($i:expr, $term:ident!( $( $args:tt )* ) ) => {{ + use $crate::{Result, Offsetable}; + let mut acc = Vec::new(); + let mut _i = $i.clone(); + let pfn = || { + loop { + match $term!(_i.clone(), $($args)*) { + Result::Complete(_, _) => return Result::Complete(_i, acc), + Result::Abort(e) => return Result::Abort(e), + Result::Incomplete(offset) => return Result::Incomplete(offset), + Result::Fail(_) => { + // noop + } + } + let item = match _i.next() { + Some(it) => it, + None => return Result::Incomplete(_i.get_offset()), + }; + acc.push(item); + } + }; + pfn() + }}; + + ($i:expr, $term:ident) => { + consume_until!($i, run!($term)) + }; +} + +pub fn must_string<'a, I, E>(matched: Result, E>, msg: E) -> Result +where + I: InputIter, + E: Debug + Display, +{ + match matched { + Result::Complete(i, mut o) => { + let new_string = String::from_utf8(o.drain(0..).map(|b| *b).collect()); + match new_string { + Ok(s) => Result::Complete(i, s), + Err(_) => Result::Abort(Error::new(msg, &i)), + } + }, + Result::Incomplete(offset) => Result::Incomplete(offset), + Result::Abort(e) => Result::Abort(e), + Result::Fail(e) => Result::Fail(e), + } +} \ No newline at end of file diff --git a/src/test.rs b/src/test.rs index 9956874..8508df9 100644 --- a/src/test.rs +++ b/src/test.rs @@ -2,6 +2,7 @@ use std::fmt::{Debug, Display}; use super::{InputIter, Offsetable, Result}; use iter::SliceIter; +use matchers::must_string; #[test] fn test_slice_iter() { @@ -349,3 +350,32 @@ fn test_repeat_abort() { let result = repeat!(iter, must!(will_fail)); assert!(result.is_abort()); } + +#[test] +fn test_until() { + let input_str = "foo; "; + let iter = SliceIter::new(input_str.as_bytes()); + let result = must_string(until!(iter, text_token!("; ")), "AAAHHH!".to_string()); + assert!(result.is_complete()); + if let Result::Complete(i, o) = result { + assert_eq!(i.get_offset(), 3); + assert_eq!(o.len(), 3); + assert_eq!(&o, "foo"); + } +} + +#[test] +fn test_until_abort() { + let input_str = "foo "; + let iter = SliceIter::new(input_str.as_bytes()); + let result = until!(iter, must!(will_fail)); + assert!(result.is_abort()); +} + +#[test] +fn test_until_incomplete() { + let input_str = "foo;"; + let iter = SliceIter::new(input_str.as_bytes()); + let result = until!(iter, text_token!("; ")); + assert!(result.is_incomplete()); +} \ No newline at end of file