nextest_filtering/parsing/
unicode_string.rs1use super::{PResult, Span, SpanLength, expect_n};
7use crate::errors::ParseSingleError;
8use std::fmt;
9use winnow::{
10 Parser,
11 combinator::{alt, delimited, preceded, repeat, trace},
12 token::{take_till, take_while},
13};
14
15fn parse_unicode(input: &mut Span<'_>) -> PResult<char> {
16 trace("parse_unicode", |input: &mut _| {
17 let parse_hex = take_while(1..=6, |c: char| c.is_ascii_hexdigit());
18 let parse_delimited_hex = preceded('u', delimited('{', parse_hex, '}'));
19 let parse_u32 = parse_delimited_hex.try_map(|hex| u32::from_str_radix(hex, 16));
20 parse_u32.verify_map(std::char::from_u32).parse_next(input)
21 })
22 .parse_next(input)
23}
24
25fn parse_escaped_char(input: &mut Span<'_>) -> PResult<Option<char>> {
26 trace("parse_escaped_char", |input: &mut _| {
27 let valid = alt((
28 parse_unicode,
29 'n'.value('\n'),
30 'r'.value('\r'),
31 't'.value('\t'),
32 'b'.value('\u{08}'),
33 'f'.value('\u{0C}'),
34 '\\'.value('\\'),
35 '/'.value('/'),
36 alt((')'.value(')'), ','.value(','))),
37 ));
38 preceded(
39 '\\',
40 expect_n(
42 valid,
43 ParseSingleError::InvalidEscapeCharacter,
44 SpanLength::Offset(-1, 2),
46 ),
47 )
48 .parse_next(input)
49 })
50 .parse_next(input)
51}
52
53pub(crate) struct DisplayParsedString<'a>(pub(crate) &'a str);
55
56impl fmt::Display for DisplayParsedString<'_> {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 for c in self.0.chars() {
59 match c {
60 '/' => f.write_str("\\/")?,
62 ')' => f.write_str("\\)")?,
63 ',' => f.write_str("\\,")?,
64 c => write!(f, "{}", c.escape_default())?,
66 }
67 }
68 Ok(())
69 }
70}
71fn parse_literal<'i>(input: &mut Span<'i>) -> PResult<&'i str> {
72 trace("parse_literal", |input: &mut _| {
73 let not_quote_slash = take_till(1.., (',', ')', '\\'));
74
75 not_quote_slash
76 .verify(|s: &str| !s.is_empty())
77 .parse_next(input)
78 })
79 .parse_next(input)
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83enum StringFragment<'a> {
84 Literal(&'a str),
85 EscapedChar(char),
86}
87
88fn parse_fragment<'i>(input: &mut Span<'i>) -> PResult<Option<StringFragment<'i>>> {
89 trace(
90 "parse_fragment",
91 alt((
92 parse_literal.map(|span| Some(StringFragment::Literal(span))),
93 parse_escaped_char.map(|res| res.map(StringFragment::EscapedChar)),
94 )),
95 )
96 .parse_next(input)
97}
98
99pub(super) fn parse_string(input: &mut Span<'_>) -> PResult<Option<String>> {
103 trace(
104 "parse_string",
105 repeat(0.., parse_fragment).fold(
106 || Some(String::new()),
107 |string, fragment| {
108 match (string, fragment) {
109 (Some(mut string), Some(StringFragment::Literal(s))) => {
110 string.push_str(s);
111 Some(string)
112 }
113 (Some(mut string), Some(StringFragment::EscapedChar(c))) => {
114 string.push(c);
115 Some(string)
116 }
117 (Some(_), None) => {
118 None
121 }
122 (None, _) => None,
123 }
124 },
125 ),
126 )
127 .parse_next(input)
128}