oni_comb_parser/text/
quoted_string_cow.rs1use alloc::borrow::Cow;
2use alloc::string::String;
3
4use crate::error::ParseError;
5use crate::fail::{Fail, PResult};
6use crate::input::Input;
7use crate::parser::Parser;
8use crate::str_input::StrInput;
9
10pub struct QuotedStringCow;
13
14pub fn quoted_string_cow() -> QuotedStringCow {
15 QuotedStringCow
16}
17
18impl<'a> Parser<StrInput<'a>> for QuotedStringCow {
19 type Error = ParseError;
20 type Output = Cow<'a, str>;
21
22 #[inline]
23 fn parse_next(&mut self, input: &mut StrInput<'a>) -> PResult<Cow<'a, str>, ParseError> {
24 let pos = input.offset();
25 let remaining = input.as_str();
26 let bytes = remaining.as_bytes();
27
28 if bytes.is_empty() || bytes[0] != b'"' {
29 return Err(Fail::Backtrack(ParseError::expected_char(pos, '"')));
30 }
31
32 let mut i = 1; loop {
35 if i >= bytes.len() {
36 return Err(Fail::Cut(ParseError::expected_char(pos + i, '"')));
38 }
39 match bytes[i] {
40 b'"' => {
41 let s = &remaining[1..i];
43 input.advance(i + 1);
44 return Ok(Cow::Borrowed(s));
45 }
46 b'\\' => {
47 break;
49 }
50 _ => {
51 i += 1;
52 }
53 }
54 }
55
56 let mut result = String::with_capacity(i + 16);
58 result.push_str(&remaining[1..i]);
59
60 let mut chars = remaining[i..].chars();
61 let mut consumed = i; loop {
64 match chars.next() {
65 Some('"') => {
66 consumed += 1;
67 input.advance(consumed);
68 return Ok(Cow::Owned(result));
69 }
70 Some('\\') => {
71 consumed += 1;
72 match chars.next() {
73 Some('"') => {
74 consumed += 1;
75 result.push('"');
76 }
77 Some('\\') => {
78 consumed += 1;
79 result.push('\\');
80 }
81 Some('/') => {
82 consumed += 1;
83 result.push('/');
84 }
85 Some('b') => {
86 consumed += 1;
87 result.push('\u{0008}');
88 }
89 Some('f') => {
90 consumed += 1;
91 result.push('\u{000C}');
92 }
93 Some('n') => {
94 consumed += 1;
95 result.push('\n');
96 }
97 Some('r') => {
98 consumed += 1;
99 result.push('\r');
100 }
101 Some('t') => {
102 consumed += 1;
103 result.push('\t');
104 }
105 Some('u') => {
106 consumed += 1;
107 let mut code: u32 = 0;
108 for _ in 0..4 {
109 match chars.next() {
110 Some(c) if c.is_ascii_hexdigit() => {
111 consumed += 1;
112 code = code * 16 + c.to_digit(16).unwrap();
113 }
114 _ => {
115 return Err(Fail::Cut(ParseError::expected_description(
116 pos + consumed,
117 "4 hex digits after \\u",
118 )));
119 }
120 }
121 }
122 match char::from_u32(code) {
123 Some(c) => result.push(c),
124 None => {
125 return Err(Fail::Cut(ParseError::expected_description(
126 pos + consumed - 4,
127 "valid unicode code point",
128 )));
129 }
130 }
131 }
132 Some(_) => {
133 return Err(Fail::Cut(ParseError::expected_description(
134 pos + consumed,
135 "valid escape sequence",
136 )));
137 }
138 None => {
139 return Err(Fail::Cut(ParseError::expected_description(
140 pos + consumed,
141 "escape character after '\\'",
142 )));
143 }
144 }
145 }
146 Some(c) => {
147 consumed += c.len_utf8();
148 result.push(c);
149 }
150 None => {
151 return Err(Fail::Cut(ParseError::expected_char(pos + consumed, '"')));
152 }
153 }
154 }
155 }
156}