Skip to main content

darklua_core/nodes/
function_call.rs

1use crate::nodes::{
2    Arguments, Expression, Identifier, Prefix, Token, Type, TypeInstantiationTokens,
3};
4
5/// Tokens associated with a function call.
6#[derive(Clone, Debug, PartialEq, Eq)]
7pub struct FunctionCallTokens {
8    pub colon: Option<Token>,
9    pub type_instantiation_tokens: Option<TypeInstantiationTokens>,
10}
11
12impl FunctionCallTokens {
13    super::impl_token_fns!(iter = [colon, type_instantiation_tokens]);
14}
15
16/// Represents a function call expression (e.g., `func()`, `obj:method()`, `a.b.c()`).
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct FunctionCall {
19    prefix: Box<Prefix>,
20    arguments: Arguments,
21    method: Option<Method>,
22    tokens: Option<FunctionCallTokens>,
23}
24
25#[derive(Clone, Debug, PartialEq, Eq)]
26struct Method {
27    name: Identifier,
28    types: Option<Vec<Type>>,
29}
30
31impl Method {
32    super::impl_token_fns!(target = [name]);
33}
34
35impl FunctionCall {
36    /// Creates a new function call with the given prefix, arguments, and optional method.
37    pub fn new(prefix: Prefix, arguments: Arguments, method: Option<Identifier>) -> Self {
38        Self {
39            prefix: Box::new(prefix),
40            arguments,
41            method: method.map(|name| Method { name, types: None }),
42            tokens: None,
43        }
44    }
45
46    /// Creates a new function call with the given name.
47    pub fn from_name<T: Into<Identifier>>(name: T) -> Self {
48        Self {
49            prefix: Box::new(name.into().into()),
50            arguments: Arguments::default(),
51            method: None,
52            tokens: None,
53        }
54    }
55
56    /// Creates a new function call with the given prefix.
57    pub fn from_prefix<T: Into<Prefix>>(prefix: T) -> Self {
58        Self {
59            prefix: Box::new(prefix.into()),
60            arguments: Arguments::default(),
61            method: None,
62            tokens: None,
63        }
64    }
65
66    /// Sets the tokens for this function call.
67    pub fn with_tokens(mut self, tokens: FunctionCallTokens) -> Self {
68        self.tokens = Some(tokens);
69        self
70    }
71
72    /// Sets the tokens for this function call.
73    #[inline]
74    pub fn set_tokens(&mut self, tokens: FunctionCallTokens) {
75        self.tokens = Some(tokens);
76    }
77
78    /// Returns the tokens for this function call, if any.
79    #[inline]
80    pub fn get_tokens(&self) -> Option<&FunctionCallTokens> {
81        self.tokens.as_ref()
82    }
83
84    /// Sets the arguments for this function call.
85    pub fn with_arguments<A: Into<Arguments>>(mut self, arguments: A) -> Self {
86        self.arguments = arguments.into();
87        self
88    }
89
90    /// Adds an argument to this function call.
91    pub fn with_argument<T: Into<Expression>>(mut self, argument: T) -> Self {
92        self.arguments = self.arguments.with_argument(argument);
93        self
94    }
95
96    /// Sets the method name for this function call (for method calls like `obj:method()`).
97    pub fn with_method(mut self, method: impl Into<Identifier>) -> Self {
98        self.set_method(method.into());
99        self
100    }
101
102    /// Sets the method with a specific type instantiation.
103    pub fn with_type_instantiation_method(
104        mut self,
105        method: impl Into<Identifier>,
106        types: Vec<Type>,
107    ) -> Self {
108        self.set_type_instantiation_method(method.into(), types);
109        self
110    }
111
112    /// Sets the method with a specific type instantiation.
113    pub fn set_type_instantiation_method(
114        &mut self,
115        method: impl Into<Identifier>,
116        types: Vec<Type>,
117    ) {
118        self.method = Some(Method {
119            name: method.into(),
120            types: Some(types),
121        });
122    }
123
124    /// Removes the type instantiation from the method, if any, and returns true if it was present.
125    pub fn remove_type_instantiation_from_method(&mut self) -> bool {
126        self.method
127            .as_mut()
128            .and_then(|method| method.types.take())
129            .is_some()
130    }
131
132    /// Returns the arguments of this function call.
133    #[inline]
134    pub fn get_arguments(&self) -> &Arguments {
135        &self.arguments
136    }
137
138    /// Returns the method name, if this is a method call.
139    #[inline]
140    pub fn get_method(&self) -> Option<&Identifier> {
141        self.method.as_ref().map(|method| &method.name)
142    }
143
144    /// Returns an iterator over the type instantiations of the method.
145    pub fn get_method_type_instantiation(&self) -> impl Iterator<Item = &Type> {
146        self.method
147            .iter()
148            .flat_map(|method| method.types.iter().flatten())
149    }
150
151    /// Returns whether this call has a method with a type instantiation.
152    pub fn has_method_type_instantiation(&self) -> bool {
153        self.method
154            .as_ref()
155            .map(|method| method.types.is_some())
156            .unwrap_or_default()
157    }
158
159    /// Returns if this call uses a method.
160    #[inline]
161    pub fn has_method(&self) -> bool {
162        self.method.is_some()
163    }
164
165    /// Returns the prefix (what is being called) of this function call.
166    #[inline]
167    pub fn get_prefix(&self) -> &Prefix {
168        &self.prefix
169    }
170
171    /// Removes and returns the method name, if any.
172    #[inline]
173    pub fn take_method(&mut self) -> Option<Identifier> {
174        let method = self.method.take();
175        if let Some(tokens) = self.tokens.as_mut() {
176            tokens.colon = None;
177        }
178        method.map(|method| method.name)
179    }
180
181    /// Sets the arguments for this function call.
182    #[inline]
183    pub fn set_arguments(&mut self, arguments: Arguments) {
184        self.arguments = arguments;
185    }
186
187    /// Sets the method name for this function call.
188    #[inline]
189    pub fn set_method(&mut self, method: Identifier) {
190        if let Some(current_method) = &mut self.method {
191            current_method.name = method;
192        } else {
193            self.method = Some(Method {
194                name: method,
195                types: None,
196            });
197        }
198    }
199
200    /// Returns a mutable reference to the arguments.
201    #[inline]
202    pub fn mutate_arguments(&mut self) -> &mut Arguments {
203        &mut self.arguments
204    }
205
206    /// Returns a mutable reference to the prefix.
207    #[inline]
208    pub fn mutate_prefix(&mut self) -> &mut Prefix {
209        &mut self.prefix
210    }
211
212    /// Returns a mutable reference to the first token of this function call,
213    /// creating it if missing.
214    pub fn mutate_first_token(&mut self) -> &mut Token {
215        self.prefix.mutate_first_token()
216    }
217
218    /// Returns a mutable reference to the last token of this function call,
219    /// creating it if missing.
220    pub fn mutate_last_token(&mut self) -> &mut Token {
221        self.arguments.mutate_last_token()
222    }
223
224    super::impl_token_fns!(iter = [tokens, method]);
225}
226
227#[cfg(test)]
228mod tests {
229    use super::*;
230    use crate::{nodes::Statement, Parser};
231
232    fn parse_call(code: &str) -> FunctionCall {
233        let parser = Parser::default().preserve_tokens();
234        let block = parser.parse(code).expect("code should parse");
235        if let Some(Statement::Call(call)) = block.first_statement() {
236            return call.clone();
237        }
238        panic!("failed to parse call from: {}", code);
239    }
240
241    #[test]
242    fn test_take_method_removes_colon_token() {
243        let mut call = parse_call("obj:method()");
244
245        assert!(call.get_tokens().unwrap().colon.is_some());
246        call.take_method();
247        assert!(call.get_tokens().unwrap().colon.is_none());
248    }
249}