darklua_core/nodes/expressions/
interpolated_string.rs1use std::iter::FromIterator;
2
3use crate::nodes::{IntoLuaStringValue, StringError, Token, Trivia};
4
5use super::{string_utils, Expression};
6
7#[derive(Clone, PartialEq, Eq)]
12pub struct StringSegment {
13 value: Vec<u8>,
14 token: Option<Token>,
15}
16
17impl std::fmt::Debug for StringSegment {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 f.debug_struct("StringSegment")
20 .field("token", &self.token)
21 .field("value", &{
22 if let Ok(s) = str::from_utf8(&self.value) {
23 format!("{:?}", s)
24 } else {
25 let escaped = self
26 .value
27 .iter()
28 .flat_map(|&b| {
29 if b <= 0x7f {
30 vec![b as char]
31 } else {
32 format!("\\x{:02x}", b).chars().collect()
33 }
34 })
35 .collect::<String>();
36 format!("{:?}", escaped)
37 }
38 })
39 .finish()
40 }
41}
42
43impl StringSegment {
44 pub fn new(value: impl AsRef<str>) -> Result<Self, StringError> {
46 let value = value.as_ref();
47 string_utils::read_escaped_string(value.char_indices(), Some(value.len()))
48 .map(Self::from_value)
49 }
50
51 pub fn from_value(value: impl IntoLuaStringValue) -> Self {
53 Self {
54 value: value.into_lua_string_value(),
55 token: None,
56 }
57 }
58
59 pub fn with_token(mut self, token: Token) -> Self {
61 self.token = Some(token);
62 self
63 }
64
65 pub fn set_token(&mut self, token: Token) {
67 self.token = Some(token);
68 }
69
70 pub fn get_token(&self) -> Option<&Token> {
72 self.token.as_ref()
73 }
74
75 pub fn get_value(&self) -> &[u8] {
77 &self.value
78 }
79
80 #[inline]
82 pub fn get_string_value(&self) -> Option<&str> {
83 str::from_utf8(&self.value).ok()
84 }
85
86 fn append(&mut self, mut other: Self) {
87 self.value.append(&mut other.value);
88 self.token = None;
89 }
90
91 #[inline]
93 pub fn len(&self) -> usize {
94 self.value.len()
95 }
96
97 #[inline]
99 pub fn is_empty(&self) -> bool {
100 self.value.is_empty()
101 }
102
103 super::impl_token_fns!(iter = [token]);
104}
105
106#[derive(Clone, Debug, PartialEq, Eq)]
111pub struct ValueSegment {
112 value: Box<Expression>,
113 tokens: Option<ValueSegmentTokens>,
114}
115
116impl ValueSegment {
117 pub fn new(value: impl Into<Expression>) -> Self {
119 Self {
120 value: Box::new(value.into()),
121 tokens: None,
122 }
123 }
124
125 pub fn with_tokens(mut self, tokens: ValueSegmentTokens) -> Self {
127 self.tokens = Some(tokens);
128 self
129 }
130
131 pub fn set_tokens(&mut self, tokens: ValueSegmentTokens) {
133 self.tokens = Some(tokens);
134 }
135
136 pub fn get_tokens(&self) -> Option<&ValueSegmentTokens> {
138 self.tokens.as_ref()
139 }
140
141 pub fn get_expression(&self) -> &Expression {
143 &self.value
144 }
145
146 pub fn mutate_expression(&mut self) -> &mut Expression {
148 &mut self.value
149 }
150
151 super::impl_token_fns!(iter = [tokens]);
152}
153
154#[derive(Clone, Debug, PartialEq, Eq)]
156pub struct ValueSegmentTokens {
157 pub opening_brace: Token,
159 pub closing_brace: Token,
161}
162
163impl ValueSegmentTokens {
164 super::impl_token_fns!(target = [opening_brace, closing_brace]);
165}
166
167#[derive(Clone, Debug, PartialEq, Eq)]
169pub enum InterpolationSegment {
170 String(StringSegment),
172 Value(ValueSegment),
174}
175
176impl InterpolationSegment {
177 pub fn clear_comments(&mut self) {
179 match self {
180 InterpolationSegment::String(segment) => segment.clear_comments(),
181 InterpolationSegment::Value(segment) => segment.clear_comments(),
182 }
183 }
184
185 pub fn clear_whitespaces(&mut self) {
187 match self {
188 InterpolationSegment::String(segment) => segment.clear_whitespaces(),
189 InterpolationSegment::Value(segment) => segment.clear_whitespaces(),
190 }
191 }
192
193 pub(crate) fn replace_referenced_tokens(&mut self, code: &str) {
194 match self {
195 InterpolationSegment::String(segment) => segment.replace_referenced_tokens(code),
196 InterpolationSegment::Value(segment) => segment.replace_referenced_tokens(code),
197 }
198 }
199
200 pub(crate) fn shift_token_line(&mut self, amount: isize) {
201 match self {
202 InterpolationSegment::String(segment) => segment.shift_token_line(amount),
203 InterpolationSegment::Value(segment) => segment.shift_token_line(amount),
204 }
205 }
206
207 pub(crate) fn filter_comments(&mut self, filter: impl Fn(&Trivia) -> bool) {
208 match self {
209 InterpolationSegment::String(segment) => segment.filter_comments(filter),
210 InterpolationSegment::Value(segment) => segment.filter_comments(filter),
211 }
212 }
213}
214
215impl From<StringSegment> for InterpolationSegment {
216 fn from(segment: StringSegment) -> Self {
217 Self::String(segment)
218 }
219}
220
221impl From<ValueSegment> for InterpolationSegment {
222 fn from(segment: ValueSegment) -> Self {
223 Self::Value(segment)
224 }
225}
226
227impl<T: Into<Expression>> From<T> for InterpolationSegment {
228 fn from(value: T) -> Self {
229 Self::Value(ValueSegment::new(value.into()))
230 }
231}
232
233impl From<&str> for InterpolationSegment {
234 fn from(string: &str) -> Self {
235 Self::String(StringSegment::from_value(string))
236 }
237}
238
239impl From<&String> for InterpolationSegment {
240 fn from(string: &String) -> Self {
241 Self::String(StringSegment::from_value(string))
242 }
243}
244
245impl From<String> for InterpolationSegment {
246 fn from(string: String) -> Self {
247 Self::String(StringSegment::from_value(string))
248 }
249}
250
251#[derive(Clone, Debug, PartialEq, Eq)]
253pub struct InterpolatedStringExpression {
254 segments: Vec<InterpolationSegment>,
255 tokens: Option<InterpolatedStringTokens>,
256}
257
258impl InterpolatedStringExpression {
259 pub fn new(segments: Vec<InterpolationSegment>) -> Self {
261 Self {
262 segments,
263 tokens: None,
264 }
265 }
266
267 pub fn empty() -> Self {
269 Self::new(Vec::default())
270 }
271
272 pub fn with_segment(mut self, segment: impl Into<InterpolationSegment>) -> Self {
274 self.push_segment(segment);
275 self
276 }
277
278 pub fn with_tokens(mut self, tokens: InterpolatedStringTokens) -> Self {
280 self.tokens = Some(tokens);
281 self
282 }
283
284 pub fn get_tokens(&self) -> Option<&InterpolatedStringTokens> {
286 self.tokens.as_ref()
287 }
288
289 pub fn set_tokens(&mut self, tokens: InterpolatedStringTokens) {
291 self.tokens = Some(tokens);
292 }
293
294 super::impl_token_fns!(iter = [tokens, segments]);
295
296 pub fn iter_segments(&self) -> impl Iterator<Item = &InterpolationSegment> {
298 self.segments.iter()
299 }
300
301 pub fn iter_mut_segments(&mut self) -> impl Iterator<Item = &mut InterpolationSegment> {
303 self.segments.iter_mut()
304 }
305
306 #[inline]
308 pub fn len(&self) -> usize {
309 self.segments.len()
310 }
311
312 #[inline]
317 pub fn is_empty(&self) -> bool {
318 self.segments.iter().all(|segment| match segment {
319 InterpolationSegment::String(string_segment) => string_segment.is_empty(),
320 InterpolationSegment::Value(_) => false,
321 })
322 }
323
324 pub fn push_segment(&mut self, segment: impl Into<InterpolationSegment>) {
326 let new_segment = segment.into();
327 match new_segment {
328 InterpolationSegment::String(string_segment) => {
329 if string_segment.get_value().is_empty() {
330 return;
331 }
332 if let Some(InterpolationSegment::String(last)) = self.segments.last_mut() {
333 last.append(string_segment);
334 } else {
335 self.segments.push(string_segment.into());
336 }
337 }
338 InterpolationSegment::Value(_) => {
339 self.segments.push(new_segment);
340 }
341 }
342 }
343
344 pub fn mutate_first_token(&mut self) -> &mut Token {
347 self.set_default_tokens();
348 &mut self.tokens.as_mut().unwrap().opening_tick
349 }
350
351 pub fn mutate_last_token(&mut self) -> &mut Token {
354 self.set_default_tokens();
355 &mut self.tokens.as_mut().unwrap().closing_tick
356 }
357
358 fn set_default_tokens(&mut self) {
359 if self.tokens.is_none() {
360 self.set_tokens(InterpolatedStringTokens {
361 opening_tick: Token::from_content("`"),
362 closing_tick: Token::from_content("`"),
363 });
364 }
365 }
366}
367
368impl FromIterator<InterpolationSegment> for InterpolatedStringExpression {
369 fn from_iter<T: IntoIterator<Item = InterpolationSegment>>(iter: T) -> Self {
370 Self {
371 segments: iter.into_iter().collect(),
372 tokens: None,
373 }
374 }
375}
376
377#[derive(Clone, Debug, PartialEq, Eq)]
379pub struct InterpolatedStringTokens {
380 pub opening_tick: Token,
382 pub closing_tick: Token,
384}
385
386impl InterpolatedStringTokens {
387 super::impl_token_fns!(target = [opening_tick, closing_tick]);
388}
389
390#[cfg(test)]
391mod test {
392 use super::*;
393
394 #[test]
395 fn push_segment_with_empty_string_does_not_mutate() {
396 let mut string = InterpolatedStringExpression::empty();
397 string.push_segment("");
398
399 pretty_assertions::assert_eq!(string, InterpolatedStringExpression::empty());
400 }
401}