Skip to main content

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