1use std::borrow::Cow;
2#[allow(unused_imports, deprecated)]
6use std::ascii::AsciiExt;
7
8use error::CoreError;
9use spec::{
10 QuotingClassifier,
11 QuotingClass,
12 WithoutQuotingValidator,
13 PartialCodePoint,
14 GeneralQSSpec
15};
16
17
18
19#[inline]
33pub fn quote<Spec: GeneralQSSpec>(
34 input: &str
35) -> Result<String, CoreError>
36{
37 let mut out = String::with_capacity(input.len()+2);
38 out.push('"');
39 quote_inner::<Spec>(input, &mut out)?;
40 out.push('"');
41 Ok(out)
42}
43
44fn quote_inner<Spec: GeneralQSSpec>(
51 input: &str,
52 out: &mut String,
53) -> Result<(), CoreError>
54{
55 use self::QuotingClass::*;
56 for ch in input.chars() {
57 match Spec::Quoting::classify_for_quoting(PartialCodePoint::from_code_point(ch as u32)) {
58 QText => out.push(ch),
59 NeedsQuoting => { out.push('\\'); out.push(ch); }
60 Invalid => return Err(CoreError::InvalidChar)
61 }
62 }
63 Ok(())
64}
65
66pub fn quote_if_needed<'a, Spec, WQImpl>(
103 input: &'a str,
104 validator: &mut WQImpl
105) -> Result<Cow<'a, str>, CoreError>
106 where Spec: GeneralQSSpec,
107 WQImpl: WithoutQuotingValidator
108{
109 let mut needs_quoting_from = None;
110 for (idx, ch) in input.char_indices() {
111 let pcp = PartialCodePoint::from_code_point(ch as u32);
112 if !validator.next(pcp) {
113 needs_quoting_from = Some(idx);
114 break;
115 } else {
116 #[cfg(debug_assertions)]
118 {
119 use self::QuotingClass::*;
120 match Spec::Quoting::classify_for_quoting(pcp) {
121 QText => {},
122 Invalid => panic!(concat!("[BUG] representable without quoted string,",
123 "but invalid in quoted string: {}"), ch),
124 NeedsQuoting => panic!(concat!("[BUG] representable without quoted string,",
125 "but not without escape in quoted string: {}"), ch)
126 }
127 }
128 }
129 }
130
131 let start_quoting_from =
132 if input.len() == 0 {
133 0
134 } else if let Some(offset) = needs_quoting_from {
135 offset
136 } else {
137 return if validator.end() {
138 Ok(Cow::Borrowed(input))
139 } else {
140 let mut out = String::with_capacity(input.len() + 2);
141 out.push('"');
142 out.push_str(input);
143 out.push('"');
144 Ok(Cow::Owned(out))
145 };
146 };
147
148
149 let mut out = String::with_capacity(input.len() + 3);
150 out.push('"');
151 out.push_str(&input[0..start_quoting_from]);
152 quote_inner::<Spec>(&input[start_quoting_from..], &mut out)?;
153 out.push('"');
154 Ok(Cow::Owned(out))
155}
156
157
158#[cfg(test)]
159mod test {
160 #[allow(warnings)]
164 use std::ascii::AsciiExt;
165 use test_utils::*;
166 use error::CoreError;
167 use super::*;
168
169 #[test]
170 fn quote_simple() {
171 let data = &[
172 ("this is simple", "\"this is simple\""),
173 ("with quotes\" ", "\"with quotes\\\" \""),
174 ("with slash\\ ", "\"with slash\\\\ \"")
175 ];
176 for &(unquoted, quoted) in data.iter() {
177 let got_quoted = quote::<TestSpec>(unquoted).unwrap();
178 assert_eq!(got_quoted, quoted);
179 }
180 }
181
182 #[test]
183 fn quote_unquotable() {
184 let res = quote::<TestSpec>("→");
185 assert_eq!(res.unwrap_err(), CoreError::InvalidChar);
186 }
187
188 #[test]
189 fn quote_if_needed_unneeded() {
190 let mut without_quoting = TestUnquotedValidator::new();
191 let out= quote_if_needed::<TestSpec, _>("abcdef", &mut without_quoting).unwrap();
192 assert_eq!(out, Cow::Borrowed("abcdef"));
193 }
194
195 #[test]
196 fn quote_if_needed_state() {
197 let mut without_quoting = TestUnquotedValidator::new();
198 let out = quote_if_needed::<TestSpec, _>("abcd.e", &mut without_quoting).unwrap();
199 assert_eq!(out, Cow::Borrowed("abcd.e"));
200 assert_eq!(without_quoting.count, 6);
201 assert_eq!(without_quoting.last_was_dot, false)
202 }
203
204 #[test]
205 fn quote_if_needed_needed_because_char() {
206 let mut without_quoting = TestUnquotedValidator::new();
207 let out = quote_if_needed::<TestSpec, _>("ab def", &mut without_quoting).unwrap();
208 let expected: Cow<'static, str> = Cow::Owned("\"ab def\"".into());
209 assert_eq!(out, expected);
210 assert!(without_quoting.count >= 2);
211 }
212
213 #[test]
214 fn quote_if_needed_needed_because_state() {
215 let mut without_quoting = TestUnquotedValidator::new();
216 let out = quote_if_needed::<TestSpec, _>("abc..f", &mut without_quoting).unwrap();
217 let expected: Cow<'static, str> = Cow::Owned("\"abc..f\"".into());
218 assert_eq!(out, expected);
219 assert!(without_quoting.count >= 4);
220 }
221
222 #[test]
223 fn quote_if_needed_needed_because_end() {
224 let mut without_quoting = TestUnquotedValidator::new();
225 let out = quote_if_needed::<TestSpec, _>("a", &mut without_quoting).unwrap();
226 let expected: Cow<'static, str> = Cow::Owned("\"a\"".into());
227 assert_eq!(out, expected);
228 assert!(without_quoting.count >= 1);
229 }
230
231 #[test]
232 fn quote_if_needed_empty_value() {
233 let mut without_quoting = TestUnquotedValidator::new();
234 let out = quote_if_needed::<TestSpec, _>("", &mut without_quoting).unwrap();
235 let expected: Cow<'static, str> = Cow::Owned("\"\"".into());
236 assert_eq!(out, expected);
237 assert_eq!(without_quoting.count, 0);
238 }
239}