Skip to main content

darklua_core/nodes/
attributes.rs

1use crate::nodes::{
2    Identifier, LiteralExpression, LiteralTable, StringExpression, Token, Trivia,
3    TupleArgumentsTokens,
4};
5
6/// A list of function attributes.
7///
8/// Attributes can be either named (e.g., `@deprecated`) or grouped
9/// (e.g., `@[attribute1, attribute2()]`).
10#[derive(Clone, Debug, Default, PartialEq, Eq)]
11pub struct Attributes {
12    attributes: Vec<Attribute>,
13}
14
15impl Attributes {
16    /// Creates a new empty list of attributes.
17    pub fn new() -> Self {
18        Self {
19            attributes: Vec::new(),
20        }
21    }
22
23    /// Adds an attribute to this list of attributes.
24    pub fn with_attribute(mut self, attribute: impl Into<Attribute>) -> Self {
25        self.attributes.push(attribute.into());
26        self
27    }
28
29    /// Appends an attribute to this list of attributes.
30    ///
31    /// Empty attribute groups are silently ignored.
32    pub fn append_attribute(&mut self, attribute: impl Into<Attribute>) {
33        let attribute = attribute.into();
34        if let Attribute::Group(list) = &attribute {
35            if list.is_empty() {
36                // don't append empty lists
37                return;
38            }
39        }
40        self.attributes.push(attribute);
41    }
42
43    /// Returns an iterator over the attributes in this list.
44    pub fn iter_attributes(&self) -> impl Iterator<Item = &Attribute> {
45        self.attributes.iter()
46    }
47
48    /// Returns a mutable iterator over the attributes in this list.
49    pub fn iter_mut_attributes(&mut self) -> impl Iterator<Item = &mut Attribute> {
50        self.attributes.iter_mut()
51    }
52
53    /// Clears all attributes.
54    pub fn clear_attributes(&mut self) {
55        self.attributes.clear();
56    }
57
58    /// Returns whether this list of attributes is empty.
59    pub fn is_empty(&self) -> bool {
60        self.attributes.is_empty()
61    }
62
63    /// Returns the number of attributes in this list.
64    pub fn len(&self) -> usize {
65        self.attributes.len()
66    }
67
68    /// Checks if an attribute with the given name exists in this list.
69    pub fn has_attribute(&self, name: &str) -> bool {
70        self.attributes.iter().any(|attr| match attr {
71            Attribute::Name(named) => named.get_identifier().get_name() == name,
72            Attribute::Group(group) => group.has_attribute(name),
73        })
74    }
75
76    /// Filters attributes based on a predicate, keeping only those that return true.
77    /// The predicate receives an immutable reference to each attribute.
78    pub fn filter_attributes<F>(&mut self, predicate: F)
79    where
80        F: Fn(&Attribute) -> bool,
81    {
82        self.attributes.retain(predicate);
83    }
84
85    /// Filters attributes based on a mutable predicate, keeping only those that return true.
86    /// The predicate receives a mutable reference to each attribute, allowing modifications.
87    pub fn filter_mut_attributes<F>(&mut self, predicate: F)
88    where
89        F: FnMut(&mut Attribute) -> bool,
90    {
91        self.attributes.retain_mut(predicate);
92    }
93
94    super::impl_token_fns!(iter = [attributes]);
95}
96
97/// Represents a function attribute.
98///
99/// Attributes can be either:
100/// - Named: `@deprecated`
101/// - Grouped: `@[attribute1, attribute2(args)]`
102#[derive(Clone, Debug, PartialEq, Eq)]
103pub enum Attribute {
104    /// A named attribute (e.g., `@deprecated`)
105    Name(NamedAttribute),
106    /// A group of attributes (e.g., `@[attr1, attr2]`)
107    Group(AttributeGroup),
108}
109
110impl Attribute {
111    /// Clears all comments from the tokens in this node.
112    pub fn clear_comments(&mut self) {
113        match self {
114            Self::Name(name) => name.clear_comments(),
115            Self::Group(list) => list.clear_comments(),
116        }
117    }
118
119    /// Clears all whitespaces information from the tokens in this node.
120    pub fn clear_whitespaces(&mut self) {
121        match self {
122            Self::Name(name) => name.clear_whitespaces(),
123            Self::Group(list) => list.clear_whitespaces(),
124        }
125    }
126
127    pub(crate) fn replace_referenced_tokens(&mut self, code: &str) {
128        match self {
129            Self::Name(name) => name.replace_referenced_tokens(code),
130            Self::Group(list) => list.replace_referenced_tokens(code),
131        }
132    }
133
134    pub(crate) fn shift_token_line(&mut self, amount: isize) {
135        match self {
136            Self::Name(name) => name.shift_token_line(amount),
137            Self::Group(list) => list.shift_token_line(amount),
138        }
139    }
140
141    pub(crate) fn filter_comments(&mut self, filter: impl Fn(&Trivia) -> bool) {
142        match self {
143            Self::Name(name) => name.filter_comments(filter),
144            Self::Group(list) => list.filter_comments(filter),
145        }
146    }
147}
148
149impl From<AttributeGroup> for Attribute {
150    fn from(v: AttributeGroup) -> Self {
151        Self::Group(v)
152    }
153}
154
155impl From<AttributeGroupElement> for Attribute {
156    fn from(v: AttributeGroupElement) -> Self {
157        Self::Group(AttributeGroup::new(v))
158    }
159}
160
161impl From<NamedAttribute> for Attribute {
162    fn from(v: NamedAttribute) -> Self {
163        Self::Name(v)
164    }
165}
166
167/// A named function attribute.
168///
169/// Named attributes follow the syntax `@name`, such as `@deprecated`.
170#[derive(Clone, Debug, PartialEq, Eq)]
171pub struct NamedAttribute {
172    name: Identifier,
173    token: Option<Token>,
174}
175
176impl NamedAttribute {
177    /// Creates a new named attribute with the given name.
178    pub fn new(name: impl Into<Identifier>) -> Self {
179        Self {
180            name: name.into(),
181            token: None,
182        }
183    }
184
185    /// Attaches a token to this named attribute for the `@` symbol.
186    pub fn with_token(mut self, token: Token) -> Self {
187        self.token = Some(token);
188        self
189    }
190
191    /// Sets the token for this named attribute's `@` symbol.
192    #[inline]
193    pub fn set_token(&mut self, token: Token) {
194        self.token = Some(token);
195    }
196
197    /// Returns the token for this named attribute's `@` symbol, if any.
198    #[inline]
199    pub fn get_token(&self) -> Option<&Token> {
200        self.token.as_ref()
201    }
202
203    /// Returns the attribute's name.
204    #[inline]
205    pub fn get_identifier(&self) -> &Identifier {
206        &self.name
207    }
208
209    /// Returns a mutable reference to the attribute's name.
210    #[inline]
211    pub fn mutate_identifier(&mut self) -> &mut Identifier {
212        &mut self.name
213    }
214
215    super::impl_token_fns!(target = [name] iter = [token]);
216}
217
218/// A group of attributes.
219///
220/// Attribute groups follow the syntax `@[attr1, attr2(args), ...]`.
221#[derive(Clone, Debug, PartialEq, Eq)]
222pub struct AttributeGroup {
223    attributes: Vec<AttributeGroupElement>,
224    tokens: Option<AttributeGroupTokens>,
225}
226
227impl AttributeGroup {
228    /// Creates a new attribute group with a single attribute.
229    pub fn new(attribute: impl Into<AttributeGroupElement>) -> Self {
230        Self {
231            attributes: vec![attribute.into()],
232            tokens: None,
233        }
234    }
235
236    /// Adds an attribute to this group.
237    pub fn with_attribute(mut self, attribute: impl Into<AttributeGroupElement>) -> Self {
238        self.attributes.push(attribute.into());
239        self
240    }
241
242    /// Appends an attribute to this group.
243    pub fn append_attribute(&mut self, attribute: impl Into<AttributeGroupElement>) {
244        self.attributes.push(attribute.into());
245    }
246
247    /// Returns an iterator over the attributes in this group.
248    pub fn iter_attributes(&self) -> impl Iterator<Item = &AttributeGroupElement> {
249        self.attributes.iter()
250    }
251
252    /// Returns a mutable iterator over the attributes in this group.
253    pub fn iter_mut_attributes(&mut self) -> impl Iterator<Item = &mut AttributeGroupElement> {
254        self.attributes.iter_mut()
255    }
256
257    /// Attaches tokens to this attribute group.
258    pub fn with_tokens(mut self, tokens: AttributeGroupTokens) -> Self {
259        self.tokens = Some(tokens);
260        self
261    }
262
263    /// Sets the tokens for this attribute group.
264    pub fn set_tokens(&mut self, tokens: AttributeGroupTokens) {
265        self.tokens = Some(tokens);
266    }
267
268    /// Returns the tokens for this attribute group, if any.
269    pub fn get_tokens(&self) -> Option<&AttributeGroupTokens> {
270        self.tokens.as_ref()
271    }
272
273    /// Returns a mutable reference to the tokens for this attribute group, if any.
274    pub fn mutate_tokens(&mut self) -> Option<&mut AttributeGroupTokens> {
275        self.tokens.as_mut()
276    }
277
278    /// Returns whether this attribute group is empty.
279    pub fn is_empty(&self) -> bool {
280        self.attributes.is_empty()
281    }
282
283    /// Returns the number of attributes in this group.
284    pub fn len(&self) -> usize {
285        self.attributes.len()
286    }
287
288    /// Checks if an attribute with the given name exists in this group.
289    pub fn has_attribute(&self, name: &str) -> bool {
290        self.attributes
291            .iter()
292            .any(|elem| elem.name().get_name() == name)
293    }
294
295    /// Removes an attribute element at the specified index.
296    /// Updates separator tokens if present.
297    pub fn remove(&mut self, index: usize) {
298        if index < self.attributes.len() {
299            self.attributes.remove(index);
300
301            // Update separators to match the new attributes length
302            if let Some(tokens) = &mut self.tokens {
303                if index < tokens.separators.len() {
304                    tokens.separators.remove(index);
305                }
306            }
307        }
308    }
309
310    /// Filters attribute elements based on a predicate, keeping only those that return true.
311    /// Updates separator tokens to stay synchronized with the attributes.
312    pub fn filter_attributes<F>(&mut self, mut predicate: F)
313    where
314        F: FnMut(&AttributeGroupElement) -> bool,
315    {
316        let mut i = 0;
317        while i < self.attributes.len() {
318            if predicate(&self.attributes[i]) {
319                i += 1;
320            } else {
321                self.remove(i);
322            }
323        }
324    }
325
326    /// Filters attribute elements with mutable access, keeping only those that return true.
327    /// Updates separator tokens to stay synchronized with the attributes.
328    pub fn filter_mut_attributes<F>(&mut self, mut predicate: F)
329    where
330        F: FnMut(&mut AttributeGroupElement) -> bool,
331    {
332        let mut i = 0;
333        while i < self.attributes.len() {
334            if predicate(&mut self.attributes[i]) {
335                i += 1;
336            } else {
337                self.remove(i);
338            }
339        }
340    }
341
342    super::impl_token_fns!(iter = [tokens, attributes]);
343}
344
345/// Tokens for an attribute group.
346#[derive(Clone, Debug, PartialEq, Eq)]
347pub struct AttributeGroupTokens {
348    /// Token for the `@[` opening.
349    pub opening_attribute_list: Token,
350    /// Token for the closing bracket `]`
351    pub closing_bracket: Token,
352    /// Tokens for the commas between entries.
353    pub separators: Vec<Token>,
354}
355
356impl AttributeGroupTokens {
357    super::impl_token_fns!(
358        target = [opening_attribute_list, closing_bracket]
359        iter = [separators]
360    );
361}
362
363/// An element within an attribute group.
364///
365/// Elements can have optional arguments: `@[attr, attr_with_args(arg1, arg2)]`.
366#[derive(Clone, Debug, PartialEq, Eq)]
367pub struct AttributeGroupElement {
368    name: Identifier,
369    arguments: Option<AttributeArguments>,
370}
371
372impl AttributeGroupElement {
373    /// Creates a new attribute group element with the given name.
374    pub fn new(name: impl Into<Identifier>) -> Self {
375        Self {
376            name: name.into(),
377            arguments: None,
378        }
379    }
380
381    /// Attaches arguments to this attribute element.
382    pub fn with_arguments(mut self, arguments: impl Into<AttributeArguments>) -> Self {
383        self.arguments = Some(arguments.into());
384        self
385    }
386
387    /// Returns the attribute element's name.
388    pub fn name(&self) -> &Identifier {
389        &self.name
390    }
391
392    /// Returns a mutable reference to the attribute element's name.
393    pub fn mutate_name(&mut self) -> &mut Identifier {
394        &mut self.name
395    }
396
397    /// Returns the attribute element's arguments, if any.
398    pub fn get_arguments(&self) -> Option<&AttributeArguments> {
399        self.arguments.as_ref()
400    }
401
402    /// Returns a mutable reference to the attribute element's arguments, if any.
403    pub fn mutate_arguments(&mut self) -> Option<&mut AttributeArguments> {
404        self.arguments.as_mut()
405    }
406
407    /// Sets the arguments for this attribute element.
408    pub fn set_arguments(&mut self, arguments: impl Into<AttributeArguments>) {
409        self.arguments = Some(arguments.into());
410    }
411
412    /// Removes the arguments from this attribute element.
413    pub fn remove_arguments(&mut self) {
414        self.arguments = None;
415    }
416
417    super::impl_token_fns!(target = [name] iter = [arguments]);
418}
419
420/// Arguments for an attribute element.
421///
422/// Attributes can accept tuple arguments, string literals, or table literals:
423/// - Tuple: `@[attr(true, 0)]`
424/// - String: `@[attr "text"]`
425/// - Table: `@[attr { key = value }]`
426#[derive(Clone, Debug, PartialEq, Eq)]
427pub enum AttributeArguments {
428    /// Tuple arguments: `attr("hello", 0)`
429    Tuple(AttributeTupleArguments),
430    /// String argument: `attr "text"`
431    String(StringExpression),
432    /// Table argument: `attr { key = value }`
433    Table(LiteralTable),
434}
435
436impl AttributeArguments {
437    /// Clears all comments from the tokens in this node.
438    pub fn clear_comments(&mut self) {
439        match self {
440            Self::Tuple(tuple) => tuple.clear_comments(),
441            Self::String(string) => string.clear_comments(),
442            Self::Table(table) => table.clear_comments(),
443        }
444    }
445
446    /// Clears all whitespaces information from the tokens in this node.
447    pub fn clear_whitespaces(&mut self) {
448        match self {
449            Self::Tuple(tuple) => tuple.clear_whitespaces(),
450            Self::String(string) => string.clear_whitespaces(),
451            Self::Table(table) => table.clear_whitespaces(),
452        }
453    }
454
455    pub(crate) fn replace_referenced_tokens(&mut self, code: &str) {
456        match self {
457            Self::Tuple(tuple) => tuple.replace_referenced_tokens(code),
458            Self::String(string) => string.replace_referenced_tokens(code),
459            Self::Table(table) => table.replace_referenced_tokens(code),
460        }
461    }
462
463    pub(crate) fn shift_token_line(&mut self, amount: isize) {
464        match self {
465            Self::Tuple(tuple) => tuple.shift_token_line(amount),
466            Self::String(string) => string.shift_token_line(amount),
467            Self::Table(table) => table.shift_token_line(amount),
468        }
469    }
470
471    pub(crate) fn filter_comments(&mut self, filter: impl Fn(&Trivia) -> bool) {
472        match self {
473            Self::Tuple(tuple) => tuple.filter_comments(filter),
474            Self::String(string) => string.filter_comments(filter),
475            Self::Table(table) => table.filter_comments(filter),
476        }
477    }
478}
479
480impl From<LiteralTable> for AttributeArguments {
481    fn from(v: LiteralTable) -> Self {
482        Self::Table(v)
483    }
484}
485
486impl From<StringExpression> for AttributeArguments {
487    fn from(v: StringExpression) -> Self {
488        Self::String(v)
489    }
490}
491
492impl From<AttributeTupleArguments> for AttributeArguments {
493    fn from(v: AttributeTupleArguments) -> Self {
494        Self::Tuple(v)
495    }
496}
497
498/// Tuple arguments for an attribute element.
499#[derive(Clone, Debug, Default, PartialEq, Eq)]
500pub struct AttributeTupleArguments {
501    values: Vec<LiteralExpression>,
502    tokens: Option<TupleArgumentsTokens>,
503}
504
505impl AttributeTupleArguments {
506    pub fn with_value(mut self, value: impl Into<LiteralExpression>) -> Self {
507        self.push(value.into());
508        self
509    }
510
511    pub fn push(&mut self, argument: impl Into<LiteralExpression>) {
512        let argument = argument.into();
513        let initial_len = self.values.len();
514
515        self.values.push(argument);
516
517        if initial_len != 0 {
518            if let Some(tokens) = &mut self.tokens {
519                if tokens.commas.len() == initial_len - 1 {
520                    tokens.commas.push(Token::from_content(","));
521                }
522            }
523        }
524    }
525
526    /// Inserts an argument at the specified index.
527    pub fn insert(&mut self, index: usize, argument: impl Into<LiteralExpression>) {
528        if index >= self.values.len() {
529            self.push(argument.into());
530        } else {
531            self.values.insert(index, argument.into());
532
533            if let Some(tokens) = &mut self.tokens {
534                if index <= tokens.commas.len() {
535                    tokens.commas.insert(index, Token::from_content(","));
536                }
537            }
538        }
539    }
540
541    /// Returns the number of arguments in this tuple.
542    pub fn len(&self) -> usize {
543        self.values.len()
544    }
545
546    /// Returns whether this tuple has no arguments.
547    pub fn is_empty(&self) -> bool {
548        self.values.is_empty()
549    }
550
551    /// Returns an iterator over the argument values.
552    pub fn iter_values(&self) -> impl Iterator<Item = &LiteralExpression> {
553        self.values.iter()
554    }
555
556    /// Returns a mutable iterator over the argument values.
557    pub fn iter_mut_values(&mut self) -> impl Iterator<Item = &mut LiteralExpression> {
558        self.values.iter_mut()
559    }
560
561    /// Returns the tokens for this tuple arguments, if any.
562    pub fn get_tokens(&self) -> Option<&TupleArgumentsTokens> {
563        self.tokens.as_ref()
564    }
565
566    /// Sets the tokens for this tuple arguments.
567    pub fn set_tokens(&mut self, tokens: TupleArgumentsTokens) {
568        self.tokens = Some(tokens);
569    }
570
571    /// Attaches tokens to this tuple arguments.
572    pub fn with_tokens(mut self, tokens: TupleArgumentsTokens) -> Self {
573        self.tokens = Some(tokens);
574        self
575    }
576
577    super::impl_token_fns!(iter = [tokens]);
578}