use crate::{
core::{
capture, exact, fragment_exact, get_and, get_end, get_hash, get_question, get_slash,
nothing, query,
},
error::{get_reason, ParseError, ParserErrorReason, PrettyParseError},
FieldNamingScheme,
};
use nom::{branch::alt, IResult};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum RouteParserToken<'a> {
Nothing,
Separator,
Exact(&'a str),
Capture(RefCaptureVariant<'a>),
QueryBegin,
QuerySeparator,
Query {
ident: &'a str,
capture_or_exact: CaptureOrExact<'a>,
},
FragmentBegin,
End,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum RefCaptureVariant<'a> {
Unnamed,
ManyUnnamed,
NumberedUnnamed {
sections: usize,
},
Named(&'a str),
ManyNamed(&'a str),
NumberedNamed {
sections: usize,
name: &'a str,
},
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CaptureOrExact<'a> {
Exact(&'a str),
Capture(RefCaptureVariant<'a>),
}
#[derive(Clone, PartialEq)]
enum ParserState<'a> {
None,
Path { prev_token: RouteParserToken<'a> },
FirstQuery { prev_token: RouteParserToken<'a> },
NthQuery { prev_token: RouteParserToken<'a> },
Fragment { prev_token: RouteParserToken<'a> },
End,
}
impl<'a> ParserState<'a> {
fn transition(self, token: RouteParserToken<'a>) -> Result<Self, ParserErrorReason> {
match self {
ParserState::None => match token {
RouteParserToken::Separator
| RouteParserToken::Exact(_)
| RouteParserToken::Capture(_) => Ok(ParserState::Path { prev_token: token }),
RouteParserToken::QueryBegin => Ok(ParserState::FirstQuery { prev_token: token }),
RouteParserToken::QuerySeparator => Ok(ParserState::NthQuery { prev_token: token }),
RouteParserToken::Query { .. } => Err(ParserErrorReason::NotAllowedStateTransition),
RouteParserToken::FragmentBegin => Ok(ParserState::Fragment { prev_token: token }),
RouteParserToken::Nothing | RouteParserToken::End => Ok(ParserState::End),
},
ParserState::Path { prev_token } => {
match prev_token {
RouteParserToken::Separator => match token {
RouteParserToken::Exact(_) | RouteParserToken::Capture(_) => {
Ok(ParserState::Path { prev_token: token })
}
RouteParserToken::QueryBegin => {
Ok(ParserState::FirstQuery { prev_token: token })
}
RouteParserToken::FragmentBegin => {
Ok(ParserState::Fragment { prev_token: token })
}
RouteParserToken::End => Ok(ParserState::End),
_ => Err(ParserErrorReason::NotAllowedStateTransition),
},
RouteParserToken::Exact(_) => match token {
RouteParserToken::Exact(_)
| RouteParserToken::Separator
| RouteParserToken::Capture(_) => {
Ok(ParserState::Path { prev_token: token })
}
RouteParserToken::QueryBegin => {
Ok(ParserState::FirstQuery { prev_token: token })
}
RouteParserToken::FragmentBegin => {
Ok(ParserState::Fragment { prev_token: token })
}
RouteParserToken::End => Ok(ParserState::End),
_ => Err(ParserErrorReason::NotAllowedStateTransition),
},
RouteParserToken::Capture(_) => match token {
RouteParserToken::Separator | RouteParserToken::Exact(_) => {
Ok(ParserState::Path { prev_token: token })
}
RouteParserToken::QueryBegin => {
Ok(ParserState::FirstQuery { prev_token: token })
}
RouteParserToken::FragmentBegin => {
Ok(ParserState::Fragment { prev_token: token })
}
RouteParserToken::End => Ok(ParserState::End),
_ => Err(ParserErrorReason::NotAllowedStateTransition),
},
_ => Err(ParserErrorReason::InvalidState), }
}
ParserState::FirstQuery { prev_token } => match prev_token {
RouteParserToken::QueryBegin => match token {
RouteParserToken::Query { .. } => {
Ok(ParserState::FirstQuery { prev_token: token })
}
_ => Err(ParserErrorReason::NotAllowedStateTransition),
},
RouteParserToken::Query { .. } => match token {
RouteParserToken::QuerySeparator => {
Ok(ParserState::NthQuery { prev_token: token })
}
RouteParserToken::FragmentBegin => {
Ok(ParserState::Fragment { prev_token: token })
}
RouteParserToken::End => Ok(ParserState::End),
_ => Err(ParserErrorReason::NotAllowedStateTransition),
},
_ => Err(ParserErrorReason::InvalidState),
},
ParserState::NthQuery { prev_token } => match prev_token {
RouteParserToken::QuerySeparator => match token {
RouteParserToken::Query { .. } => {
Ok(ParserState::NthQuery { prev_token: token })
}
_ => Err(ParserErrorReason::NotAllowedStateTransition),
},
RouteParserToken::Query { .. } => match token {
RouteParserToken::QuerySeparator => {
Ok(ParserState::NthQuery { prev_token: token })
}
RouteParserToken::FragmentBegin => {
Ok(ParserState::Fragment { prev_token: token })
}
RouteParserToken::End => Ok(ParserState::End),
_ => Err(ParserErrorReason::NotAllowedStateTransition),
},
_ => Err(ParserErrorReason::InvalidState),
},
ParserState::Fragment { prev_token } => match prev_token {
RouteParserToken::FragmentBegin
| RouteParserToken::Exact(_)
| RouteParserToken::Capture(_) => Ok(ParserState::Fragment { prev_token: token }),
RouteParserToken::End => Ok(ParserState::End),
_ => Err(ParserErrorReason::InvalidState),
},
ParserState::End => Err(ParserErrorReason::TokensAfterEndToken),
}
}
}
pub fn parse(
mut i: &str,
field_naming_scheme: FieldNamingScheme,
) -> Result<Vec<RouteParserToken>, PrettyParseError> {
let input = i;
let mut tokens: Vec<RouteParserToken> = vec![];
let mut state = ParserState::None;
loop {
let (ii, token) = parse_impl(i, &state, field_naming_scheme).map_err(|e| match e {
nom::Err::Error(e) | nom::Err::Failure(e) => PrettyParseError {
error: e,
input,
remaining: i,
},
_ => panic!("parser should not be incomplete"),
})?;
i = ii;
state = state.transition(token).map_err(|reason| {
let error = ParseError {
reason: Some(reason),
expected: vec![],
offset: 0,
};
PrettyParseError {
error,
input,
remaining: i,
}
})?;
tokens.push(token);
if i.is_empty() {
break;
}
}
Ok(tokens)
}
fn parse_impl<'a>(
i: &'a str,
state: &ParserState,
field_naming_scheme: FieldNamingScheme,
) -> IResult<&'a str, RouteParserToken<'a>, ParseError> {
match state {
ParserState::None => alt((
get_slash,
get_question,
get_and,
get_hash,
capture(field_naming_scheme),
exact,
get_end,
nothing,
))(i),
ParserState::Path { prev_token } => match prev_token {
RouteParserToken::Separator => {
alt((
exact,
capture(field_naming_scheme),
get_question,
get_hash,
get_end,
))(i)
.map_err(|mut e: nom::Err<ParseError>| {
let reason: &mut Option<ParserErrorReason> = get_reason(&mut e);
*reason = get_slash(i)
.map(|_| ParserErrorReason::DoubleSlash)
.or_else(|_| get_and(i).map(|_| ParserErrorReason::AndBeforeQuestion))
.ok()
.or(*reason);
e
})
}
RouteParserToken::Exact(_) => {
alt((
get_slash,
exact, capture(field_naming_scheme),
get_question,
get_hash,
get_end,
))(i)
.map_err(|mut e: nom::Err<ParseError>| {
let reason: &mut Option<ParserErrorReason> = get_reason(&mut e);
*reason = get_and(i)
.map(|_| ParserErrorReason::AndBeforeQuestion)
.ok()
.or(*reason);
e
})
}
RouteParserToken::Capture(_) => {
alt((get_slash, exact, get_question, get_hash, get_end))(i).map_err(
|mut e: nom::Err<ParseError>| {
let reason: &mut Option<ParserErrorReason> = get_reason(&mut e);
*reason = capture(field_naming_scheme)(i)
.map(|_| ParserErrorReason::AdjacentCaptures)
.or_else(|_| get_and(i).map(|_| ParserErrorReason::AndBeforeQuestion))
.ok()
.or(*reason);
e
},
)
}
_ => Err(nom::Err::Failure(ParseError {
reason: Some(ParserErrorReason::InvalidState),
expected: vec![],
offset: 0,
})),
},
ParserState::FirstQuery { prev_token } => match prev_token {
RouteParserToken::QueryBegin => {
query(field_naming_scheme)(i).map_err(|mut e: nom::Err<ParseError>| {
let reason: &mut Option<ParserErrorReason> = get_reason(&mut e);
*reason = get_question(i)
.map(|_| ParserErrorReason::MultipleQuestions)
.ok()
.or(*reason);
e
})
}
RouteParserToken::Query { .. } => {
alt((get_and, get_hash, get_end))(i).map_err(|mut e: nom::Err<ParseError>| {
let reason: &mut Option<ParserErrorReason> = get_reason(&mut e);
*reason = get_question(i)
.map(|_| ParserErrorReason::MultipleQuestions)
.ok()
.or(*reason);
e
})
}
_ => Err(nom::Err::Failure(ParseError {
reason: Some(ParserErrorReason::InvalidState),
expected: vec![],
offset: 0,
})),
},
ParserState::NthQuery { prev_token } => match prev_token {
RouteParserToken::QuerySeparator => {
query(field_naming_scheme)(i).map_err(|mut e: nom::Err<ParseError>| {
let reason: &mut Option<ParserErrorReason> = get_reason(&mut e);
*reason = get_question(i)
.map(|_| ParserErrorReason::MultipleQuestions)
.ok()
.or(*reason);
e
})
}
RouteParserToken::Query { .. } => {
alt((get_and, get_hash, get_end))(i).map_err(|mut e: nom::Err<ParseError>| {
let reason: &mut Option<ParserErrorReason> = get_reason(&mut e);
*reason = get_question(i)
.map(|_| ParserErrorReason::MultipleQuestions)
.ok()
.or(*reason);
e
})
}
_ => Err(nom::Err::Failure(ParseError {
reason: Some(ParserErrorReason::InvalidState),
expected: vec![],
offset: 0,
})),
},
ParserState::Fragment { prev_token } => match prev_token {
RouteParserToken::FragmentBegin => {
alt((fragment_exact, capture(field_naming_scheme), get_end))(i)
}
RouteParserToken::Exact(_) => alt((capture(field_naming_scheme), get_end))(i),
RouteParserToken::Capture(_) => alt((fragment_exact, get_end))(i),
_ => Err(nom::Err::Failure(ParseError {
reason: Some(ParserErrorReason::InvalidState),
expected: vec![],
offset: 0,
})),
},
ParserState::End => Err(nom::Err::Failure(ParseError {
reason: Some(ParserErrorReason::TokensAfterEndToken),
expected: vec![],
offset: 0,
})),
}
}
#[cfg(test)]
mod test {
use super::parse as actual_parse;
use crate::{parser::RouteParserToken, FieldNamingScheme, PrettyParseError};
fn parse(i: &str) -> Result<Vec<RouteParserToken>, PrettyParseError> {
actual_parse(i, FieldNamingScheme::Unnamed)
}
mod does_parse {
use super::*;
#[test]
fn empty() {
let x = parse("").expect("Should parse");
assert_eq!(x, vec![RouteParserToken::Nothing])
}
#[test]
fn slash() {
parse("/").expect("should parse");
}
#[test]
fn slash_exact() {
parse("/hello").expect("should parse");
}
#[test]
fn multiple_exact() {
parse("/lorem/ipsum").expect("should parse");
}
#[test]
fn capture_in_path() {
parse("/lorem/{ipsum}").expect("should parse");
}
#[test]
fn capture_rest_in_path() {
parse("/lorem/{*:ipsum}").expect("should parse");
}
#[test]
fn capture_numbered_in_path() {
parse("/lorem/{5:ipsum}").expect("should parse");
}
#[test]
fn exact_query_after_path() {
parse("/lorem?ipsum=dolor").expect("should parse");
}
#[test]
fn leading_query_separator() {
parse("&lorem=ipsum").expect("Should parse");
}
#[test]
fn exact_query() {
parse("?lorem=ipsum").expect("should parse");
}
#[test]
fn capture_query() {
parse("?lorem={ipsum}").expect("should parse");
}
#[test]
fn multiple_queries() {
parse("?lorem=ipsum&dolor=sit").expect("should parse");
}
#[test]
fn query_and_exact_fragment() {
parse("?lorem=ipsum#dolor").expect("should parse");
}
#[test]
fn query_with_exact_and_capture_fragment() {
parse("?lorem=ipsum#dolor{sit}").expect("should parse");
}
#[test]
fn query_with_capture_fragment() {
parse("?lorem=ipsum#{dolor}").expect("should parse");
}
#[test]
fn escaped_backslash() {
let tokens = parse(r#"/escaped\\backslash"#).expect("should parse");
let expected = vec![
RouteParserToken::Separator,
RouteParserToken::Exact(r#"escaped\\backslash"#),
];
assert_eq!(tokens, expected);
}
#[test]
fn escaped_exclamation() {
let tokens = parse(r#"/escaped!!exclamation"#).expect("should parse");
let expected = vec![
RouteParserToken::Separator,
RouteParserToken::Exact(r#"escaped"#),
RouteParserToken::Exact(r#"!"#),
RouteParserToken::Exact(r#"exclamation"#),
];
assert_eq!(tokens, expected);
}
#[test]
fn escaped_open_bracket() {
let tokens = parse(r#"/escaped{{bracket"#).expect("should parse");
let expected = vec![
RouteParserToken::Separator,
RouteParserToken::Exact(r#"escaped"#),
RouteParserToken::Exact(r#"{"#),
RouteParserToken::Exact(r#"bracket"#),
];
assert_eq!(tokens, expected);
}
#[test]
fn escaped_close_bracket() {
let tokens = parse(r#"/escaped}}bracket"#).expect("should parse");
let expected = vec![
RouteParserToken::Separator,
RouteParserToken::Exact(r#"escaped"#),
RouteParserToken::Exact(r#"}"#),
RouteParserToken::Exact(r#"bracket"#),
];
assert_eq!(tokens, expected);
}
}
mod does_not_parse {
use super::*;
use crate::error::{ExpectedToken, ParserErrorReason};
#[test]
fn double_slash() {
let x = parse("//").expect_err("Should not parse");
assert_eq!(x.error.reason, Some(ParserErrorReason::DoubleSlash))
}
#[test]
fn slash_ampersand() {
let x = parse("/&lorem=ipsum").expect_err("Should not parse");
assert_eq!(x.error.reason, Some(ParserErrorReason::AndBeforeQuestion))
}
#[test]
fn non_ident_capture() {
let x = parse("/{lor#m}").expect_err("Should not parse");
assert_eq!(x.error.reason, Some(ParserErrorReason::BadRustIdent('#')));
assert_eq!(
x.error.expected,
vec![ExpectedToken::CloseBracket, ExpectedToken::Ident]
)
}
#[test]
fn after_end() {
let x = parse("/lorem/ipsum!/dolor").expect_err("Should not parse");
assert_eq!(x.error.reason, Some(ParserErrorReason::TokensAfterEndToken));
}
}
mod correct_parse {
use super::*;
use crate::parser::{CaptureOrExact, RefCaptureVariant};
#[test]
fn starting_literal() {
let parsed = parse("lorem").unwrap();
let expected = vec![RouteParserToken::Exact("lorem")];
assert_eq!(parsed, expected);
}
#[test]
fn minimal_path() {
let parsed = parse("/lorem").unwrap();
let expected = vec![
RouteParserToken::Separator,
RouteParserToken::Exact("lorem"),
];
assert_eq!(parsed, expected);
}
#[test]
fn multiple_path() {
let parsed = parse("/lorem/ipsum/dolor/sit").unwrap();
let expected = vec![
RouteParserToken::Separator,
RouteParserToken::Exact("lorem"),
RouteParserToken::Separator,
RouteParserToken::Exact("ipsum"),
RouteParserToken::Separator,
RouteParserToken::Exact("dolor"),
RouteParserToken::Separator,
RouteParserToken::Exact("sit"),
];
assert_eq!(parsed, expected);
}
#[test]
fn capture_path() {
let parsed = parse("/{lorem}/{ipsum}").unwrap();
let expected = vec![
RouteParserToken::Separator,
RouteParserToken::Capture(RefCaptureVariant::Named("lorem")),
RouteParserToken::Separator,
RouteParserToken::Capture(RefCaptureVariant::Named("ipsum")),
];
assert_eq!(parsed, expected);
}
#[test]
fn query() {
let parsed = parse("?query=this").unwrap();
let expected = vec![
RouteParserToken::QueryBegin,
RouteParserToken::Query {
ident: "query",
capture_or_exact: CaptureOrExact::Exact("this"),
},
];
assert_eq!(parsed, expected);
}
#[test]
fn query_2_part() {
let parsed = parse("?lorem=ipsum&dolor=sit").unwrap();
let expected = vec![
RouteParserToken::QueryBegin,
RouteParserToken::Query {
ident: "lorem",
capture_or_exact: CaptureOrExact::Exact("ipsum"),
},
RouteParserToken::QuerySeparator,
RouteParserToken::Query {
ident: "dolor",
capture_or_exact: CaptureOrExact::Exact("sit"),
},
];
assert_eq!(parsed, expected);
}
#[test]
fn query_3_part() {
let parsed = parse("?lorem=ipsum&dolor=sit&amet=consectetur").unwrap();
let expected = vec![
RouteParserToken::QueryBegin,
RouteParserToken::Query {
ident: "lorem",
capture_or_exact: CaptureOrExact::Exact("ipsum"),
},
RouteParserToken::QuerySeparator,
RouteParserToken::Query {
ident: "dolor",
capture_or_exact: CaptureOrExact::Exact("sit"),
},
RouteParserToken::QuerySeparator,
RouteParserToken::Query {
ident: "amet",
capture_or_exact: CaptureOrExact::Exact("consectetur"),
},
];
assert_eq!(parsed, expected);
}
#[test]
fn exact_fragment() {
let parsed = parse("#lorem").unwrap();
let expected = vec![
RouteParserToken::FragmentBegin,
RouteParserToken::Exact("lorem"),
];
assert_eq!(parsed, expected);
}
#[test]
fn capture_fragment() {
let parsed = parse("#{lorem}").unwrap();
let expected = vec![
RouteParserToken::FragmentBegin,
RouteParserToken::Capture(RefCaptureVariant::Named("lorem")),
];
assert_eq!(parsed, expected);
}
#[test]
fn mixed_fragment() {
let parsed = parse("#{lorem}ipsum{dolor}").unwrap();
let expected = vec![
RouteParserToken::FragmentBegin,
RouteParserToken::Capture(RefCaptureVariant::Named("lorem")),
RouteParserToken::Exact("ipsum"),
RouteParserToken::Capture(RefCaptureVariant::Named("dolor")),
];
assert_eq!(parsed, expected);
}
#[test]
fn end_after_path() {
let parsed = parse("/lorem!").unwrap();
let expected = vec![
RouteParserToken::Separator,
RouteParserToken::Exact("lorem"),
RouteParserToken::End,
];
assert_eq!(parsed, expected);
}
#[test]
fn end_after_path_separator() {
let parsed = parse("/lorem/!").unwrap();
let expected = vec![
RouteParserToken::Separator,
RouteParserToken::Exact("lorem"),
RouteParserToken::Separator,
RouteParserToken::End,
];
assert_eq!(parsed, expected);
}
#[test]
fn end_after_path_capture() {
let parsed = parse("/lorem/{cap}!").unwrap();
let expected = vec![
RouteParserToken::Separator,
RouteParserToken::Exact("lorem"),
RouteParserToken::Separator,
RouteParserToken::Capture(RefCaptureVariant::Named("cap")),
RouteParserToken::End,
];
assert_eq!(parsed, expected);
}
#[test]
fn end_after_query_capture() {
let parsed = parse("?lorem={cap}!").unwrap();
let expected = vec![
RouteParserToken::QueryBegin,
RouteParserToken::Query {
ident: "lorem",
capture_or_exact: CaptureOrExact::Capture(RefCaptureVariant::Named("cap")),
},
RouteParserToken::End,
];
assert_eq!(parsed, expected);
}
#[test]
fn end_after_frag_capture() {
let parsed = parse("#{cap}!").unwrap();
let expected = vec![
RouteParserToken::FragmentBegin,
RouteParserToken::Capture(RefCaptureVariant::Named("cap")),
RouteParserToken::End,
];
assert_eq!(parsed, expected);
}
#[test]
fn just_end() {
let parsed = parse("!").unwrap();
assert_eq!(parsed, vec![RouteParserToken::End]);
}
}
}