darklua_core/nodes/expressions/
interpolated_string.rs

1use std::iter::FromIterator;
2
3use crate::nodes::{IntoLuaStringValue, StringError, Token, Trivia};
4
5use super::{string_utils, Expression};
6
7/// Represents a string segment in an interpolated string.
8///
9/// String segments are the literal text parts of an interpolated string,
10/// appearing between expression segments.
11#[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    /// Creates a new string segment from a string value, processing escape sequences.
45    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    /// Creates a new string segment from a string value without processing escapes.
52    pub fn from_value(value: impl IntoLuaStringValue) -> Self {
53        Self {
54            value: value.into_lua_string_value(),
55            token: None,
56        }
57    }
58
59    /// Attaches a token to this string segment and returns the updated segment.
60    pub fn with_token(mut self, token: Token) -> Self {
61        self.token = Some(token);
62        self
63    }
64
65    /// Attaches a token to this string segment.
66    pub fn set_token(&mut self, token: Token) {
67        self.token = Some(token);
68    }
69
70    /// Returns a reference to the token attached to this string segment, if any.
71    pub fn get_token(&self) -> Option<&Token> {
72        self.token.as_ref()
73    }
74
75    /// Returns the string value of this segment.
76    pub fn get_value(&self) -> &[u8] {
77        &self.value
78    }
79
80    /// Returns the string value if it is valid UTF-8.
81    #[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    /// Returns the length of the string value in this segment.
92    #[inline]
93    pub fn len(&self) -> usize {
94        self.value.len()
95    }
96
97    /// Returns whether the string value in this segment is empty.
98    #[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/// Represents an expression segment in an interpolated string.
107///
108/// Value segments contain expressions that are evaluated and converted to strings
109/// when the interpolated string is evaluated.
110#[derive(Clone, Debug, PartialEq, Eq)]
111pub struct ValueSegment {
112    value: Box<Expression>,
113    tokens: Option<ValueSegmentTokens>,
114}
115
116impl ValueSegment {
117    /// Creates a new value segment with the given expression.
118    pub fn new(value: impl Into<Expression>) -> Self {
119        Self {
120            value: Box::new(value.into()),
121            tokens: None,
122        }
123    }
124
125    /// Attaches tokens to this value segment.
126    pub fn with_tokens(mut self, tokens: ValueSegmentTokens) -> Self {
127        self.tokens = Some(tokens);
128        self
129    }
130
131    /// Attaches tokens to this value segment.
132    pub fn set_tokens(&mut self, tokens: ValueSegmentTokens) {
133        self.tokens = Some(tokens);
134    }
135
136    /// Returns a reference to the tokens attached to this value segment, if any.
137    pub fn get_tokens(&self) -> Option<&ValueSegmentTokens> {
138        self.tokens.as_ref()
139    }
140
141    /// Returns a reference to the expression in this value segment.
142    pub fn get_expression(&self) -> &Expression {
143        &self.value
144    }
145
146    /// Returns a mutable reference to the expression in this value segment.
147    pub fn mutate_expression(&mut self) -> &mut Expression {
148        &mut self.value
149    }
150
151    super::impl_token_fns!(iter = [tokens]);
152}
153
154/// Contains token information for a value segment in an interpolated string.
155#[derive(Clone, Debug, PartialEq, Eq)]
156pub struct ValueSegmentTokens {
157    /// The opening brace token (`{`)
158    pub opening_brace: Token,
159    /// The closing brace token (`}`)
160    pub closing_brace: Token,
161}
162
163impl ValueSegmentTokens {
164    super::impl_token_fns!(target = [opening_brace, closing_brace]);
165}
166
167/// Represents a segment in an interpolated string.
168#[derive(Clone, Debug, PartialEq, Eq)]
169pub enum InterpolationSegment {
170    /// A literal string segment
171    String(StringSegment),
172    /// An expression value segment
173    Value(ValueSegment),
174}
175
176impl InterpolationSegment {
177    /// Clears all comments from the tokens in this node.
178    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    /// Clears all whitespaces information from the tokens in this node.
186    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/// Represents an interpolated string expression.
252#[derive(Clone, Debug, PartialEq, Eq)]
253pub struct InterpolatedStringExpression {
254    segments: Vec<InterpolationSegment>,
255    tokens: Option<InterpolatedStringTokens>,
256}
257
258impl InterpolatedStringExpression {
259    /// Creates a new interpolated string expression with the given segments.
260    pub fn new(segments: Vec<InterpolationSegment>) -> Self {
261        Self {
262            segments,
263            tokens: None,
264        }
265    }
266
267    /// Creates an empty interpolated string expression with no segments.
268    pub fn empty() -> Self {
269        Self::new(Vec::default())
270    }
271
272    /// Adds a segment to this interpolated string expression and returns the updated expression.
273    pub fn with_segment(mut self, segment: impl Into<InterpolationSegment>) -> Self {
274        self.push_segment(segment);
275        self
276    }
277
278    /// Attaches tokens to this interpolated string expression.
279    pub fn with_tokens(mut self, tokens: InterpolatedStringTokens) -> Self {
280        self.tokens = Some(tokens);
281        self
282    }
283
284    /// Returns a reference to the tokens attached to this interpolated string expression, if any.
285    pub fn get_tokens(&self) -> Option<&InterpolatedStringTokens> {
286        self.tokens.as_ref()
287    }
288
289    /// Attaches tokens to this interpolated string expression.
290    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    /// Returns an iterator over the segments in this interpolated string expression.
297    pub fn iter_segments(&self) -> impl Iterator<Item = &InterpolationSegment> {
298        self.segments.iter()
299    }
300
301    /// Returns a mutable iterator over the segments in this interpolated string expression.
302    pub fn iter_mut_segments(&mut self) -> impl Iterator<Item = &mut InterpolationSegment> {
303        self.segments.iter_mut()
304    }
305
306    /// Returns the number of segments in this interpolated string expression.
307    #[inline]
308    pub fn len(&self) -> usize {
309        self.segments.len()
310    }
311
312    /// Returns whether this interpolated string expression is empty.
313    ///
314    /// An interpolated string is considered empty if it has no segments or all of its
315    /// string segments are empty and it has no value segments.
316    #[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    /// Adds a segment to this interpolated string expression.
325    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    /// Returns a mutable reference to the first token for this interpolated string,
345    /// creating it if missing.
346    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    /// Returns a mutable reference to the last token for this interpolated string,
352    /// creating it if missing.
353    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/// Contains token information for an interpolated string expression.
378#[derive(Clone, Debug, PartialEq, Eq)]
379pub struct InterpolatedStringTokens {
380    /// The opening backtick token
381    pub opening_tick: Token,
382    /// The closing backtick token
383    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}