use spec::{ScanAutomaton, GeneralQSSpec, PartialCodePoint};
use error::CoreError;
pub fn validate<Spec: GeneralQSSpec>(input: &str) -> bool {
parse::<Spec>(input)
.map(|res|res.tail.is_empty())
.unwrap_or(false)
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Parsed<'a> {
pub quoted_string: &'a str,
pub tail: &'a str
}
pub fn parse<Impl: GeneralQSSpec>(input: &str) -> Result<Parsed, (usize, CoreError)> {
let mut automaton = ScanAutomaton::<Impl::Parsing>::new();
for (idx, bch) in input.bytes().enumerate() {
automaton.advance(PartialCodePoint::from_utf8_byte(bch))
.map_err(|err| (idx, err.into()))?;
if automaton.did_end() {
return Ok(Parsed {
quoted_string: &input[0..idx + 1],
tail: &input[idx + 1..]
})
}
}
match automaton.end() {
Ok(_) =>
panic!("[BUG] automaton.did_end() == false but automaton.end() does not trigger error"),
Err(err) => {
Err((input.len(), err.into()))
}
}
}
#[cfg(test)]
mod test {
mod parse {
use test_utils::*;
use error::CoreError;
use super::super::parse;
#[test]
fn parse_simple() {
let parsed = parse::<TestSpec>("\"simple\"").unwrap();
assert_eq!(parsed.quoted_string, "\"simple\"");
assert_eq!(parsed.tail, "");
}
#[test]
fn parse_with_tail() {
let parsed = parse::<TestSpec>("\"simple\"; abc").unwrap();
assert_eq!(parsed.quoted_string, "\"simple\"");
assert_eq!(parsed.tail, "; abc");
}
#[test]
fn parse_with_quoted_pairs() {
let parsed = parse::<TestSpec>("\"si\\\"m\\\\ple\"").unwrap();
assert_eq!(parsed.quoted_string, "\"si\\\"m\\\\ple\"");
assert_eq!(parsed.tail, "");
}
#[test]
fn parse_with_unnecessary_quoted_pairs() {
let parsed = parse::<TestSpec>("\"sim\\p\\le\"").unwrap();
assert_eq!(parsed.quoted_string, "\"sim\\p\\le\"");
assert_eq!(parsed.tail, "");
}
#[test]
fn reject_missing_quoted() {
let res = parse::<TestSpec>("simple");
assert_eq!(res, Err((0, CoreError::DoesNotStartWithDQuotes)));
}
#[test]
fn reject_tailing_escape() {
let res = parse::<TestSpec>("\"simple\\\"");
assert_eq!(res, Err((9, CoreError::DoesNotEndWithDQuotes)));
}
#[test]
fn reject_unquoted_quotable() {
let res = parse::<TestSpec>("\"simp\\\0le\"");
assert_eq!(res, Err((6, CoreError::UnquoteableCharQuoted)));
}
#[test]
fn reject_missing_closing_dquotes() {
let res = parse::<TestSpec>("\"simple");
assert_eq!(res, Err((7, CoreError::DoesNotEndWithDQuotes)));
}
#[test]
fn empty_string_does_not_panic() {
let res = parse::<TestSpec>("");
assert_eq!(res, Err((0, CoreError::DoesNotEndWithDQuotes)));
}
}
mod validate {
use test_utils::*;
use super::super::validate;
#[test]
fn accept_valid_quoted_string() {
assert!(validate::<TestSpec>("\"that\\\"s strange\""));
}
#[test]
fn reject_invalid_quoted_string() {
assert!(!validate::<TestSpec>("ups"))
}
#[test]
fn reject_quoted_string_shorter_than_input() {
assert!(!validate::<TestSpec>("\"nice!\"ups whats here?\""))
}
}
}