cairo_lang_syntax/attribute/
structured.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
use std::fmt;

use cairo_lang_debug::DebugWithDb;
use smol_str::SmolStr;

use crate::node::db::SyntaxGroup;
use crate::node::{ast, Terminal, TypedSyntaxNode};

/// Easier to digest representation of an [ast::Attribute].
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Attribute {
    pub stable_ptr: ast::AttributePtr,
    pub id: SmolStr,
    pub id_stable_ptr: ast::ExprPathPtr,
    pub args: Vec<AttributeArg>,
    pub args_stable_ptr: ast::OptionArgListParenthesizedPtr,
}
impl Attribute {
    /// Checks if the given attribute has a single argument with the given name.
    pub fn is_single_unnamed_arg(&self, db: &dyn SyntaxGroup, arg_name: &str) -> bool {
        match &self.args[..] {
            [arg] => match &arg.variant {
                AttributeArgVariant::Unnamed(value) => {
                    value.as_syntax_node().get_text_without_trivia(db) == arg_name
                }
                _ => false,
            },
            _ => false,
        }
    }
}

/// Easier to digest representation of a single attribute value.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AttributeArg {
    pub variant: AttributeArgVariant,
    pub arg: ast::Arg,
    pub modifiers: Vec<Modifier>,
}

/// Variant of [`AttributeArg`].
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AttributeArgVariant {
    /// Just `value`.
    Unnamed(ast::Expr),
    /// `name: value`.
    Named { value: ast::Expr, name: NameInfo },
    /// `:name`
    FieldInitShorthand(NameInfo),
}

#[derive(Clone, Debug, PartialEq, Eq)]
/// The data on a name part of an argument.
pub struct NameInfo {
    /// The name of the argument.
    pub text: SmolStr,
    /// The stable pointer to the name.
    pub stable_ptr: ast::TerminalIdentifierPtr,
}
impl NameInfo {
    fn from_ast(name: ast::TerminalIdentifier, db: &dyn SyntaxGroup) -> Self {
        NameInfo { text: name.text(db), stable_ptr: name.stable_ptr() }
    }
}

/// Easier to digest representation of a [`ast::Modifier`] attached to [`AttributeArg`].
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Modifier {
    pub text: SmolStr,
    pub stable_ptr: ast::ModifierPtr,
}

impl<'a> DebugWithDb<dyn SyntaxGroup + 'a> for Attribute {
    fn fmt(&self, f: &mut fmt::Formatter<'_>, db: &(dyn SyntaxGroup + 'a)) -> fmt::Result {
        write!(f, r#"Attribute {{ id: "{}""#, self.id)?;
        if !self.args.is_empty() {
            write!(f, ", args: [")?;
            for arg in self.args.iter() {
                write!(f, "{:?}, ", arg.arg.as_syntax_node().get_text(db))?;
            }
            write!(f, "]")?;
        }
        write!(f, " }}")
    }
}

pub trait AttributeStructurize {
    /// Return the structured attribute for the given [ast::Attribute].
    fn structurize(self, db: &dyn SyntaxGroup) -> Attribute;
}

impl AttributeStructurize for ast::Attribute {
    fn structurize(self, db: &dyn SyntaxGroup) -> Attribute {
        let attr_id = self.attr(db);
        let attr_args = self.arguments(db);

        Attribute {
            stable_ptr: self.stable_ptr(),
            id: attr_id.as_syntax_node().get_text_without_trivia(db).into(),
            id_stable_ptr: attr_id.stable_ptr(),
            args: match attr_args {
                ast::OptionArgListParenthesized::ArgListParenthesized(ref attribute_args) => {
                    attribute_args
                        .arguments(db)
                        .elements(db)
                        .into_iter()
                        .map(|arg| AttributeArg::from_ast(arg, db))
                        .collect()
                }
                ast::OptionArgListParenthesized::Empty(_) => vec![],
            },
            args_stable_ptr: attr_args.stable_ptr(),
        }
    }
}

pub trait AttributeListStructurize {
    /// Return structured attributes for the given [ast::AttributeList].
    fn structurize(self, db: &dyn SyntaxGroup) -> Vec<Attribute>;
}

impl AttributeListStructurize for ast::AttributeList {
    fn structurize(self, db: &dyn SyntaxGroup) -> Vec<Attribute> {
        // TODO(ilya): Consider checking for attribute repetitions.
        self.elements(db).into_iter().map(|attr| attr.structurize(db)).collect()
    }
}

impl AttributeArg {
    /// Build [`AttributeArg`] from [`ast::Arg`].
    pub fn from_ast(arg: ast::Arg, db: &dyn SyntaxGroup) -> AttributeArg {
        let variant = match arg.arg_clause(db) {
            ast::ArgClause::Unnamed(clause) => AttributeArgVariant::Unnamed(clause.value(db)),
            ast::ArgClause::Named(clause) => AttributeArgVariant::Named {
                value: clause.value(db),
                name: NameInfo::from_ast(clause.name(db), db),
            },
            ast::ArgClause::FieldInitShorthand(clause) => AttributeArgVariant::FieldInitShorthand(
                NameInfo::from_ast(clause.name(db).name(db), db),
            ),
        };

        let modifiers = arg
            .modifiers(db)
            .elements(db)
            .into_iter()
            .map(|modifier| Modifier::from(modifier, db))
            .collect();

        AttributeArg { variant, arg, modifiers }
    }

    pub fn text(&self, db: &dyn SyntaxGroup) -> String {
        match &self.variant {
            AttributeArgVariant::Unnamed(value) => {
                value.as_syntax_node().get_text_without_trivia(db)
            }
            AttributeArgVariant::Named { value, name } => {
                format!("{}: {}", name.text, value.as_syntax_node().get_text_without_trivia(db))
            }
            AttributeArgVariant::FieldInitShorthand(name) => {
                format!(":{}", name.text)
            }
        }
    }
}

impl Modifier {
    /// Build [`Modifier`] from [`ast::Modifier`].
    fn from(modifier: ast::Modifier, db: &dyn SyntaxGroup) -> Modifier {
        Modifier {
            stable_ptr: modifier.stable_ptr(),
            text: modifier.as_syntax_node().get_text(db).into(),
        }
    }
}