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
use nom::{ bytes::complete::{take_while, take_while1}, character::complete::char, combinator::{cut, map, opt}, multi::separated_list0, sequence::{pair, preceded, terminated}, IResult, }; use nom_locate::LocatedSpan; pub type Span<'a> = LocatedSpan<&'a str>; const WHITSPACE: &str = " \t\r\n"; const ALPHA_EXTRA: &str = "_"; pub fn ws(input: Span) -> IResult<Span, Span> { take_while(move |c| WHITSPACE.contains(c))(input) } pub fn ws1(input: Span) -> IResult<Span, Span> { take_while1(move |c| WHITSPACE.contains(c))(input) } pub fn trailing_comma(input: Span) -> IResult<Span, Option<char>> { opt(preceded(ws, char(',')))(input) } pub fn parse_identifier(input: Span) -> IResult<Span, String> { map( pair( take_while1(move |c: char| c.is_ascii_alphabetic()), take_while(move |c: char| c.is_ascii_alphanumeric() || ALPHA_EXTRA.contains(c)), ), |t| format!("{}{}", t.0, t.1), )(input) } fn parse_generics(input: Span) -> IResult<Span, Vec<String>> { map( opt(preceded( preceded(ws, char('<')), cut(terminated( separated_list0(parse_field_separator, preceded(ws, parse_identifier)), preceded(trailing_comma, preceded(ws, char('>'))), )), )), |v| match v { Some(v) => v, None => Vec::with_capacity(0), }, )(input) } pub fn parse_identifier_with_generics(input: Span) -> IResult<Span, (String, Vec<String>)> { pair(parse_identifier, parse_generics)(input) } #[cfg(test)] pub(crate) fn assert_parse<'a, T: std::fmt::Debug + PartialEq>( output: IResult<LocatedSpan<&'a str>, T>, expected_value: T, ) { assert!(output.is_ok(), "{:?}", output); let output = output.unwrap(); assert_eq!(output.0.fragment(), &""); assert_eq!(output.1, expected_value); } #[test] fn test_parse_identifier() { assert_parse(parse_identifier(Span::new("test")), "test".to_string()); assert_parse( parse_identifier(Span::new("test123")), "test123".to_string(), ); } #[test] fn test_parse_identifier_invalid() { use nom::error::ErrorKind; assert_eq!( parse_identifier(Span::new("123test")), Err(nom::Err::Error(nom::error::Error { input: Span::new("123test"), code: ErrorKind::TakeWhile1 })) ); assert_eq!( parse_identifier(Span::new("_test")), Err(nom::Err::Error(nom::error::Error { input: Span::new("_test"), code: ErrorKind::TakeWhile1 })) ); } pub fn parse_field_separator(input: Span) -> IResult<Span, char> { preceded(ws, char(','))(input) }