near_sdk_macros/
lib.rs

1#![recursion_limit = "128"]
2extern crate proc_macro;
3
4mod core_impl;
5
6use core_impl::{ext::generate_ext_structs, metadata::generate_contract_metadata_method};
7
8use proc_macro::TokenStream;
9
10use self::core_impl::*;
11use darling::ast::NestedMeta;
12use darling::{Error, FromMeta};
13use inflector::Inflector;
14use proc_macro2::{Ident, Span};
15use quote::{quote, ToTokens};
16use syn::{
17    parse_quote, Expr, ImplItem, Item, ItemEnum, ItemImpl, ItemStruct, ItemTrait, WhereClause,
18};
19
20#[derive(Debug, Clone)]
21struct Serializers {
22    vec: Vec<Expr>,
23}
24
25impl FromMeta for Serializers {
26    fn from_expr(expr: &syn::Expr) -> Result<Self, darling::Error> {
27        match expr {
28            syn::Expr::Array(expr_array) => Ok(Serializers {
29                vec: expr_array
30                    .elems
31                    .iter()
32                    .map(<Expr as FromMeta>::from_expr)
33                    .map(|x| x.unwrap())
34                    .collect::<Vec<_>>(),
35            }),
36            _ => Err(Error::unexpected_expr_type(expr)),
37        }
38    }
39}
40
41#[derive(FromMeta)]
42struct NearMacroArgs {
43    serializers: Option<Serializers>,
44    #[darling(default, flatten)]
45    near_bindgen_args: NearBindgenMacroArgs,
46    inside_nearsdk: Option<bool>,
47}
48
49fn has_nested_near_macros(item: TokenStream) -> bool {
50    syn::parse::<syn::Item>(item)
51        .ok()
52        .and_then(|item_ast| {
53            let attrs = match item_ast {
54                syn::Item::Struct(s) => s.attrs,
55                syn::Item::Enum(e) => e.attrs,
56                syn::Item::Impl(i) => i.attrs,
57                _ => vec![], // Other cases don't support near macros anyway
58            };
59
60            attrs.into_iter().find(|attr| {
61                let path_str = attr.path().to_token_stream().to_string();
62                path_str == "near" || path_str == "near_bindgen"
63            })
64        })
65        .is_some()
66}
67
68#[proc_macro_attribute]
69pub fn near(attr: TokenStream, item: TokenStream) -> TokenStream {
70    if attr.to_string().contains("event_json") {
71        return core_impl::near_events(attr, item);
72    }
73
74    let meta_list = match NestedMeta::parse_meta_list(attr.into()) {
75        Ok(v) => v,
76        Err(e) => {
77            return TokenStream::from(Error::from(e).write_errors());
78        }
79    };
80
81    let near_macro_args = match NearMacroArgs::from_list(&meta_list) {
82        Ok(v) => v,
83        Err(e) => {
84            return TokenStream::from(e.write_errors());
85        }
86    };
87
88    let near_sdk_crate = if near_macro_args.inside_nearsdk.unwrap_or(false) {
89        quote! {crate}
90    } else {
91        quote! {::near_sdk}
92    };
93
94    // Check for nested near macros by parsing the input and examining actual attributes
95    if has_nested_near_macros(item.clone()) {
96        return TokenStream::from(
97            syn::Error::new(
98                Span::call_site(),
99                "#[near] or #[near_bindgen] attributes are not allowed to be nested inside of the outmost #[near] attribute. Only a single #[near] attribute is allowed",
100            )
101            .to_compile_error(),
102        );
103    }
104    let string_borsh_crate = quote! {#near_sdk_crate::borsh}.to_string();
105    let string_serde_crate = quote! {#near_sdk_crate::serde}.to_string();
106    let string_serde_with_crate = quote! {#near_sdk_crate::serde_with}.to_string();
107
108    let mut expanded: proc_macro2::TokenStream = quote! {};
109
110    if near_macro_args.near_bindgen_args.contract_state.is_some() {
111        let near_bindgen_args = near_macro_args.near_bindgen_args;
112        expanded = quote! {#[#near_sdk_crate::near_bindgen(#near_bindgen_args)]};
113    };
114
115    let mut has_borsh = false;
116    let mut has_json = false;
117
118    let mut borsh_attr = quote! {};
119
120    match near_macro_args.serializers {
121        Some(serializers) => {
122            let attr2 = serializers.clone();
123
124            attr2.vec.iter().for_each(|old_expr| {
125                let new_expr = &mut old_expr.clone();
126                match &mut *new_expr {
127                    Expr::Call(ref mut call_expr) => {
128                        if let Expr::Path(ref mut path) = &mut *call_expr.func {
129                            if let Some(ident) = path.path.get_ident() {
130                                if *ident == "json" {
131                                    has_json = true;
132                                    path.path =
133                                        syn::Path::from(Ident::new("serde", Span::call_site()));
134                                    call_expr.args.push(parse_quote! {crate=#string_serde_crate});
135                                } else if *ident == "borsh" {
136                                    has_borsh = true;
137                                    call_expr.args.push(parse_quote! {crate=#string_borsh_crate});
138                                }
139                            }
140                        }
141                        borsh_attr = quote! {#[#new_expr]};
142                    }
143                    Expr::Path(ref mut path_expr) => {
144                        if let Some(ident) = path_expr.path.get_ident() {
145                            if *ident == "json" {
146                                has_json = true;
147                            }
148                            if *ident == "borsh" {
149                                has_borsh = true;
150                                borsh_attr = quote! {#[borsh(crate=#string_borsh_crate)]};
151                            }
152                        }
153                    }
154                    _ => {}
155                }
156            });
157        }
158        None => {
159            has_borsh = true;
160            borsh_attr = quote! {#[borsh(crate = #string_borsh_crate)]};
161        }
162    }
163
164    // Add serde_as BEFORE schema_derive so that serde_with can process
165    // the schemars = true parameter and inject schema attributes before
166    // JsonSchema derive macro runs
167    if has_json {
168        let enable_schemars = cfg!(feature = "abi").then_some(quote! {schemars = true,});
169        expanded = quote! {
170            #expanded
171            #[#near_sdk_crate::serde_with::serde_as(
172                crate = #string_serde_with_crate,
173                #enable_schemars
174            )]
175            #[derive(#near_sdk_crate::serde::Serialize, #near_sdk_crate::serde::Deserialize)]
176            #[serde(crate = #string_serde_crate)]
177        };
178    }
179
180    #[cfg(feature = "abi")]
181    {
182        let schema_derive: proc_macro2::TokenStream =
183            get_schema_derive(has_json, has_borsh, near_sdk_crate.clone(), false);
184        expanded = quote! {
185            #expanded
186            #schema_derive
187        };
188    }
189
190    if has_borsh {
191        expanded = quote! {
192            #expanded
193            #[derive(#near_sdk_crate::borsh::BorshSerialize, #near_sdk_crate::borsh::BorshDeserialize)]
194            #borsh_attr
195        };
196    }
197
198    if let Ok(input) = syn::parse::<ItemStruct>(item.clone()) {
199        expanded = quote! {
200            #expanded
201            #input
202        };
203    } else if let Ok(input) = syn::parse::<ItemEnum>(item.clone()) {
204        expanded = quote! {
205            #expanded
206            #input
207        };
208    } else if let Ok(input) = syn::parse::<ItemImpl>(item) {
209        expanded = quote! {
210            #[#near_sdk_crate::near_bindgen]
211            #input
212        };
213    } else {
214        return TokenStream::from(
215            syn::Error::new(
216                Span::call_site(),
217                "near macro can only be used on struct or enum definition and impl sections.",
218            )
219            .to_compile_error(),
220        );
221    }
222
223    TokenStream::from(expanded)
224}
225
226#[derive(Default, FromMeta)]
227struct NearBindgenMacroArgs {
228    contract_state: Option<core_impl::state::ContractStateArgs>,
229    #[darling(default)]
230    contract_metadata: core_impl::ContractMetadata,
231}
232
233impl quote::ToTokens for NearBindgenMacroArgs {
234    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
235        let contract_state = &self.contract_state;
236        let contract_metadata = &self.contract_metadata;
237        tokens.extend(quote! {
238            contract_state(#contract_state),
239            contract_metadata(#contract_metadata),
240        })
241    }
242}
243
244#[proc_macro_attribute]
245pub fn near_bindgen(attr: TokenStream, item: TokenStream) -> TokenStream {
246    if attr.to_string().contains("event_json") {
247        return core_impl::near_events(attr, item);
248    }
249
250    let (ident, generics) = if let Ok(input) = syn::parse::<ItemStruct>(item.clone()) {
251        (input.ident, input.generics)
252    } else if let Ok(input) = syn::parse::<ItemEnum>(item.clone()) {
253        (input.ident, input.generics)
254    } else if let Ok(input) = syn::parse::<ItemImpl>(item) {
255        for method in &input.items {
256            if let ImplItem::Fn(m) = method {
257                let ident = &m.sig.ident;
258                if ident.eq("__contract_abi") || ident.eq("contract_source_metadata") {
259                    return TokenStream::from(
260                        syn::Error::new_spanned(
261                            ident.to_token_stream(),
262                            "use of reserved contract method",
263                        )
264                        .to_compile_error(),
265                    );
266                }
267            }
268        }
269        return match process_impl_block(input) {
270            Ok(output) => output,
271            Err(output) => output,
272        }
273        .into();
274    } else {
275        return TokenStream::from(
276            syn::Error::new(
277                Span::call_site(),
278                "near_bindgen can only be used on struct or enum definition and impl sections.",
279            )
280            .to_compile_error(),
281        );
282    };
283
284    let attr_args = match NestedMeta::parse_meta_list(attr.into()) {
285        Ok(v) => v,
286        Err(e) => {
287            return Error::from(e).write_errors().into();
288        }
289    };
290
291    let args = match NearBindgenMacroArgs::from_list(&attr_args) {
292        Ok(v) => v,
293        Err(e) => {
294            return e.write_errors().into();
295        }
296    };
297
298    let impl_contract_state = args.contract_state.map(|s| s.impl_contract_state(&ident, &generics));
299    let metadata = args.contract_metadata.contract_source_metadata_const();
300
301    let metadata_impl_gen = {
302        let metadata_impl_gen = generate_contract_metadata_method(&ident, &generics).into();
303
304        let metadata_impl_gen = syn::parse::<ItemImpl>(metadata_impl_gen)
305            .expect("failed to generate contract metadata");
306        process_impl_block(metadata_impl_gen)
307    };
308
309    let metadata_impl_gen = match metadata_impl_gen {
310        Ok(metadata) => metadata,
311        Err(err) => return err.into(),
312    };
313
314    let ext_gen = generate_ext_structs(&ident, Some(&generics));
315    #[cfg(feature = "__abi-embed-checked")]
316    let abi_embedded = abi::embed();
317    #[cfg(not(feature = "__abi-embed-checked"))]
318    let abi_embedded = quote! {};
319    let item = proc_macro2::TokenStream::from(item);
320    TokenStream::from(quote! {
321        #item
322        #ext_gen
323        #abi_embedded
324        #metadata
325        #metadata_impl_gen
326        #impl_contract_state
327    })
328}
329
330// This function deals with impl block processing, generating wrappers and ABI.
331//
332// # Arguments
333// * input - impl block to process.
334//
335// The Result has a TokenStream error type, because those need to be propagated to the compiler.
336fn process_impl_block(
337    mut input: ItemImpl,
338) -> Result<proc_macro2::TokenStream, proc_macro2::TokenStream> {
339    let item_impl_info = match ItemImplInfo::new(&mut input) {
340        Ok(x) => x,
341        Err(err) => return Err(err.to_compile_error()),
342    };
343
344    #[cfg(not(feature = "__abi-generate"))]
345    let abi_generated = quote! {};
346    #[cfg(feature = "__abi-generate")]
347    let abi_generated = abi::generate(&item_impl_info);
348
349    let generated_code = item_impl_info.wrapper_code();
350
351    // Add wrapper methods for ext call API
352    let ext_generated_code = item_impl_info.generate_ext_wrapper_code();
353
354    Ok(TokenStream::from(quote! {
355        #ext_generated_code
356        #input
357        #generated_code
358        #abi_generated
359    })
360    .into())
361}
362
363#[proc_macro_attribute]
364pub fn ext_contract(attr: TokenStream, item: TokenStream) -> TokenStream {
365    if let Ok(mut input) = syn::parse::<ItemTrait>(item) {
366        let mod_name: Option<proc_macro2::Ident> = if attr.is_empty() {
367            None
368        } else {
369            match syn::parse(attr) {
370                Ok(x) => x,
371                Err(err) => {
372                    return TokenStream::from(
373                        syn::Error::new(
374                            Span::call_site(),
375                            format!("Failed to parse mod name for ext_contract: {err}"),
376                        )
377                        .to_compile_error(),
378                    )
379                }
380            }
381        };
382        let item_trait_info = match ItemTraitInfo::new(&mut input, mod_name) {
383            Ok(x) => x,
384            Err(err) => return TokenStream::from(err.to_compile_error()),
385        };
386        let ext_api = item_trait_info.wrap_trait_ext();
387
388        TokenStream::from(quote! {
389            #input
390            #ext_api
391        })
392    } else {
393        TokenStream::from(
394            syn::Error::new(Span::call_site(), "ext_contract can only be used on traits")
395                .to_compile_error(),
396        )
397    }
398}
399
400#[cfg(feature = "abi")]
401#[derive(darling::FromDeriveInput, Debug)]
402#[darling(attributes(abi), forward_attrs(serde, borsh_skip, schemars, validate))]
403struct DeriveNearSchema {
404    attrs: Vec<syn::Attribute>,
405    json: Option<bool>,
406    borsh: Option<bool>,
407}
408
409#[proc_macro_derive(NearSchema, attributes(abi, serde, borsh, schemars, validate, inside_nearsdk))]
410pub fn derive_near_schema(#[allow(unused)] input: TokenStream) -> TokenStream {
411    #[cfg(not(feature = "abi"))]
412    {
413        TokenStream::from(quote! {})
414    }
415
416    #[cfg(feature = "abi")]
417    {
418        use darling::FromDeriveInput;
419
420        let derive_input = syn::parse_macro_input!(input as syn::DeriveInput);
421        let generics = derive_input.generics.clone();
422        let args = match DeriveNearSchema::from_derive_input(&derive_input) {
423            Ok(v) => v,
424            Err(e) => {
425                return TokenStream::from(e.write_errors());
426            }
427        };
428
429        if args.borsh.is_none()
430            && args.json.is_none()
431            && derive_input.clone().attrs.iter().any(|attr| attr.path().is_ident("abi"))
432        {
433            return TokenStream::from(
434                syn::Error::new_spanned(
435                    derive_input.to_token_stream(),
436                    "At least one of `json` or `borsh` inside of `#[abi(...)]` must be specified",
437                )
438                .to_compile_error(),
439            );
440        }
441
442        // #[abi(json, borsh)]
443        let (json_schema, borsh_schema) = (args.json.unwrap_or(false), args.borsh.unwrap_or(false));
444        let mut input = derive_input.clone();
445        input.attrs = args.attrs;
446
447        let strip_unknown_attr = |attrs: &mut Vec<syn::Attribute>| {
448            attrs.retain(|attr| {
449                ["serde", "schemars", "validate", "borsh"]
450                    .iter()
451                    .any(|&path| attr.path().is_ident(path))
452            });
453        };
454
455        match &mut input.data {
456            syn::Data::Struct(data) => {
457                for field in &mut data.fields {
458                    strip_unknown_attr(&mut field.attrs);
459                }
460            }
461            syn::Data::Enum(data) => {
462                for variant in &mut data.variants {
463                    strip_unknown_attr(&mut variant.attrs);
464                    for field in &mut variant.fields {
465                        strip_unknown_attr(&mut field.attrs);
466                    }
467                }
468            }
469            syn::Data::Union(_) => {
470                return TokenStream::from(
471                    syn::Error::new_spanned(
472                        input.to_token_stream(),
473                        "`NearSchema` does not support derive for unions",
474                    )
475                    .to_compile_error(),
476                )
477            }
478        }
479
480        let near_sdk_crate =
481            if derive_input.attrs.iter().any(|attr| attr.path().is_ident("inside_nearsdk")) {
482                quote! {crate}
483            } else {
484                quote! {::near_sdk}
485            };
486
487        // <unspecified> or #[abi(json)]
488        let json_schema = json_schema || !borsh_schema;
489
490        let derive = get_schema_derive(json_schema, borsh_schema, near_sdk_crate.clone(), true);
491
492        let input_ident = &input.ident;
493
494        let input_ident_proxy = quote::format_ident!("{}__NEAR_SCHEMA_PROXY", input_ident);
495
496        let json_impl = if json_schema {
497            let where_clause = get_where_clause(
498                &generics,
499                input_ident,
500                quote! {#near_sdk_crate::schemars::JsonSchema},
501            );
502            quote! {
503                #[automatically_derived]
504                impl #generics #near_sdk_crate::schemars::JsonSchema for #input_ident_proxy #generics #where_clause {
505                    fn schema_name() -> ::std::string::String {
506                        <#input_ident #generics as #near_sdk_crate::schemars::JsonSchema>::schema_name()
507                    }
508
509                    fn json_schema(gen: &mut #near_sdk_crate::schemars::gen::SchemaGenerator) -> #near_sdk_crate::schemars::schema::Schema {
510                        <#input_ident #generics as #near_sdk_crate::schemars::JsonSchema>::json_schema(gen)
511                    }
512                }
513            }
514        } else {
515            quote! {}
516        };
517
518        let borsh_impl = if borsh_schema {
519            let where_clause = get_where_clause(
520                &generics,
521                input_ident,
522                quote! {#near_sdk_crate::borsh::BorshSchema},
523            );
524            quote! {
525                #[automatically_derived]
526                impl #generics #near_sdk_crate::borsh::BorshSchema for #input_ident_proxy #generics #where_clause {
527                    fn declaration() -> #near_sdk_crate::borsh::schema::Declaration {
528                        <#input_ident #generics as #near_sdk_crate::borsh::BorshSchema>::declaration()
529                    }
530
531                    fn add_definitions_recursively(
532                        definitions: &mut #near_sdk_crate::borsh::__private::maybestd::collections::BTreeMap<
533                            #near_sdk_crate::borsh::schema::Declaration,
534                            #near_sdk_crate::borsh::schema::Definition
535                        >,
536                    ) {
537                        <#input_ident #generics as #near_sdk_crate::borsh::BorshSchema>::add_definitions_recursively(definitions);
538                    }
539                }
540            }
541        } else {
542            quote! {}
543        };
544
545        TokenStream::from(quote! {
546            #[cfg(not(target_arch = "wasm32"))]
547            const _: () = {
548                #[allow(non_camel_case_types)]
549                type #input_ident_proxy #generics = #input_ident #generics;
550                {
551                    #derive
552                    #[allow(dead_code)]
553                    #input
554
555                    #json_impl
556                    #borsh_impl
557                };
558            };
559        })
560    }
561}
562
563#[allow(dead_code)]
564fn get_schema_derive(
565    json_schema: bool,
566    borsh_schema: bool,
567    near_sdk_crate: proc_macro2::TokenStream,
568    need_borsh_crate: bool,
569) -> proc_macro2::TokenStream {
570    let string_borsh_crate = quote! {#near_sdk_crate::borsh}.to_string();
571    let string_schemars_crate = quote! {#near_sdk_crate::schemars}.to_string();
572
573    let mut derive = quote! {};
574    if borsh_schema {
575        derive = quote! {
576            #[cfg_attr(not(target_arch = "wasm32"), derive(#near_sdk_crate::borsh::BorshSchema))]
577        };
578        if need_borsh_crate {
579            derive = quote! {
580                #derive
581                #[cfg_attr(not(target_arch = "wasm32"), borsh(crate = #string_borsh_crate))]
582            };
583        }
584    }
585    if json_schema {
586        derive = quote! {
587            #derive
588            #[cfg_attr(not(target_arch = "wasm32"), derive(#near_sdk_crate::schemars::JsonSchema))]
589            #[cfg_attr(not(target_arch = "wasm32"), schemars(crate = #string_schemars_crate))]
590        };
591    }
592    derive
593}
594
595#[cfg(feature = "abi")]
596fn get_where_clause(
597    generics: &syn::Generics,
598    input_ident: &syn::Ident,
599    trait_name: proc_macro2::TokenStream,
600) -> WhereClause {
601    let (_, ty_generics, where_clause) = generics.split_for_impl();
602
603    let predicate = parse_quote!(#input_ident #ty_generics: #trait_name);
604
605    let where_clause: WhereClause = if let Some(mut w) = where_clause.cloned() {
606        w.predicates.push(predicate);
607        w
608    } else {
609        parse_quote!(where #predicate)
610    };
611    where_clause
612}
613
614#[proc_macro_derive(PanicOnDefault)]
615pub fn derive_no_default(item: TokenStream) -> TokenStream {
616    match syn::parse::<Item>(item) {
617        Ok(Item::Enum(ItemEnum { ident, .. })) | Ok(Item::Struct(ItemStruct { ident, .. })) => {
618            TokenStream::from(quote! {
619                const _: () = {
620                    #[automatically_derived]
621                    impl ::std::default::Default for #ident {
622                        fn default() -> Self {
623                            ::near_sdk::env::panic_str("The contract is not initialized");
624                        }
625                    }
626                };
627            })
628        }
629        _ => TokenStream::from(
630            syn::Error::new(
631                Span::call_site(),
632                "PanicOnDefault can only be used on type declarations sections.",
633            )
634            .to_compile_error(),
635        ),
636    }
637}
638
639#[proc_macro_derive(BorshStorageKey)]
640pub fn borsh_storage_key(item: TokenStream) -> TokenStream {
641    let (name, generics) = if let Ok(input) = syn::parse::<ItemEnum>(item.clone()) {
642        (input.ident, input.generics)
643    } else if let Ok(input) = syn::parse::<ItemStruct>(item) {
644        (input.ident, input.generics)
645    } else {
646        return TokenStream::from(
647            syn::Error::new(
648                Span::call_site(),
649                "BorshStorageKey can only be used as a derive on enums or structs.",
650            )
651            .to_compile_error(),
652        );
653    };
654    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
655    let predicate = parse_quote!(#name #ty_generics: ::near_sdk::borsh::BorshSerialize);
656    let where_clause: WhereClause = if let Some(mut w) = where_clause.cloned() {
657        w.predicates.push(predicate);
658        w
659    } else {
660        parse_quote!(where #predicate)
661    };
662    TokenStream::from(quote! {
663        impl #impl_generics ::near_sdk::__private::BorshIntoStorageKey for #name #ty_generics #where_clause {}
664    })
665}
666
667#[proc_macro_derive(FunctionError)]
668pub fn function_error(item: TokenStream) -> TokenStream {
669    let name = if let Ok(input) = syn::parse::<ItemEnum>(item.clone()) {
670        input.ident
671    } else if let Ok(input) = syn::parse::<ItemStruct>(item) {
672        input.ident
673    } else {
674        return TokenStream::from(
675            syn::Error::new(
676                Span::call_site(),
677                "FunctionError can only be used as a derive on enums or structs.",
678            )
679            .to_compile_error(),
680        );
681    };
682    TokenStream::from(quote! {
683        impl ::near_sdk::FunctionError for #name {
684            fn panic(&self) -> ! {
685                ::near_sdk::env::panic_str(&::std::string::ToString::to_string(&self))
686            }
687        }
688    })
689}
690
691#[proc_macro_derive(EventMetadata, attributes(event_version))]
692pub fn derive_event_attributes(item: TokenStream) -> TokenStream {
693    if let Ok(input) = syn::parse::<ItemEnum>(item) {
694        let name = &input.ident;
695        // get `standard` const injected from `near_events`
696        let standard_name = format!("{name}_event_standard");
697        let standard_ident = syn::Ident::new(&standard_name, Span::call_site());
698        // version from each attribute macro
699        let mut event_meta: Vec<proc_macro2::TokenStream> = vec![];
700        let mut version_arms: Vec<proc_macro2::TokenStream> = vec![];
701        let mut event_name_arms: Vec<proc_macro2::TokenStream> = vec![];
702        for var in &input.variants {
703            if let Some(version) = core_impl::get_event_version(var) {
704                let var_ident = &var.ident;
705                event_meta.push(quote! {
706                    #name::#var_ident { .. } => {(::std::string::ToString::to_string(&#standard_ident), ::std::string::ToString::to_string(#version))}
707                });
708                version_arms.push(quote! {
709                    #name::#var_ident { .. } => ::std::borrow::Cow::Borrowed(#version)
710                });
711                let event_name =
712                    proc_macro2::Literal::string(&var.ident.to_string().to_snake_case());
713                event_name_arms.push(quote! {
714                    #name::#var_ident { .. } => ::std::borrow::Cow::Borrowed(#event_name)
715                });
716            } else {
717                return TokenStream::from(
718                    syn::Error::new(
719                        Span::call_site(),
720                        "Near events must have `event_version`. Must have a single string literal value.",
721                    )
722                    .to_compile_error(),
723                );
724            }
725        }
726
727        // handle lifetimes, generics, and where clauses
728        let (impl_generics, type_generics, where_clause) = &input.generics.split_for_impl();
729        // add `'near_event` lifetime for user defined events
730        let mut generics = input.generics.clone();
731        let event_lifetime = syn::Lifetime::new("'near_event", Span::call_site());
732        generics.params.insert(
733            0,
734            syn::GenericParam::Lifetime(syn::LifetimeParam::new(event_lifetime.clone())),
735        );
736
737        TokenStream::from(quote! {
738            impl #impl_generics #name #type_generics #where_clause {
739                pub fn emit(&self) {
740                    use ::near_sdk::AsNep297Event;
741                    ::near_sdk::env::log_str(&self.to_nep297_event().to_event_log());
742                }
743
744                pub fn to_json(&self) -> ::near_sdk::serde_json::Value {
745                    use ::near_sdk::AsNep297Event;
746                    self.to_nep297_event().to_json()
747                }
748
749                pub fn standard(&self) -> ::std::borrow::Cow<'_, str> {
750                    ::std::borrow::Cow::Borrowed(#standard_ident)
751                }
752
753                pub fn version(&self) -> ::std::borrow::Cow<'_, str> {
754                    match self {
755                        #(#version_arms),*
756                    }
757                }
758
759                pub fn event(&self) -> ::std::borrow::Cow<'_, str> {
760                    match self {
761                        #(#event_name_arms),*
762                    }
763                }
764            }
765
766            impl #impl_generics ::near_sdk::events::AsNep297Event for #name #type_generics #where_clause{
767                fn to_nep297_event(&self) -> ::near_sdk::events::Nep297Event<'_, Self> {
768                    ::near_sdk::events::Nep297Event::new(
769                        ::std::borrow::Cow::Borrowed(#standard_ident),
770                        match self {
771                            #(#version_arms),*
772                        },
773                        match self {
774                            #(#event_name_arms),*
775                        },
776                        self,
777                    )
778                }
779            }
780
781
782        })
783    } else {
784        TokenStream::from(
785            syn::Error::new(
786                Span::call_site(),
787                "EventMetadata can only be used as a derive on enums.",
788            )
789            .to_compile_error(),
790        )
791    }
792}