home ~ projects ~ socials

Capture A Match That Is Not Followed By A String In Rust's nom

The goal of this parser is to match everything up to a specific string. Right no it returns a Vec of &str that can be joined. I'm trying to figure out if there's a way to flatten it and return a single &str.

I don't have a specific need to get to the &str. Mainly I'm curious to see if it can be done and thereby keep &str all the way up. If not, I'll do the conversion of the final Vec into a String for the return value of get_up_to()

```cargo
[dependencies]
nom = "7.1.3"
```

use nom::bytes::complete::tag_no_case;
use nom::bytes::complete::take_until;
use nom::combinator::not;
use nom::sequence::pair;
use nom::sequence::terminated;
use nom::IResult;

fn get_up_to<'a>(source: &'a str, target: &'a str) -> IResult<&'a str, Vec<&'a str>> {
  let mut result = vec![];
  let (source, captured) = (|src| get_part(src, target))(source)?;
  result.push(captured.0);
  result.push(captured.1);
  Ok((source, result))
}

fn get_part<'a>(source: &'a str, target: &'a str) -> IResult<&'a str, (&'a str, &'a str)> {
    let (source, captured) = terminated(
        pair(take_until(target), tag_no_case(target)),
        not(tag_no_case(target)),
    )(source)?;
  Ok((source, captured))
}

fn main() {
  let tests = vec![("alfa bravo charlie", "bravo", " charlie", "alfa bravo"), ];
  for test in tests.iter() {
    let result = get_up_to(test.0, test.1).unwrap();
    assert_eq!(result.0, test.2, "Check remainder");
    assert_eq!(result.1.join(""), test.3, "Check captured");
  }
  println!("All Tests Passed");
}
Output:
All Tests Passed
-- end of line --