darklua_core/nodes/
arguments.rs

1use std::{iter, mem};
2
3use crate::nodes::{Expression, StringExpression, TableExpression, Token};
4
5/// Tokens associated with tuple arguments.
6#[derive(Clone, Debug, PartialEq, Eq)]
7pub struct TupleArgumentsTokens {
8    pub opening_parenthese: Token,
9    pub closing_parenthese: Token,
10    pub commas: Vec<Token>,
11}
12
13impl TupleArgumentsTokens {
14    super::impl_token_fns!(
15        target = [opening_parenthese, closing_parenthese]
16        iter = [commas]
17    );
18}
19
20/// Represents a list of arguments enclosed in parentheses.
21#[derive(Clone, Debug, Default, PartialEq, Eq)]
22pub struct TupleArguments {
23    values: Vec<Expression>,
24    tokens: Option<TupleArgumentsTokens>,
25}
26
27impl TupleArguments {
28    /// Creates a new tuple of arguments with the given expressions.
29    pub fn new(values: Vec<Expression>) -> Self {
30        Self {
31            values,
32            tokens: None,
33        }
34    }
35
36    /// Converts this tuple into expressions.
37    #[inline]
38    pub fn to_expressions(self) -> Vec<Expression> {
39        self.values
40    }
41
42    /// Sets the tokens for this tuple.
43    pub fn with_tokens(mut self, tokens: TupleArgumentsTokens) -> Self {
44        self.tokens = Some(tokens);
45        self
46    }
47
48    /// Sets the tokens for this tuple.
49    #[inline]
50    pub fn set_tokens(&mut self, tokens: TupleArgumentsTokens) {
51        self.tokens = Some(tokens);
52    }
53
54    /// Returns the tokens for this tuple, if any.
55    #[inline]
56    pub fn get_tokens(&self) -> Option<&TupleArgumentsTokens> {
57        self.tokens.as_ref()
58    }
59
60    /// Adds an argument to this tuple.
61    pub fn with_argument<T: Into<Expression>>(mut self, argument: T) -> Self {
62        self.push(argument.into());
63        self
64    }
65
66    /// Pushes an argument to this tuple.
67    pub fn push(&mut self, argument: impl Into<Expression>) {
68        let argument = argument.into();
69        let initial_len = self.values.len();
70
71        self.values.push(argument);
72
73        if initial_len != 0 {
74            if let Some(tokens) = &mut self.tokens {
75                if tokens.commas.len() == initial_len - 1 {
76                    tokens.commas.push(Token::from_content(","));
77                }
78            }
79        }
80    }
81
82    /// Inserts an argument at the specified index.
83    pub fn insert(&mut self, index: usize, argument: impl Into<Expression>) {
84        if index >= self.values.len() {
85            self.push(argument.into());
86        } else {
87            self.values.insert(index, argument.into());
88
89            if let Some(tokens) = &mut self.tokens {
90                if index <= tokens.commas.len() {
91                    tokens.commas.insert(index, Token::from_content(","));
92                }
93            }
94        }
95    }
96
97    /// Returns a mutable reference to the last token of this tuple of arguments,
98    /// creating it if missing.
99    pub fn mutate_last_token(&mut self) -> &mut Token {
100        if self.get_tokens().is_none() {
101            self.set_tokens(TupleArgumentsTokens {
102                opening_parenthese: Token::from_content("("),
103                closing_parenthese: Token::from_content(")"),
104                commas: (0..self.len().saturating_sub(1))
105                    .map(|_| Token::from_content(","))
106                    .collect(),
107            });
108        }
109        &mut self.tokens.as_mut().unwrap().closing_parenthese
110    }
111
112    /// Returns the number of arguments in this tuple.
113    #[inline]
114    pub fn len(&self) -> usize {
115        self.values.len()
116    }
117
118    /// Returns whether this tuple has no arguments.
119    #[inline]
120    pub fn is_empty(&self) -> bool {
121        self.values.is_empty()
122    }
123
124    /// Returns an iterator over the argument expressions.
125    #[inline]
126    pub fn iter_values(&self) -> impl Iterator<Item = &Expression> {
127        self.values.iter()
128    }
129
130    /// Returns a mutable iterator over the argument expressions.
131    #[inline]
132    pub fn iter_mut_values(&mut self) -> impl Iterator<Item = &mut Expression> {
133        self.values.iter_mut()
134    }
135
136    super::impl_token_fns!(iter = [tokens]);
137}
138
139impl From<Arguments> for TupleArguments {
140    fn from(arguments: Arguments) -> Self {
141        match arguments {
142            Arguments::Tuple(tuple) => tuple,
143            Arguments::String(string) => TupleArguments::default().with_argument(string),
144            Arguments::Table(table) => TupleArguments::default().with_argument(table),
145        }
146    }
147}
148
149impl iter::FromIterator<Expression> for TupleArguments {
150    fn from_iter<T: IntoIterator<Item = Expression>>(iter: T) -> Self {
151        Self {
152            values: iter.into_iter().collect(),
153            tokens: None,
154        }
155    }
156}
157
158/// Represents the different ways arguments can be passed to a function call.
159#[derive(Clone, Debug, PartialEq, Eq)]
160pub enum Arguments {
161    /// Multiple arguments in parentheses: `func(arg1, arg2)`
162    Tuple(TupleArguments),
163    /// A single string argument without parentheses: `func "string"`
164    String(StringExpression),
165    /// A single table argument without parentheses: `func {key=value}`
166    Table(TableExpression),
167}
168
169impl Arguments {
170    /// Returns the total number of arguments.
171    #[inline]
172    pub fn len(&self) -> usize {
173        match self {
174            Self::Tuple(tuple) => tuple.len(),
175            Self::String(_) | Self::Table(_) => 1,
176        }
177    }
178
179    /// Returns true if this is an empty tuple of arguments.
180    #[inline]
181    pub fn is_empty(&self) -> bool {
182        match self {
183            Self::Tuple(tuple) => tuple.is_empty(),
184            Self::String(_) | Self::Table(_) => false,
185        }
186    }
187
188    /// Converts these arguments into a vector of expressions.
189    pub fn to_expressions(self) -> Vec<Expression> {
190        match self {
191            Self::Tuple(expressions) => expressions.to_expressions(),
192            Self::String(string) => vec![string.into()],
193            Self::Table(table) => vec![table.into()],
194        }
195    }
196
197    /// Adds an argument to these arguments, converting to a tuple if needed.
198    pub fn with_argument<T: Into<Expression>>(self, argument: T) -> Self {
199        TupleArguments::from(self).with_argument(argument).into()
200    }
201
202    /// Pushes an argument to these arguments, converting to a tuple if needed.
203    pub fn push(&mut self, argument: impl Into<Expression>) {
204        let argument = argument.into();
205
206        let tuple_args = match self {
207            Arguments::Tuple(tuple) => {
208                tuple.push(argument);
209                return;
210            }
211            Arguments::String(value) => TupleArguments::default()
212                .with_argument(mem::replace(value, StringExpression::empty())),
213            Arguments::Table(value) => TupleArguments::default().with_argument(mem::take(value)),
214        };
215
216        *self = tuple_args.with_argument(argument).into();
217    }
218
219    /// Inserts an argument at the specified index, converting to a tuple if needed.
220    pub fn insert(&mut self, index: usize, argument: impl Into<Expression>) {
221        let argument = argument.into();
222
223        let mut tuple_args = match self {
224            Arguments::Tuple(tuple) => {
225                tuple.insert(index, argument);
226                return;
227            }
228            Arguments::String(value) => {
229                let string = mem::replace(value, StringExpression::empty());
230                TupleArguments::default().with_argument(Expression::from(string))
231            }
232            Arguments::Table(value) => {
233                let table = mem::take(value);
234                TupleArguments::default().with_argument(Expression::from(table))
235            }
236        };
237
238        tuple_args.insert(index, argument);
239
240        *self = tuple_args.into();
241    }
242
243    /// Returns a mutable reference to the last token of these arguments,
244    /// creating it if missing.
245    pub fn mutate_last_token(&mut self) -> &mut Token {
246        match self {
247            Arguments::Tuple(tuple) => tuple.mutate_last_token(),
248            Arguments::String(string) => string.mutate_or_insert_token(),
249            Arguments::Table(table) => table.mutate_last_token(),
250        }
251    }
252
253    /// Removes all comments from these arguments.
254    pub fn clear_comments(&mut self) {
255        match self {
256            Arguments::Tuple(tuple) => tuple.clear_comments(),
257            Arguments::String(_) | Arguments::Table(_) => {}
258        }
259    }
260
261    /// Removes all whitespace from these arguments.
262    pub fn clear_whitespaces(&mut self) {
263        match self {
264            Arguments::Tuple(tuple) => tuple.clear_whitespaces(),
265            Arguments::String(_) | Arguments::Table(_) => {}
266        }
267    }
268
269    /// Replaces referenced tokens with the actual content from the source code.
270    pub(crate) fn replace_referenced_tokens(&mut self, code: &str) {
271        match self {
272            Arguments::Tuple(tuple) => tuple.replace_referenced_tokens(code),
273            Arguments::String(_) | Arguments::Table(_) => {}
274        }
275    }
276
277    /// Shifts token line numbers by the specified amount.
278    pub(crate) fn shift_token_line(&mut self, amount: isize) {
279        match self {
280            Arguments::Tuple(tuple) => tuple.shift_token_line(amount),
281            Arguments::String(_) | Arguments::Table(_) => {}
282        }
283    }
284
285    /// Filters comments using the provided predicate.
286    pub(crate) fn filter_comments(&mut self, filter: impl Fn(&super::Trivia) -> bool) {
287        match self {
288            Arguments::Tuple(tuple) => tuple.filter_comments(filter),
289            Arguments::String(_) | Arguments::Table(_) => {}
290        }
291    }
292}
293
294impl Default for Arguments {
295    fn default() -> Self {
296        Self::Tuple(TupleArguments::default())
297    }
298}
299
300impl From<TupleArguments> for Arguments {
301    fn from(tuple: TupleArguments) -> Self {
302        Self::Tuple(tuple)
303    }
304}
305
306impl From<TableExpression> for Arguments {
307    fn from(table: TableExpression) -> Self {
308        Self::Table(table)
309    }
310}
311
312impl From<StringExpression> for Arguments {
313    fn from(string: StringExpression) -> Self {
314        Self::String(string)
315    }
316}
317
318#[cfg(test)]
319mod tests {
320    use super::*;
321    use crate::{
322        nodes::{Identifier, Statement},
323        Parser,
324    };
325
326    fn parse_arguments_with_tokens(lua: &str) -> Arguments {
327        let parser = Parser::default().preserve_tokens();
328
329        let code = format!("f {}", lua);
330
331        let block = parser.parse(&code).expect("code should parse");
332        if let Some(statement) = block.first_statement() {
333            if let Statement::Call(call) = statement {
334                return call.get_arguments().clone();
335            }
336        }
337        panic!("failed to parse call arguments from: {}", lua);
338    }
339
340    fn get_tuple_tokens(args: &Arguments) -> &TupleArgumentsTokens {
341        match args {
342            Arguments::Tuple(tuple) => tuple.get_tokens().expect("tuple should have tokens"),
343            Arguments::String(_) | Arguments::Table(_) => panic!("expected tuple arguments"),
344        }
345    }
346
347    fn expect_comma_tokens(args: &Arguments, index: usize) {
348        let tokens = get_tuple_tokens(args);
349        assert_eq!(tokens.commas[index], Token::from_content(","));
350    }
351
352    mod arguments_len {
353        use super::*;
354
355        #[test]
356        fn empty_tuple() {
357            let empty_tuple = Arguments::Tuple(TupleArguments::new(vec![]));
358            assert_eq!(empty_tuple.len(), 0);
359        }
360
361        #[test]
362        fn single_tuple() {
363            let single_tuple = Arguments::Tuple(TupleArguments::new(vec![Expression::Identifier(
364                Identifier::new("x"),
365            )]));
366            assert_eq!(single_tuple.len(), 1);
367        }
368
369        #[test]
370        fn multi_tuple() {
371            let multi_tuple = Arguments::Tuple(TupleArguments::new(vec![
372                Expression::Identifier(Identifier::new("x")),
373                Expression::Identifier(Identifier::new("y")),
374                Expression::Identifier(Identifier::new("z")),
375            ]));
376            assert_eq!(multi_tuple.len(), 3);
377        }
378
379        #[test]
380        fn string() {
381            let string_args = Arguments::String(StringExpression::from_value("test"));
382            assert_eq!(string_args.len(), 1);
383        }
384
385        #[test]
386        fn table() {
387            let table_args = Arguments::Table(TableExpression::new(vec![]));
388            assert_eq!(table_args.len(), 1);
389        }
390    }
391
392    mod arguments_is_empty {
393        use super::*;
394
395        #[test]
396        fn empty_tuple() {
397            let empty_tuple = Arguments::Tuple(TupleArguments::new(vec![]));
398            assert!(empty_tuple.is_empty());
399        }
400
401        #[test]
402        fn single_tuple() {
403            let single_tuple = Arguments::Tuple(TupleArguments::new(vec![Expression::Identifier(
404                Identifier::new("x"),
405            )]));
406            assert!(!single_tuple.is_empty());
407        }
408
409        #[test]
410        fn multi_tuple() {
411            let multi_tuple = Arguments::Tuple(TupleArguments::new(vec![
412                Expression::Identifier(Identifier::new("x")),
413                Expression::Identifier(Identifier::new("y")),
414            ]));
415            assert!(!multi_tuple.is_empty());
416        }
417
418        #[test]
419        fn string() {
420            let string_args = Arguments::String(StringExpression::from_value("test"));
421            assert!(!string_args.is_empty());
422        }
423
424        #[test]
425        fn table() {
426            let table_args = Arguments::Table(TableExpression::new(vec![]));
427            assert!(!table_args.is_empty());
428        }
429    }
430
431    #[test]
432    fn push_argument_handles_commas() {
433        let mut args = parse_arguments_with_tokens("()");
434
435        args.push(Identifier::new("first"));
436        assert_eq!(get_tuple_tokens(&args).commas.len(), 0);
437
438        args.push(Identifier::new("second"));
439        assert_eq!(get_tuple_tokens(&args).commas.len(), 1);
440        expect_comma_tokens(&args, 0);
441
442        args.push(Identifier::new("third"));
443        assert_eq!(get_tuple_tokens(&args).commas.len(), 2);
444        expect_comma_tokens(&args, 1);
445    }
446
447    #[test]
448    fn insert_argument_handles_commas() {
449        let mut args = parse_arguments_with_tokens("(first, third)");
450
451        args.insert(1, Identifier::new("second"));
452        assert_eq!(get_tuple_tokens(&args).commas.len(), 2);
453        expect_comma_tokens(&args, 1);
454
455        args.insert(3, Identifier::new("fourth"));
456        assert_eq!(get_tuple_tokens(&args).commas.len(), 3);
457
458        args.insert(0, Identifier::new("zero"));
459        assert_eq!(get_tuple_tokens(&args).commas.len(), 4);
460    }
461}