cairo_lang_starknet/inline_macros/
get_dep_component.rs

1use cairo_lang_defs::extract_macro_unnamed_args;
2use cairo_lang_defs::patcher::{PatchBuilder, RewriteNode};
3use cairo_lang_defs::plugin::{
4    InlineMacroExprPlugin, InlinePluginResult, MacroPluginMetadata, NamedPlugin, PluginDiagnostic,
5    PluginGeneratedFile,
6};
7use cairo_lang_syntax::node::db::SyntaxGroup;
8use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode, ast};
9use cairo_lang_utils::extract_matches;
10
11/// Macro for getting a component given a contract state that has it.
12#[derive(Debug, Default)]
13pub struct GetDepComponentMacro;
14impl NamedPlugin for GetDepComponentMacro {
15    const NAME: &'static str = "get_dep_component";
16}
17impl InlineMacroExprPlugin for GetDepComponentMacro {
18    fn generate_code(
19        &self,
20        db: &dyn SyntaxGroup,
21        syntax: &ast::ExprInlineMacro,
22        _metadata: &MacroPluginMetadata<'_>,
23    ) -> InlinePluginResult {
24        get_dep_component_generate_code_helper(db, syntax, false)
25    }
26}
27
28/// Macro for getting a mutable component given a mutable contract state that has it.
29#[derive(Debug, Default)]
30pub struct GetDepComponentMutMacro;
31impl NamedPlugin for GetDepComponentMutMacro {
32    const NAME: &'static str = "get_dep_component_mut";
33}
34impl InlineMacroExprPlugin for GetDepComponentMutMacro {
35    fn generate_code(
36        &self,
37        db: &dyn SyntaxGroup,
38        syntax: &ast::ExprInlineMacro,
39        _metadata: &MacroPluginMetadata<'_>,
40    ) -> InlinePluginResult {
41        get_dep_component_generate_code_helper(db, syntax, true)
42    }
43}
44
45/// A helper function for the code generation of both `DepComponentMacro` and
46/// `DepComponentMutMacro`. `is_mut` selects between the two.
47fn get_dep_component_generate_code_helper(
48    db: &dyn SyntaxGroup,
49    syntax: &ast::ExprInlineMacro,
50    is_mut: bool,
51) -> InlinePluginResult {
52    let [contract_arg, component_impl_arg] =
53        extract_macro_unnamed_args!(db, syntax, 2, ast::WrappedArgList::ParenthesizedArgList(_));
54
55    if is_mut {
56        // `extract_macro_unnamed_args` above guarantees that we have `ParenthesizedArgList`.
57        let contract_arg_modifiers =
58            extract_matches!(syntax.arguments(db), ast::WrappedArgList::ParenthesizedArgList)
59                .arguments(db)
60                .elements(db)[0]
61                .modifiers(db)
62                .elements(db);
63
64        // Verify the first element has only a `ref` modifier.
65        if !matches!(&contract_arg_modifiers[..], &[ast::Modifier::Ref(_)]) {
66            // TODO(Gil): The generated diagnostics points to the whole inline macro, it should
67            // point to the arg.
68            let diagnostics = vec![PluginDiagnostic::error(
69                contract_arg.stable_ptr().untyped(),
70                format!(
71                    "The first argument of `{}` macro must have only a `ref` modifier.",
72                    GetDepComponentMutMacro::NAME
73                ),
74            )];
75            return InlinePluginResult { code: None, diagnostics };
76        };
77    }
78    let mut builder = PatchBuilder::new(db, syntax);
79    let (let_part, maybe_mut, maybe_ref) =
80        if is_mut { ("let mut", "_mut", "ref ") } else { ("let", "", "") };
81    builder.add_modified(RewriteNode::interpolate_patched(
82        &format!(
83            "
84            {{
85                {let_part} __get_dep_component_macro_temp_contract__ = \
86         HasComponent::get_contract{maybe_mut}({maybe_ref}$contract_path$);
87                $component_impl_path$::get_component{maybe_mut}({maybe_ref}\
88         __get_dep_component_macro_temp_contract__)
89            }}
90            "
91        ),
92        &[
93            ("contract_path".to_string(), RewriteNode::from_ast_trimmed(&contract_arg)),
94            ("component_impl_path".to_string(), RewriteNode::from_ast_trimmed(&component_impl_arg)),
95        ]
96        .into(),
97    ));
98
99    let (content, code_mappings) = builder.build();
100    InlinePluginResult {
101        code: Some(PluginGeneratedFile {
102            name: "get_dep_component_inline_macro".into(),
103            content,
104            code_mappings,
105            aux_data: None,
106            diagnostics_note: Default::default(),
107        }),
108        diagnostics: vec![],
109    }
110}