use spec::{GeneralQSSpec, ScanAutomaton, PartialCodePoint};
use error::CoreError;
use std::borrow::Cow;
pub fn to_content<'a, Spec: GeneralQSSpec>(
quoted_string: &'a str
) -> Result<Cow<'a, str>, CoreError>
{
let mut automaton = ScanAutomaton::<Spec::Parsing>::new();
let mut continue_copy_from = None;
for (idx, bch) in quoted_string.bytes().enumerate() {
let emit = automaton.advance(PartialCodePoint::from_utf8_byte(bch))?;
if !emit {
continue_copy_from = Some(idx);
break;
}
}
if let Some(idx) = continue_copy_from {
let mut buffer = Vec::with_capacity(quoted_string.len()-2);
buffer.extend_from_slice("ed_string.as_bytes()[0..idx]);
for bch in "ed_string.as_bytes()[idx+1..] {
let emit = automaton.advance(PartialCodePoint::from_utf8_byte(*bch))?;
if emit {
buffer.push(*bch)
}
}
automaton.end()?;
let strfied = String::from_utf8(buffer)
.expect("[BUG] automaton caused a code point to be only partially emitted");
Ok(Cow::Owned(strfied))
} else {
automaton.end()?;
let len = quoted_string.len();
Ok(Cow::Borrowed("ed_string[1..len-1]))
}
}
pub fn strip_dquotes(quoted_string: &str) -> Option<&str> {
let len = quoted_string.len();
let bytes = quoted_string.as_bytes();
if bytes.iter().next() == Some(&b'"') && bytes[len-1] == b'"' {
Some("ed_string[1..len-1])
} else {
None
}
}
#[cfg(test)]
mod test {
mod to_content {
use test_utils::*;
use error::CoreError;
use std::borrow::Cow;
use super::super::to_content;
#[test]
fn no_quotes() {
let res = to_content::<TestSpec>("noquotes");
assert_eq!(res, Err(CoreError::DoesNotStartWithDQuotes));
}
#[test]
fn unnecessary_quoted() {
let res = to_content::<TestSpec>(r#""simple""#).unwrap();
assert_eq!(res, Cow::Borrowed("simple"))
}
#[test]
fn quoted_but_no_quoted_pair() {
let res = to_content::<TestSpec>(r#""abc def""#).unwrap();
assert_eq!(res, Cow::Borrowed("abc def"))
}
#[test]
fn with_quoted_pair() {
let res = to_content::<TestSpec>(r#""a\"b""#).unwrap();
let expected: Cow<'static, str> = Cow::Owned(r#"a"b"#.into());
assert_eq!(res, expected);
}
#[test]
fn with_multiple_quoted_pairs() {
let res = to_content::<TestSpec>(r#""a\"\bc\ d""#).unwrap();
let expected: Cow<'static, str> = Cow::Owned(r#"a"bc d"#.into());
assert_eq!(res, expected);
}
#[test]
fn empty() {
let res = to_content::<TestSpec>(r#""""#).unwrap();
assert_eq!(res, Cow::Borrowed(""))
}
#[test]
fn strip_non_semantic_ws() {
let res = to_content::<TestSpec>("\"hy \n\nthere\"").unwrap();
let expected: Cow<'static, str> = Cow::Owned("hy there".into());
assert_eq!(res, expected);
}
#[test]
fn tailing_escape() {
let res = to_content::<TestSpec>(r#""ab\""#);
assert_eq!(res, Err(CoreError::DoesNotEndWithDQuotes));
}
#[test]
fn missing_escape() {
let res = to_content::<TestSpec>("\"a\"\"");
assert_eq!(res, Err(CoreError::QuotedStringAlreadyEnded));
}
#[test]
fn custom_state_in_parsing_impl_is_used() {
let res = to_content::<TestSpec>("\"hy \n+++---\nthere\"").unwrap();
let expected: Cow<'static, str> = Cow::Owned("hy there".into());
assert_eq!(res, expected);
let res = to_content::<TestSpec>("\"hy \n+--\nthere\"");
assert_eq!(res, Err(CoreError::InvalidChar));
}
}
mod strip_quotes {
use super::super::strip_dquotes;
#[test]
fn empty_string() {
assert!(strip_dquotes("").is_none());
}
#[test]
fn empty_quoted_string() {
assert_eq!(strip_dquotes("\"\""), Some(""));
}
#[test]
fn missing_quotes() {
assert_eq!(strip_dquotes("\"abc"), None);
assert_eq!(strip_dquotes("abc\""), None);
}
#[test]
fn simple_string() {
assert_eq!(strip_dquotes("\"simple\""), Some("simple"));
}
}
}