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::Call(call)) = block.first_statement() {
333            return call.get_arguments().clone();
334        }
335        panic!("failed to parse call arguments from: {}", lua);
336    }
337
338    fn get_tuple_tokens(args: &Arguments) -> &TupleArgumentsTokens {
339        match args {
340            Arguments::Tuple(tuple) => tuple.get_tokens().expect("tuple should have tokens"),
341            Arguments::String(_) | Arguments::Table(_) => panic!("expected tuple arguments"),
342        }
343    }
344
345    fn expect_comma_tokens(args: &Arguments, index: usize) {
346        let tokens = get_tuple_tokens(args);
347        assert_eq!(tokens.commas[index], Token::from_content(","));
348    }
349
350    mod arguments_len {
351        use super::*;
352
353        #[test]
354        fn empty_tuple() {
355            let empty_tuple = Arguments::Tuple(TupleArguments::new(vec![]));
356            assert_eq!(empty_tuple.len(), 0);
357        }
358
359        #[test]
360        fn single_tuple() {
361            let single_tuple = Arguments::Tuple(TupleArguments::new(vec![Expression::Identifier(
362                Identifier::new("x"),
363            )]));
364            assert_eq!(single_tuple.len(), 1);
365        }
366
367        #[test]
368        fn multi_tuple() {
369            let multi_tuple = Arguments::Tuple(TupleArguments::new(vec![
370                Expression::Identifier(Identifier::new("x")),
371                Expression::Identifier(Identifier::new("y")),
372                Expression::Identifier(Identifier::new("z")),
373            ]));
374            assert_eq!(multi_tuple.len(), 3);
375        }
376
377        #[test]
378        fn string() {
379            let string_args = Arguments::String(StringExpression::from_value("test"));
380            assert_eq!(string_args.len(), 1);
381        }
382
383        #[test]
384        fn table() {
385            let table_args = Arguments::Table(TableExpression::new(vec![]));
386            assert_eq!(table_args.len(), 1);
387        }
388    }
389
390    mod arguments_is_empty {
391        use super::*;
392
393        #[test]
394        fn empty_tuple() {
395            let empty_tuple = Arguments::Tuple(TupleArguments::new(vec![]));
396            assert!(empty_tuple.is_empty());
397        }
398
399        #[test]
400        fn single_tuple() {
401            let single_tuple = Arguments::Tuple(TupleArguments::new(vec![Expression::Identifier(
402                Identifier::new("x"),
403            )]));
404            assert!(!single_tuple.is_empty());
405        }
406
407        #[test]
408        fn multi_tuple() {
409            let multi_tuple = Arguments::Tuple(TupleArguments::new(vec![
410                Expression::Identifier(Identifier::new("x")),
411                Expression::Identifier(Identifier::new("y")),
412            ]));
413            assert!(!multi_tuple.is_empty());
414        }
415
416        #[test]
417        fn string() {
418            let string_args = Arguments::String(StringExpression::from_value("test"));
419            assert!(!string_args.is_empty());
420        }
421
422        #[test]
423        fn table() {
424            let table_args = Arguments::Table(TableExpression::new(vec![]));
425            assert!(!table_args.is_empty());
426        }
427    }
428
429    #[test]
430    fn push_argument_handles_commas() {
431        let mut args = parse_arguments_with_tokens("()");
432
433        args.push(Identifier::new("first"));
434        assert_eq!(get_tuple_tokens(&args).commas.len(), 0);
435
436        args.push(Identifier::new("second"));
437        assert_eq!(get_tuple_tokens(&args).commas.len(), 1);
438        expect_comma_tokens(&args, 0);
439
440        args.push(Identifier::new("third"));
441        assert_eq!(get_tuple_tokens(&args).commas.len(), 2);
442        expect_comma_tokens(&args, 1);
443    }
444
445    #[test]
446    fn insert_argument_handles_commas() {
447        let mut args = parse_arguments_with_tokens("(first, third)");
448
449        args.insert(1, Identifier::new("second"));
450        assert_eq!(get_tuple_tokens(&args).commas.len(), 2);
451        expect_comma_tokens(&args, 1);
452
453        args.insert(3, Identifier::new("fourth"));
454        assert_eq!(get_tuple_tokens(&args).commas.len(), 3);
455
456        args.insert(0, Identifier::new("zero"));
457        assert_eq!(get_tuple_tokens(&args).commas.len(), 4);
458    }
459}