cairo_lang_syntax/attribute/
structured.rs

1use std::fmt;
2
3use cairo_lang_debug::DebugWithDb;
4use smol_str::SmolStr;
5
6use crate::node::db::SyntaxGroup;
7use crate::node::{Terminal, TypedSyntaxNode, ast};
8
9/// Easier to digest representation of an [ast::Attribute].
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct Attribute {
12    pub stable_ptr: ast::AttributePtr,
13    pub id: SmolStr,
14    pub id_stable_ptr: ast::ExprPathPtr,
15    pub args: Vec<AttributeArg>,
16    pub args_stable_ptr: ast::OptionArgListParenthesizedPtr,
17}
18impl Attribute {
19    /// Checks if the given attribute has a single argument with the given name.
20    pub fn is_single_unnamed_arg(&self, db: &dyn SyntaxGroup, arg_name: &str) -> bool {
21        match &self.args[..] {
22            [arg] => match &arg.variant {
23                AttributeArgVariant::Unnamed(value) => {
24                    value.as_syntax_node().get_text_without_trivia(db) == arg_name
25                }
26                _ => false,
27            },
28            _ => false,
29        }
30    }
31}
32
33/// Easier to digest representation of a single attribute value.
34#[derive(Clone, Debug, PartialEq, Eq)]
35pub struct AttributeArg {
36    pub variant: AttributeArgVariant,
37    pub arg: ast::Arg,
38    pub modifiers: Vec<Modifier>,
39}
40
41/// Variant of [`AttributeArg`].
42#[derive(Clone, Debug, PartialEq, Eq)]
43pub enum AttributeArgVariant {
44    /// Just `value`.
45    Unnamed(ast::Expr),
46    /// `name: value`.
47    Named { value: ast::Expr, name: NameInfo },
48    /// `:name`
49    FieldInitShorthand(NameInfo),
50}
51
52#[derive(Clone, Debug, PartialEq, Eq)]
53/// The data on a name part of an argument.
54pub struct NameInfo {
55    /// The name of the argument.
56    pub text: SmolStr,
57    /// The stable pointer to the name.
58    pub stable_ptr: ast::TerminalIdentifierPtr,
59}
60impl NameInfo {
61    fn from_ast(name: ast::TerminalIdentifier, db: &dyn SyntaxGroup) -> Self {
62        NameInfo { text: name.text(db), stable_ptr: name.stable_ptr() }
63    }
64}
65
66/// Easier to digest representation of a [`ast::Modifier`] attached to [`AttributeArg`].
67#[derive(Clone, Debug, PartialEq, Eq)]
68pub struct Modifier {
69    pub text: SmolStr,
70    pub stable_ptr: ast::ModifierPtr,
71}
72
73impl<'a> DebugWithDb<dyn SyntaxGroup + 'a> for Attribute {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>, db: &(dyn SyntaxGroup + 'a)) -> fmt::Result {
75        write!(f, r#"Attribute {{ id: "{}""#, self.id)?;
76        if !self.args.is_empty() {
77            write!(f, ", args: [")?;
78            for arg in self.args.iter() {
79                write!(f, "{:?}, ", arg.arg.as_syntax_node().get_text(db))?;
80            }
81            write!(f, "]")?;
82        }
83        write!(f, " }}")
84    }
85}
86
87pub trait AttributeStructurize {
88    /// Return the structured attribute for the given [ast::Attribute].
89    fn structurize(self, db: &dyn SyntaxGroup) -> Attribute;
90}
91
92impl AttributeStructurize for ast::Attribute {
93    fn structurize(self, db: &dyn SyntaxGroup) -> Attribute {
94        let attr_id = self.attr(db);
95        let attr_args = self.arguments(db);
96
97        Attribute {
98            stable_ptr: self.stable_ptr(),
99            id: attr_id.as_syntax_node().get_text_without_trivia(db).into(),
100            id_stable_ptr: attr_id.stable_ptr(),
101            args: match attr_args {
102                ast::OptionArgListParenthesized::ArgListParenthesized(ref attribute_args) => {
103                    attribute_args
104                        .arguments(db)
105                        .elements(db)
106                        .into_iter()
107                        .map(|arg| AttributeArg::from_ast(arg, db))
108                        .collect()
109                }
110                ast::OptionArgListParenthesized::Empty(_) => vec![],
111            },
112            args_stable_ptr: attr_args.stable_ptr(),
113        }
114    }
115}
116
117pub trait AttributeListStructurize {
118    /// Return structured attributes for the given [ast::AttributeList].
119    fn structurize(self, db: &dyn SyntaxGroup) -> Vec<Attribute>;
120}
121
122impl AttributeListStructurize for ast::AttributeList {
123    fn structurize(self, db: &dyn SyntaxGroup) -> Vec<Attribute> {
124        // TODO(ilya): Consider checking for attribute repetitions.
125        self.elements(db).into_iter().map(|attr| attr.structurize(db)).collect()
126    }
127}
128
129impl AttributeArg {
130    /// Build [`AttributeArg`] from [`ast::Arg`].
131    pub fn from_ast(arg: ast::Arg, db: &dyn SyntaxGroup) -> AttributeArg {
132        let variant = match arg.arg_clause(db) {
133            ast::ArgClause::Unnamed(clause) => AttributeArgVariant::Unnamed(clause.value(db)),
134            ast::ArgClause::Named(clause) => AttributeArgVariant::Named {
135                value: clause.value(db),
136                name: NameInfo::from_ast(clause.name(db), db),
137            },
138            ast::ArgClause::FieldInitShorthand(clause) => AttributeArgVariant::FieldInitShorthand(
139                NameInfo::from_ast(clause.name(db).name(db), db),
140            ),
141        };
142
143        let modifiers = arg
144            .modifiers(db)
145            .elements(db)
146            .into_iter()
147            .map(|modifier| Modifier::from(modifier, db))
148            .collect();
149
150        AttributeArg { variant, arg, modifiers }
151    }
152
153    pub fn text(&self, db: &dyn SyntaxGroup) -> String {
154        match &self.variant {
155            AttributeArgVariant::Unnamed(value) => {
156                value.as_syntax_node().get_text_without_trivia(db)
157            }
158            AttributeArgVariant::Named { value, name } => {
159                format!("{}: {}", name.text, value.as_syntax_node().get_text_without_trivia(db))
160            }
161            AttributeArgVariant::FieldInitShorthand(name) => {
162                format!(":{}", name.text)
163            }
164        }
165    }
166}
167
168impl Modifier {
169    /// Build [`Modifier`] from [`ast::Modifier`].
170    fn from(modifier: ast::Modifier, db: &dyn SyntaxGroup) -> Modifier {
171        Modifier {
172            stable_ptr: modifier.stable_ptr(),
173            text: modifier.as_syntax_node().get_text(db).into(),
174        }
175    }
176}