1use spec::{ScanAutomaton, GeneralQSSpec, PartialCodePoint};
2use error::CoreError;
3
4pub fn validate<Spec: GeneralQSSpec>(input: &str) -> bool {
20 parse::<Spec>(input)
21 .map(|res|res.tail.is_empty())
22 .unwrap_or(false)
23}
24
25#[derive(Debug, Clone, Eq, PartialEq, Hash)]
27pub struct Parsed<'a> {
28 pub quoted_string: &'a str,
30 pub tail: &'a str
32}
33
34pub fn parse<Impl: GeneralQSSpec>(input: &str) -> Result<Parsed, (usize, CoreError)> {
59 let mut automaton = ScanAutomaton::<Impl::Parsing>::new();
60
61 for (idx, bch) in input.bytes().enumerate() {
62 automaton.advance(PartialCodePoint::from_utf8_byte(bch))
63 .map_err(|err| (idx, err.into()))?;
64
65 if automaton.did_end() {
66 return Ok(Parsed {
67 quoted_string: &input[0..idx + 1],
69 tail: &input[idx + 1..]
70 })
71 }
72 }
73 match automaton.end() {
76 Ok(_) =>
77 panic!("[BUG] automaton.did_end() == false but automaton.end() does not trigger error"),
78 Err(err) => {
79 Err((input.len(), err.into()))
80 }
81 }
82}
83
84
85#[cfg(test)]
86mod test {
87
88 mod parse {
89 use test_utils::*;
90 use error::CoreError;
91 use super::super::parse;
92
93 #[test]
94 fn parse_simple() {
95 let parsed = parse::<TestSpec>("\"simple\"").unwrap();
96 assert_eq!(parsed.quoted_string, "\"simple\"");
97 assert_eq!(parsed.tail, "");
98 }
99
100 #[test]
101 fn parse_with_tail() {
102 let parsed = parse::<TestSpec>("\"simple\"; abc").unwrap();
103 assert_eq!(parsed.quoted_string, "\"simple\"");
104 assert_eq!(parsed.tail, "; abc");
105 }
106
107 #[test]
108 fn parse_with_quoted_pairs() {
109 let parsed = parse::<TestSpec>("\"si\\\"m\\\\ple\"").unwrap();
110 assert_eq!(parsed.quoted_string, "\"si\\\"m\\\\ple\"");
111 assert_eq!(parsed.tail, "");
112 }
113
114 #[test]
115 fn parse_with_unnecessary_quoted_pairs() {
116 let parsed = parse::<TestSpec>("\"sim\\p\\le\"").unwrap();
117 assert_eq!(parsed.quoted_string, "\"sim\\p\\le\"");
118 assert_eq!(parsed.tail, "");
119 }
120
121 #[test]
122 fn reject_missing_quoted() {
123 let res = parse::<TestSpec>("simple");
124 assert_eq!(res, Err((0, CoreError::DoesNotStartWithDQuotes)));
125 }
126
127 #[test]
128 fn reject_tailing_escape() {
129 let res = parse::<TestSpec>("\"simple\\\"");
130 assert_eq!(res, Err((9, CoreError::DoesNotEndWithDQuotes)));
131 }
132
133 #[test]
134 fn reject_unquoted_quotable() {
135 let res = parse::<TestSpec>("\"simp\\\0le\"");
136 assert_eq!(res, Err((6, CoreError::UnquoteableCharQuoted)));
137 }
138
139 #[test]
140 fn reject_missing_closing_dquotes() {
141 let res = parse::<TestSpec>("\"simple");
142 assert_eq!(res, Err((7, CoreError::DoesNotEndWithDQuotes)));
143 }
144
145 #[test]
146 fn empty_string_does_not_panic() {
147 let res = parse::<TestSpec>("");
148 assert_eq!(res, Err((0, CoreError::DoesNotEndWithDQuotes)));
149 }
150
151 }
152
153 mod validate {
154 use test_utils::*;
155 use super::super::validate;
156
157 #[test]
158 fn accept_valid_quoted_string() {
159 assert!(validate::<TestSpec>("\"that\\\"s strange\""));
160 }
161
162 #[test]
163 fn reject_invalid_quoted_string() {
164 assert!(!validate::<TestSpec>("ups"))
165 }
166
167 #[test]
168 fn reject_quoted_string_shorter_than_input() {
169 assert!(!validate::<TestSpec>("\"nice!\"ups whats here?\""))
170 }
171
172 }
173
174
175
176}