darklua_core/nodes/expressions/
interpolated_string.rs

1use std::iter::FromIterator;
2
3use crate::nodes::{StringError, Token, Trivia};
4
5use super::{string_utils, Expression};
6
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct StringSegment {
9    value: String,
10    token: Option<Token>,
11}
12
13impl StringSegment {
14    pub fn new(value: impl AsRef<str>) -> Result<Self, StringError> {
15        string_utils::read_escaped_string(value.as_ref().char_indices(), None).map(Self::from_value)
16    }
17
18    pub fn from_value(value: impl Into<String>) -> Self {
19        Self {
20            value: value.into(),
21            token: None,
22        }
23    }
24
25    pub fn with_token(mut self, token: Token) -> Self {
26        self.token = Some(token);
27        self
28    }
29
30    pub fn set_token(&mut self, token: Token) {
31        self.token = Some(token);
32    }
33
34    pub fn get_token(&self) -> Option<&Token> {
35        self.token.as_ref()
36    }
37
38    pub fn get_value(&self) -> &str {
39        self.value.as_str()
40    }
41
42    fn append(&mut self, mut other: Self) {
43        self.value.extend(other.value.drain(..));
44        self.token = None;
45    }
46
47    #[inline]
48    pub fn len(&self) -> usize {
49        self.value.len()
50    }
51
52    #[inline]
53    pub fn is_empty(&self) -> bool {
54        self.value.is_empty()
55    }
56
57    super::impl_token_fns!(iter = [token]);
58}
59
60#[derive(Clone, Debug, PartialEq, Eq)]
61pub struct ValueSegment {
62    value: Expression,
63    tokens: Option<ValueSegmentTokens>,
64}
65
66impl ValueSegment {
67    pub fn new(value: impl Into<Expression>) -> Self {
68        Self {
69            value: value.into(),
70            tokens: None,
71        }
72    }
73
74    pub fn with_tokens(mut self, tokens: ValueSegmentTokens) -> Self {
75        self.tokens = Some(tokens);
76        self
77    }
78
79    pub fn set_tokens(&mut self, tokens: ValueSegmentTokens) {
80        self.tokens = Some(tokens);
81    }
82
83    pub fn get_tokens(&self) -> Option<&ValueSegmentTokens> {
84        self.tokens.as_ref()
85    }
86
87    pub fn get_expression(&self) -> &Expression {
88        &self.value
89    }
90
91    pub fn mutate_expression(&mut self) -> &mut Expression {
92        &mut self.value
93    }
94
95    super::impl_token_fns!(iter = [tokens]);
96}
97
98#[derive(Clone, Debug, PartialEq, Eq)]
99pub struct ValueSegmentTokens {
100    pub opening_brace: Token,
101    pub closing_brace: Token,
102}
103
104impl ValueSegmentTokens {
105    super::impl_token_fns!(target = [opening_brace, closing_brace]);
106}
107
108#[derive(Clone, Debug, PartialEq, Eq)]
109pub enum InterpolationSegment {
110    String(StringSegment),
111    Value(ValueSegment),
112}
113
114impl InterpolationSegment {
115    pub fn clear_comments(&mut self) {
116        match self {
117            InterpolationSegment::String(segment) => segment.clear_comments(),
118            InterpolationSegment::Value(segment) => segment.clear_comments(),
119        }
120    }
121
122    pub fn clear_whitespaces(&mut self) {
123        match self {
124            InterpolationSegment::String(segment) => segment.clear_whitespaces(),
125            InterpolationSegment::Value(segment) => segment.clear_whitespaces(),
126        }
127    }
128
129    pub(crate) fn replace_referenced_tokens(&mut self, code: &str) {
130        match self {
131            InterpolationSegment::String(segment) => segment.replace_referenced_tokens(code),
132            InterpolationSegment::Value(segment) => segment.replace_referenced_tokens(code),
133        }
134    }
135
136    pub(crate) fn shift_token_line(&mut self, amount: usize) {
137        match self {
138            InterpolationSegment::String(segment) => segment.shift_token_line(amount),
139            InterpolationSegment::Value(segment) => segment.shift_token_line(amount),
140        }
141    }
142
143    pub(crate) fn filter_comments(&mut self, filter: impl Fn(&Trivia) -> bool) {
144        match self {
145            InterpolationSegment::String(segment) => segment.filter_comments(filter),
146            InterpolationSegment::Value(segment) => segment.filter_comments(filter),
147        }
148    }
149}
150
151impl From<StringSegment> for InterpolationSegment {
152    fn from(segment: StringSegment) -> Self {
153        Self::String(segment)
154    }
155}
156
157impl From<ValueSegment> for InterpolationSegment {
158    fn from(segment: ValueSegment) -> Self {
159        Self::Value(segment)
160    }
161}
162
163impl<T: Into<Expression>> From<T> for InterpolationSegment {
164    fn from(value: T) -> Self {
165        Self::Value(ValueSegment::new(value.into()))
166    }
167}
168
169impl From<&str> for InterpolationSegment {
170    fn from(string: &str) -> Self {
171        Self::String(StringSegment::from_value(string))
172    }
173}
174
175impl From<&String> for InterpolationSegment {
176    fn from(string: &String) -> Self {
177        Self::String(StringSegment::from_value(string))
178    }
179}
180
181impl From<String> for InterpolationSegment {
182    fn from(string: String) -> Self {
183        Self::String(StringSegment::from_value(string))
184    }
185}
186
187#[derive(Clone, Debug, PartialEq, Eq)]
188pub struct InterpolatedStringExpression {
189    segments: Vec<InterpolationSegment>,
190    tokens: Option<InterpolatedStringTokens>,
191}
192
193impl InterpolatedStringExpression {
194    pub fn new(segments: Vec<InterpolationSegment>) -> Self {
195        Self {
196            segments,
197            tokens: None,
198        }
199    }
200
201    pub fn empty() -> Self {
202        Self::new(Vec::default())
203    }
204
205    pub fn with_segment(mut self, segment: impl Into<InterpolationSegment>) -> Self {
206        self.push_segment(segment);
207        self
208    }
209
210    pub fn with_tokens(mut self, tokens: InterpolatedStringTokens) -> Self {
211        self.tokens = Some(tokens);
212        self
213    }
214
215    pub fn get_tokens(&self) -> Option<&InterpolatedStringTokens> {
216        self.tokens.as_ref()
217    }
218
219    pub fn set_tokens(&mut self, tokens: InterpolatedStringTokens) {
220        self.tokens = Some(tokens);
221    }
222
223    super::impl_token_fns!(iter = [tokens, segments]);
224
225    pub fn iter_segments(&self) -> impl Iterator<Item = &InterpolationSegment> {
226        self.segments.iter()
227    }
228
229    pub fn iter_mut_segments(&mut self) -> impl Iterator<Item = &mut InterpolationSegment> {
230        self.segments.iter_mut()
231    }
232
233    #[inline]
234    pub fn len(&self) -> usize {
235        self.segments.len()
236    }
237
238    #[inline]
239    pub fn is_empty(&self) -> bool {
240        self.segments.iter().all(|segment| match segment {
241            InterpolationSegment::String(string_segment) => string_segment.is_empty(),
242            InterpolationSegment::Value(_) => false,
243        })
244    }
245
246    pub fn push_segment(&mut self, segment: impl Into<InterpolationSegment>) {
247        let new_segment = segment.into();
248        match new_segment {
249            InterpolationSegment::String(string_segment) => {
250                if string_segment.get_value().is_empty() {
251                    return;
252                }
253                if let Some(InterpolationSegment::String(last)) = self.segments.last_mut() {
254                    last.append(string_segment);
255                } else {
256                    self.segments.push(string_segment.into());
257                }
258            }
259            InterpolationSegment::Value(_) => {
260                self.segments.push(new_segment);
261            }
262        }
263    }
264}
265
266impl FromIterator<InterpolationSegment> for InterpolatedStringExpression {
267    fn from_iter<T: IntoIterator<Item = InterpolationSegment>>(iter: T) -> Self {
268        Self {
269            segments: iter.into_iter().collect(),
270            tokens: None,
271        }
272    }
273}
274
275#[derive(Clone, Debug, PartialEq, Eq)]
276pub struct InterpolatedStringTokens {
277    pub opening_tick: Token,
278    pub closing_tick: Token,
279}
280
281impl InterpolatedStringTokens {
282    super::impl_token_fns!(target = [opening_tick, closing_tick]);
283}
284
285#[cfg(test)]
286mod test {
287    use super::*;
288
289    #[test]
290    fn push_segment_with_empty_string_does_not_mutate() {
291        let mut string = InterpolatedStringExpression::empty();
292        string.push_segment("");
293
294        pretty_assertions::assert_eq!(string, InterpolatedStringExpression::empty());
295    }
296}