nextest_filtering/parsing/
unicode_string.rsuse super::{expect_n, PResult, Span, SpanLength};
use crate::errors::ParseSingleError;
use std::fmt;
use winnow::{
combinator::{alt, delimited, preceded, repeat, trace},
token::{take_till, take_while},
Parser,
};
fn parse_unicode(input: &mut Span<'_>) -> PResult<char> {
trace("parse_unicode", |input: &mut _| {
let parse_hex = take_while(1..=6, |c: char| c.is_ascii_hexdigit());
let parse_delimited_hex = preceded('u', delimited('{', parse_hex, '}'));
let parse_u32 = parse_delimited_hex.try_map(|hex| u32::from_str_radix(hex, 16));
parse_u32.verify_map(std::char::from_u32).parse_next(input)
})
.parse_next(input)
}
fn parse_escaped_char(input: &mut Span<'_>) -> PResult<Option<char>> {
trace("parse_escaped_char", |input: &mut _| {
let valid = alt((
parse_unicode,
'n'.value('\n'),
'r'.value('\r'),
't'.value('\t'),
'b'.value('\u{08}'),
'f'.value('\u{0C}'),
'\\'.value('\\'),
'/'.value('/'),
')'.value(')'),
','.value(','),
));
preceded(
'\\',
expect_n(
valid,
ParseSingleError::InvalidEscapeCharacter,
SpanLength::Offset(-1, 2),
),
)
.parse_next(input)
})
.parse_next(input)
}
pub(crate) struct DisplayParsedString<'a>(pub(crate) &'a str);
impl fmt::Display for DisplayParsedString<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for c in self.0.chars() {
match c {
'/' => f.write_str("\\/")?,
')' => f.write_str("\\)")?,
',' => f.write_str("\\,")?,
c => write!(f, "{}", c.escape_default())?,
}
}
Ok(())
}
}
fn parse_literal<'i>(input: &mut Span<'i>) -> PResult<&'i str> {
trace("parse_literal", |input: &mut _| {
let not_quote_slash = take_till(1.., (',', ')', '\\'));
not_quote_slash
.verify(|s: &str| !s.is_empty())
.parse_next(input)
})
.parse_next(input)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum StringFragment<'a> {
Literal(&'a str),
EscapedChar(char),
}
fn parse_fragment<'i>(input: &mut Span<'i>) -> PResult<Option<StringFragment<'i>>> {
trace(
"parse_fragment",
alt((
parse_literal.map(|span| Some(StringFragment::Literal(span))),
parse_escaped_char.map(|res| res.map(StringFragment::EscapedChar)),
)),
)
.parse_next(input)
}
pub(super) fn parse_string(input: &mut Span<'_>) -> PResult<Option<String>> {
trace(
"parse_string",
repeat(0.., parse_fragment).fold(
|| Some(String::new()),
|string, fragment| {
match (string, fragment) {
(Some(mut string), Some(StringFragment::Literal(s))) => {
string.push_str(s);
Some(string)
}
(Some(mut string), Some(StringFragment::EscapedChar(c))) => {
string.push(c);
Some(string)
}
(Some(_), None) => {
None
}
(None, _) => None,
}
},
),
)
.parse_next(input)
}