1use crate::parse::{parse_decimal, parse_id};
2use crate::{Captures, CompileError, Error, ParseError, Regex};
3use std::borrow::Cow;
4use std::io;
5use std::mem;
6
7#[derive(Debug)]
10pub struct Expander {
11 sub_char: char,
12 open: &'static str,
13 close: &'static str,
14 allow_undelimited_name: bool,
15}
16
17impl Default for Expander {
18 fn default() -> Self {
22 Expander {
23 sub_char: '$',
24 open: "{",
25 close: "}",
26 allow_undelimited_name: true,
27 }
28 }
29}
30
31impl Expander {
32 pub fn python() -> Expander {
54 Expander {
55 sub_char: '\\',
56 open: "g<",
57 close: ">",
58 allow_undelimited_name: false,
59 }
60 }
61
62 pub fn check(&self, template: &str, regex: &Regex) -> crate::Result<()> {
70 let on_group_num = |num| {
71 if num == 0 {
72 Ok(())
73 } else if !regex.named_groups.is_empty() {
74 Err(Error::CompileError(CompileError::NamedBackrefOnly))
75 } else if num < regex.captures_len() {
76 Ok(())
77 } else {
78 Err(Error::CompileError(CompileError::InvalidBackref))
79 }
80 };
81 self.exec(template, |step| match step {
82 Step::Char(_) => Ok(()),
83 Step::GroupName(name) => {
84 if regex.named_groups.contains_key(name) {
85 Ok(())
86 } else if let Ok(num) = name.parse() {
87 on_group_num(num)
88 } else {
89 Err(Error::CompileError(CompileError::InvalidBackref))
90 }
91 }
92 Step::GroupNum(num) => on_group_num(num),
93 Step::Error => Err(Error::ParseError(
94 0,
95 ParseError::GeneralParseError(
96 "parse error in template while expanding".to_string(),
97 ),
98 )),
99 })
100 }
101
102 pub fn escape<'a>(&self, text: &'a str) -> Cow<'a, str> {
112 if text.contains(self.sub_char) {
113 let mut quoted = String::with_capacity(self.sub_char.len_utf8() * 2);
114 quoted.push(self.sub_char);
115 quoted.push(self.sub_char);
116 Cow::Owned(text.replace(self.sub_char, "ed))
117 } else {
118 Cow::Borrowed(text)
119 }
120 }
121
122 #[doc(hidden)]
123 #[deprecated(since = "0.4.0", note = "Use `escape` instead.")]
124 pub fn quote<'a>(&self, text: &'a str) -> Cow<'a, str> {
125 self.escape(text)
126 }
127
128 pub fn expansion(&self, template: &str, captures: &Captures<'_>) -> String {
131 let mut cursor = io::Cursor::new(Vec::with_capacity(template.len()));
132 self.write_expansion(&mut cursor, template, captures)
133 .expect("expansion succeeded");
134 String::from_utf8(cursor.into_inner()).expect("expansion is UTF-8")
135 }
136
137 pub fn append_expansion(&self, dst: &mut String, template: &str, captures: &Captures<'_>) {
140 let pos = dst.len();
141 let mut cursor = io::Cursor::new(mem::replace(dst, String::new()).into_bytes());
142 cursor.set_position(pos as u64);
143 self.write_expansion(&mut cursor, template, captures)
144 .expect("expansion succeeded");
145 *dst = String::from_utf8(cursor.into_inner()).expect("expansion is UTF-8");
146 }
147
148 pub fn write_expansion(
151 &self,
152 mut dst: impl io::Write,
153 template: &str,
154 captures: &Captures<'_>,
155 ) -> io::Result<()> {
156 self.exec(template, |step| match step {
157 Step::Char(c) => write!(dst, "{}", c),
158 Step::GroupName(name) => {
159 if let Some(m) = captures.name(name) {
160 write!(dst, "{}", m.as_str())
161 } else if let Some(m) = name.parse().ok().and_then(|num| captures.get(num)) {
162 write!(dst, "{}", m.as_str())
163 } else {
164 Ok(())
165 }
166 }
167 Step::GroupNum(num) => {
168 if let Some(m) = captures.get(num) {
169 write!(dst, "{}", m.as_str())
170 } else {
171 Ok(())
172 }
173 }
174 Step::Error => Ok(()),
175 })
176 }
177
178 fn exec<'t, E>(
179 &self,
180 template: &'t str,
181 mut f: impl FnMut(Step<'t>) -> Result<(), E>,
182 ) -> Result<(), E> {
183 debug_assert!(!self.open.is_empty());
184 debug_assert!(!self.close.is_empty());
185 let mut iter = template.chars();
186 while let Some(c) = iter.next() {
187 if c == self.sub_char {
188 let tail = iter.as_str();
189 let skip = if tail.starts_with(self.sub_char) {
190 f(Step::Char(self.sub_char))?;
191 1
192 } else if let Some((id, skip)) =
193 parse_id(tail, self.open, self.close).or_else(|| {
194 if self.allow_undelimited_name {
195 parse_id(tail, "", "")
196 } else {
197 None
198 }
199 })
200 {
201 f(Step::GroupName(id))?;
202 skip
203 } else if let Some((skip, num)) = parse_decimal(tail, 0) {
204 f(Step::GroupNum(num))?;
205 skip
206 } else {
207 f(Step::Error)?;
208 f(Step::Char(self.sub_char))?;
209 0
210 };
211 iter = iter.as_str()[skip..].chars();
212 } else {
213 f(Step::Char(c))?;
214 }
215 }
216 Ok(())
217 }
218}
219
220enum Step<'a> {
221 Char(char),
222 GroupName(&'a str),
223 GroupNum(usize),
224 Error,
225}