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 ')'.value(')'),
37 ','.value(','),
38 ));
39 preceded(
40 '\\',
41 expect_n(
43 valid,
44 ParseSingleError::InvalidEscapeCharacter,
45 SpanLength::Offset(-1, 2),
47 ),
48 )
49 .parse_next(input)
50 })
51 .parse_next(input)
52}
53
54pub(crate) struct DisplayParsedString<'a>(pub(crate) &'a str);
56
57impl fmt::Display for DisplayParsedString<'_> {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 for c in self.0.chars() {
60 match c {
61 '/' => f.write_str("\\/")?,
63 ')' => f.write_str("\\)")?,
64 ',' => f.write_str("\\,")?,
65 c => write!(f, "{}", c.escape_default())?,
67 }
68 }
69 Ok(())
70 }
71}
72fn parse_literal<'i>(input: &mut Span<'i>) -> PResult<&'i str> {
73 trace("parse_literal", |input: &mut _| {
74 let not_quote_slash = take_till(1.., (',', ')', '\\'));
75
76 not_quote_slash
77 .verify(|s: &str| !s.is_empty())
78 .parse_next(input)
79 })
80 .parse_next(input)
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84enum StringFragment<'a> {
85 Literal(&'a str),
86 EscapedChar(char),
87}
88
89fn parse_fragment<'i>(input: &mut Span<'i>) -> PResult<Option<StringFragment<'i>>> {
90 trace(
91 "parse_fragment",
92 alt((
93 parse_literal.map(|span| Some(StringFragment::Literal(span))),
94 parse_escaped_char.map(|res| res.map(StringFragment::EscapedChar)),
95 )),
96 )
97 .parse_next(input)
98}
99
100pub(super) fn parse_string(input: &mut Span<'_>) -> PResult<Option<String>> {
104 trace(
105 "parse_string",
106 repeat(0.., parse_fragment).fold(
107 || Some(String::new()),
108 |string, fragment| {
109 match (string, fragment) {
110 (Some(mut string), Some(StringFragment::Literal(s))) => {
111 string.push_str(s);
112 Some(string)
113 }
114 (Some(mut string), Some(StringFragment::EscapedChar(c))) => {
115 string.push(c);
116 Some(string)
117 }
118 (Some(_), None) => {
119 None
122 }
123 (None, _) => None,
124 }
125 },
126 ),
127 )
128 .parse_next(input)
129}