use crate::deserialize::Item;
use nom::branch::alt;
use nom::character::streaming::char;
use nom::combinator::map;
use nom::error::{ErrorKind, ParseError};
use nom::sequence::delimited;
use nom::{AsChar, IResult, Input, Parser};
use std::cell::Cell;
pub fn string<'a, I: Input, E: ParseError<I>>(input: I) -> IResult<I, Item, E>
where <I as Input>::Item: AsChar {
alt((
quoted_string::<I, E>,
unquoted_string::<I, E>
)).parse(input)
}
#[test]
pub fn test_string() {
use nom::Finish;
use crate::DeserializeError;
match string::<&str, DeserializeError<&str>>("\"nya\\\"\\(\"").finish() {
Ok(item) => assert_eq!(item, ("", Item::String(String::from("nya\"\\(")))),
Err(err) => panic!("{}", err),
}
match string::<&str, DeserializeError<&str>>("nya\\\"\\(").finish() {
Ok(item) => assert_eq!(item, ("", Item::String(String::from("nya\"(")))),
Err(err) => panic!("{}", err),
}
}
pub fn string_escaper<'a, I: Input, E: ParseError<I>, R: Fn(<I as Input>::Item, &mut Vec<char>) -> bool + 'a>(replacer: R) -> impl Fn(I) -> IResult<I, String, E>
where <I as Input>::Item: AsChar {
move |input| {
let buf_cell: Cell<Vec<char>> = Cell::new(Vec::with_capacity(input.input_len()));
let split = input.split_at_position1_complete::<_, E>(|a| {
let mut buf = buf_cell.take();
let result = replacer(a, &mut buf);
buf_cell.set(buf);
result
}, ErrorKind::Many)?;
Ok((split.0, buf_cell
.take()
.iter()
.collect::<String>()
))
}
}
pub fn quoted_string<'a, I: Input, E: ParseError<I>>(input: I) -> IResult<I, Item, E>
where <I as Input>::Item: AsChar {
map(delimited(
char('"'),
string_escaper(|p: <I as Input>::Item, history| {
match p.as_char() {
'"' => {
match history.last() {
None => true,
Some(last) => match last {
'\\' => {
history.pop();
history.push(p.as_char());
false
},
_ => {
true
}
}
}
}
'n' | 'r' | 't' | '\\' => {
match history.last() {
None => {
history.push(p.as_char());
false
},
Some(last) => match last {
'\\' => {
history.pop();
history.push(match p.as_char() {
'n' => '\n',
'r' => '\r',
't' => '\t',
'\\' => '\\',
_ => unreachable!()
});
false
}
_ => {
history.push(p.as_char());
false
}
}
}
}
_ => {
history.push(p.as_char());
false
}
}
}),
char('"')
), Item::String).parse(input)
}
#[test]
pub fn test_quoted_string() {
use nom::Finish;
use crate::DeserializeError;
match quoted_string::<&str, DeserializeError<&str>>("\"nya\\\"\\(\"").finish() {
Ok(item) => assert_eq!(item, ("", Item::String(String::from("nya\"\\(")))),
Err(err) => panic!("{}", err),
}
}
pub fn unquoted_string<'a, I: Input, E: ParseError<I>>(input: I) -> IResult<I, Item, E>
where <I as Input>::Item: AsChar {
map(string_escaper(|p: <I as Input>::Item, history| {
match p.as_char() {
'"' | '(' | ')' | ',' | ' ' | '\t' | '\n' | '\r' => {
match history.last() {
None => true,
Some(last) => match last {
'\\' => {
history.pop();
history.push(p.as_char());
false
},
_ => true
}
}
}
'n' | 'r' | 't' | '\\' => {
match history.last() {
None => history.push(p.as_char()),
Some(last) => match last {
'\\' => {
history.pop();
history.push(match p.as_char() {
'n' => '\n',
'r' => '\r',
't' => '\t',
'\\' => '\\',
_ => unreachable!()
})
}
_ => history.push(p.as_char())
}
}
false
}
_ => {
history.push(p.as_char());
false
}
}
}), Item::String).parse(input)
}
#[test]
pub fn test_unquoted_string() {
use nom::Finish;
use crate::DeserializeError;
match unquoted_string::<&str, DeserializeError<&str>>("nya\\\"\\(").finish() {
Ok(item) => assert_eq!(item, ("", Item::String(String::from("nya\"(")))),
Err(err) => panic!("{}", err),
}
}