cairo_lang_syntax/attribute/
structured.rs1use std::fmt;
2
3use cairo_lang_debug::DebugWithDb;
4use cairo_lang_filesystem::ids::SmolStrId;
5use salsa::Database;
6
7use crate::node::{Terminal, TypedSyntaxNode, ast};
8
9#[derive(Clone, Debug, PartialEq, Eq, salsa::Update)]
11pub struct Attribute<'a> {
12 pub stable_ptr: ast::AttributePtr<'a>,
13 pub id: SmolStrId<'a>,
14 pub id_stable_ptr: ast::ExprPathPtr<'a>,
15 pub args: Vec<AttributeArg<'a>>,
16 pub args_stable_ptr: ast::OptionArgListParenthesizedPtr<'a>,
17}
18impl<'a> Attribute<'a> {
19 pub fn is_single_unnamed_arg(&self, db: &'a dyn Database, 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).long(db) == arg_name
25 }
26 _ => false,
27 },
28 _ => false,
29 }
30 }
31}
32
33#[derive(Clone, Debug, PartialEq, Eq, salsa::Update)]
35pub struct AttributeArg<'a> {
36 pub variant: AttributeArgVariant<'a>,
37 pub arg: ast::Arg<'a>,
38 pub modifiers: Vec<Modifier<'a>>,
39}
40
41#[derive(Clone, Debug, PartialEq, Eq, salsa::Update)]
43pub enum AttributeArgVariant<'a> {
44 Unnamed(ast::Expr<'a>),
46 Named { value: ast::Expr<'a>, name: NameInfo<'a> },
48 FieldInitShorthand(NameInfo<'a>),
50}
51
52#[derive(Clone, Debug, PartialEq, Eq, salsa::Update)]
53pub struct NameInfo<'a> {
55 pub text: SmolStrId<'a>,
57 pub stable_ptr: ast::TerminalIdentifierPtr<'a>,
59}
60impl<'a> NameInfo<'a> {
61 fn from_ast(name: &ast::TerminalIdentifier<'a>, db: &'a dyn Database) -> Self {
62 NameInfo { text: name.text(db), stable_ptr: name.stable_ptr(db) }
63 }
64}
65
66#[derive(Clone, Debug, PartialEq, Eq, salsa::Update)]
68pub struct Modifier<'a> {
69 pub text: SmolStrId<'a>,
70 pub stable_ptr: ast::ModifierPtr<'a>,
71}
72
73impl<'a> DebugWithDb<'a> for Attribute<'a> {
74 type Db = dyn Database;
75 fn fmt(&self, f: &mut fmt::Formatter<'_>, db: &'a dyn Database) -> fmt::Result {
76 write!(f, r#"Attribute {{ id: "{}""#, self.id.long(db))?;
77 if !self.args.is_empty() {
78 write!(f, ", args: [")?;
79 for arg in &self.args {
80 write!(f, "{:?}, ", arg.arg.as_syntax_node().get_text(db))?;
81 }
82 write!(f, "]")?;
83 }
84 write!(f, " }}")
85 }
86}
87
88pub trait AttributeStructurize<'a> {
89 fn structurize(self, db: &'a dyn Database) -> Attribute<'a>;
91}
92
93impl<'a> AttributeStructurize<'a> for ast::Attribute<'a> {
94 fn structurize(self, db: &'a dyn Database) -> Attribute<'a> {
95 let attr_id = self.attr(db);
96 let attr_args = self.arguments(db);
97
98 Attribute {
99 stable_ptr: self.stable_ptr(db),
100 id: attr_id.as_syntax_node().get_text_without_trivia(db),
101 id_stable_ptr: attr_id.stable_ptr(db),
102 args: match attr_args {
103 ast::OptionArgListParenthesized::ArgListParenthesized(ref attribute_args) => {
104 attribute_args
105 .arguments(db)
106 .elements(db)
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(db),
113 }
114 }
115}
116
117pub trait AttributeListStructurize<'a> {
118 fn structurize(self, db: &'a dyn Database) -> Vec<Attribute<'a>>;
120}
121
122impl<'a> AttributeListStructurize<'a> for ast::AttributeList<'a> {
123 fn structurize(self, db: &'a dyn Database) -> Vec<Attribute<'a>> {
124 self.elements(db).map(|attr| attr.structurize(db)).collect()
126 }
127}
128
129impl<'a> AttributeArg<'a> {
130 pub fn from_ast(arg: ast::Arg<'a>, db: &'a dyn Database) -> AttributeArg<'a> {
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 =
144 arg.modifiers(db).elements(db).map(|modifier| Modifier::from(modifier, db)).collect();
145
146 AttributeArg { variant, arg, modifiers }
147 }
148
149 pub fn text(&self, db: &dyn Database) -> String {
150 match &self.variant {
151 AttributeArgVariant::Unnamed(value) => {
152 value.as_syntax_node().get_text_without_trivia(db).to_string(db)
153 }
154 AttributeArgVariant::Named { value, name } => {
155 format!(
156 "{}: {}",
157 name.text.long(db),
158 value.as_syntax_node().get_text_without_trivia(db).long(db)
159 )
160 }
161 AttributeArgVariant::FieldInitShorthand(name) => {
162 format!(":{}", name.text.long(db))
163 }
164 }
165 }
166}
167
168impl<'a> Modifier<'a> {
169 fn from(modifier: ast::Modifier<'a>, db: &'a dyn Database) -> Modifier<'a> {
171 Modifier {
172 stable_ptr: modifier.stable_ptr(db),
173 text: modifier
174 .as_syntax_node()
175 .text(db)
176 .expect("Modifier should always have underlying text"),
177 }
178 }
179}