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 proc_macro2::{Ident, Span};
14use quote::{quote, ToTokens};
15use syn::{parse_quote, Expr, ImplItem, ItemEnum, ItemImpl, ItemStruct, ItemTrait, WhereClause};
16
17#[derive(Debug, Clone)]
18struct Serializers {
19 vec: Vec<Expr>,
20}
21
22impl FromMeta for Serializers {
23 fn from_expr(expr: &syn::Expr) -> Result<Self, darling::Error> {
24 match expr {
25 syn::Expr::Array(expr_array) => Ok(Serializers {
26 vec: expr_array
27 .elems
28 .iter()
29 .map(<Expr as FromMeta>::from_expr)
30 .map(|x| x.unwrap())
31 .collect::<Vec<_>>(),
32 }),
33 _ => Err(Error::unexpected_expr_type(expr)),
34 }
35 }
36}
37
38#[derive(FromMeta)]
39struct UncMacroArgs {
40 serializers: Option<Serializers>,
41 contract_state: Option<bool>,
42 contract_metadata: Option<core_impl::ContractMetadata>,
43 inside_uncsdk: Option<bool>,
44}
45
46#[proc_macro_attribute]
98pub fn unc(attr: TokenStream, item: TokenStream) -> TokenStream {
99 if attr.to_string().contains("event_json") {
100 return core_impl::unc_events(attr, item);
101 }
102
103 let meta_list = match NestedMeta::parse_meta_list(attr.into()) {
104 Ok(v) => v,
105 Err(e) => {
106 return TokenStream::from(Error::from(e).write_errors());
107 }
108 };
109
110 let unc_macro_args = match UncMacroArgs::from_list(&meta_list) {
111 Ok(v) => v,
112 Err(e) => {
113 return TokenStream::from(e.write_errors());
114 }
115 };
116
117 let unc_sdk_crate = if unc_macro_args.inside_uncsdk.unwrap_or(false) {
118 quote! {crate}
119 } else {
120 quote! {::unc_sdk}
121 };
122 let string_borsh_crate = quote! {#unc_sdk_crate::borsh}.to_string();
123 let string_serde_crate = quote! {#unc_sdk_crate::serde}.to_string();
124
125 let mut expanded: proc_macro2::TokenStream = quote! {};
126
127 if unc_macro_args.contract_state.unwrap_or(false) {
128 if let Some(metadata) = unc_macro_args.contract_metadata {
129 expanded = quote! {#[#unc_sdk_crate::unc_bindgen(#metadata)]}
130 } else {
131 expanded = quote! {#[#unc_sdk_crate::unc_bindgen]}
132 }
133 };
134
135 let mut has_borsh = false;
136 let mut has_json = false;
137
138 let mut borsh_attr = quote! {};
139
140 match unc_macro_args.serializers {
141 Some(serializers) => {
142 let attr2 = serializers.clone();
143
144 attr2.vec.iter().for_each(|old_expr| {
145 let new_expr = &mut old_expr.clone();
146 match &mut *new_expr {
147 Expr::Call(ref mut call_expr) => {
148 if let Expr::Path(ref mut path) = &mut *call_expr.func {
149 if let Some(ident) = path.path.get_ident() {
150 if *ident == "json" {
151 has_json = true;
152 path.path =
153 syn::Path::from(Ident::new("serde", Span::call_site()));
154 call_expr.args.push(parse_quote! {crate=#string_serde_crate});
155 } else if *ident == "borsh" {
156 has_borsh = true;
157 call_expr.args.push(parse_quote! {crate=#string_borsh_crate});
158 }
159 }
160 }
161 borsh_attr = quote! {#[#new_expr]};
162 }
163 Expr::Path(ref mut path_expr) => {
164 if let Some(ident) = path_expr.path.get_ident() {
165 if *ident == "json" {
166 has_json = true;
167 }
168 if *ident == "borsh" {
169 has_borsh = true;
170 borsh_attr = quote! {#[borsh(crate=#string_borsh_crate)]};
171 }
172 }
173 }
174 _ => {}
175 }
176 });
177 }
178 None => {
179 has_borsh = true;
180 borsh_attr = quote! {#[borsh(crate = #string_borsh_crate)]};
181 }
182 }
183
184 #[cfg(feature = "abi")]
185 {
186 let schema_derive: proc_macro2::TokenStream =
187 get_schema_derive(has_json, has_borsh, unc_sdk_crate.clone(), false);
188 expanded = quote! {
189 #expanded
190 #schema_derive
191 };
192 }
193
194 if has_borsh {
195 expanded = quote! {
196 #expanded
197 #[derive(#unc_sdk_crate::borsh::BorshSerialize, #unc_sdk_crate::borsh::BorshDeserialize)]
198 #borsh_attr
199 };
200 }
201
202 if has_json {
203 expanded = quote! {
204 #expanded
205 #[derive(#unc_sdk_crate::serde::Serialize, #unc_sdk_crate::serde::Deserialize)]
206 #[serde(crate = #string_serde_crate)]
207 };
208 }
209
210 if let Ok(input) = syn::parse::<ItemStruct>(item.clone()) {
211 expanded = quote! {
212 #expanded
213 #input
214 };
215 } else if let Ok(input) = syn::parse::<ItemEnum>(item.clone()) {
216 expanded = quote! {
217 #expanded
218 #input
219 };
220 } else if let Ok(input) = syn::parse::<ItemImpl>(item) {
221 expanded = quote! {
222 #[#unc_sdk_crate::unc_bindgen]
223 #input
224 };
225 } else {
226 return TokenStream::from(
227 syn::Error::new(
228 Span::call_site(),
229 "unc macro can only be used on struct or enum definition and impl sections.",
230 )
231 .to_compile_error(),
232 );
233 }
234
235 TokenStream::from(expanded)
236}
237
238#[proc_macro_attribute]
336pub fn unc_bindgen(attr: TokenStream, item: TokenStream) -> TokenStream {
337 if attr.to_string().contains("event_json") {
338 return core_impl::unc_events(attr, item);
339 }
340
341 let generate_metadata = |ident: &Ident,
342 generics: &syn::Generics|
343 -> Result<proc_macro2::TokenStream, proc_macro2::TokenStream> {
344 let metadata_impl_gen = generate_contract_metadata_method(ident, generics).into();
345 let metadata_impl_gen = syn::parse::<ItemImpl>(metadata_impl_gen)
346 .expect("failed to generate contract metadata");
347 process_impl_block(metadata_impl_gen)
348 };
349
350 if let Ok(input) = syn::parse::<ItemStruct>(item.clone()) {
351 let metadata = core_impl::contract_source_metadata_const(attr);
352
353 let metadata_impl_gen = generate_metadata(&input.ident, &input.generics);
354
355 let metadata_impl_gen = match metadata_impl_gen {
356 Ok(metadata) => metadata,
357 Err(err) => return err.into(),
358 };
359
360 let ext_gen = generate_ext_structs(&input.ident, Some(&input.generics));
361 #[cfg(feature = "__abi-embed-checked")]
362 let abi_embedded = abi::embed();
363 #[cfg(not(feature = "__abi-embed-checked"))]
364 let abi_embedded = quote! {};
365 TokenStream::from(quote! {
366 #input
367 #ext_gen
368 #abi_embedded
369 #metadata
370 #metadata_impl_gen
371 })
372 } else if let Ok(input) = syn::parse::<ItemEnum>(item.clone()) {
373 let metadata = core_impl::contract_source_metadata_const(attr);
374 let metadata_impl_gen = generate_metadata(&input.ident, &input.generics);
375
376 let metadata_impl_gen = match metadata_impl_gen {
377 Ok(metadata) => metadata,
378 Err(err) => return err.into(),
379 };
380
381 let ext_gen = generate_ext_structs(&input.ident, Some(&input.generics));
382 #[cfg(feature = "__abi-embed-checked")]
383 let abi_embedded = abi::embed();
384 #[cfg(not(feature = "__abi-embed-checked"))]
385 let abi_embedded = quote! {};
386 TokenStream::from(quote! {
387 #input
388 #ext_gen
389 #abi_embedded
390 #metadata
391 #metadata_impl_gen
392 })
393 } else if let Ok(input) = syn::parse::<ItemImpl>(item) {
394 for method in &input.items {
395 if let ImplItem::Fn(m) = method {
396 let ident = &m.sig.ident;
397 if ident.eq("__contract_abi") || ident.eq("contract_source_metadata") {
398 return TokenStream::from(
399 syn::Error::new_spanned(
400 ident.to_token_stream(),
401 "use of reserved contract method",
402 )
403 .to_compile_error(),
404 );
405 }
406 }
407 }
408 match process_impl_block(input) {
409 Ok(output) => output,
410 Err(output) => output,
411 }
412 .into()
413 } else {
414 TokenStream::from(
415 syn::Error::new(
416 Span::call_site(),
417 "unc_bindgen can only be used on struct or enum definition and impl sections.",
418 )
419 .to_compile_error(),
420 )
421 }
422}
423
424fn process_impl_block(
431 mut input: ItemImpl,
432) -> Result<proc_macro2::TokenStream, proc_macro2::TokenStream> {
433 let item_impl_info = match ItemImplInfo::new(&mut input) {
434 Ok(x) => x,
435 Err(err) => return Err(err.to_compile_error()),
436 };
437
438 #[cfg(not(feature = "__abi-generate"))]
439 let abi_generated = quote! {};
440 #[cfg(feature = "__abi-generate")]
441 let abi_generated = abi::generate(&item_impl_info);
442
443 let generated_code = item_impl_info.wrapper_code();
444
445 let ext_generated_code = item_impl_info.generate_ext_wrapper_code();
447
448 Ok(TokenStream::from(quote! {
449 #ext_generated_code
450 #input
451 #generated_code
452 #abi_generated
453 })
454 .into())
455}
456
457#[proc_macro_attribute]
473pub fn ext_contract(attr: TokenStream, item: TokenStream) -> TokenStream {
474 if let Ok(mut input) = syn::parse::<ItemTrait>(item) {
475 let mod_name: Option<proc_macro2::Ident> = if attr.is_empty() {
476 None
477 } else {
478 match syn::parse(attr) {
479 Ok(x) => x,
480 Err(err) => {
481 return TokenStream::from(
482 syn::Error::new(
483 Span::call_site(),
484 format!("Failed to parse mod name for ext_contract: {}", err),
485 )
486 .to_compile_error(),
487 )
488 }
489 }
490 };
491 let item_trait_info = match ItemTraitInfo::new(&mut input, mod_name) {
492 Ok(x) => x,
493 Err(err) => return TokenStream::from(err.to_compile_error()),
494 };
495 let ext_api = item_trait_info.wrap_trait_ext();
496
497 TokenStream::from(quote! {
498 #input
499 #ext_api
500 })
501 } else {
502 TokenStream::from(
503 syn::Error::new(Span::call_site(), "ext_contract can only be used on traits")
504 .to_compile_error(),
505 )
506 }
507}
508
509#[proc_macro_attribute]
513#[deprecated(since = "2.0.0", note = "Case is handled internally by macro, no need to import")]
514pub fn callback(_attr: TokenStream, item: TokenStream) -> TokenStream {
515 item
516}
517
518#[deprecated(since = "2.0.0", note = "Case is handled internally by macro, no need to import")]
520#[proc_macro_attribute]
521pub fn callback_vec(_attr: TokenStream, item: TokenStream) -> TokenStream {
522 item
523}
524
525#[deprecated(since = "2.0.0", note = "Case is handled internally by macro, no need to import")]
527#[proc_macro_attribute]
528pub fn serializer(_attr: TokenStream, item: TokenStream) -> TokenStream {
529 item
530}
531
532#[deprecated(since = "2.0.0", note = "Case is handled internally by macro, no need to import")]
534#[proc_macro_attribute]
535pub fn result_serializer(_attr: TokenStream, item: TokenStream) -> TokenStream {
536 item
537}
538
539#[deprecated(since = "2.0.0", note = "Case is handled internally by macro, no need to import")]
541#[proc_macro_attribute]
542pub fn init(_attr: TokenStream, item: TokenStream) -> TokenStream {
543 item
544}
545
546#[cfg(feature = "abi")]
547#[derive(darling::FromDeriveInput, Debug)]
548#[darling(attributes(abi), forward_attrs(serde, borsh_skip, schemars, validate))]
549struct DeriveUncSchema {
550 attrs: Vec<syn::Attribute>,
551 json: Option<bool>,
552 borsh: Option<bool>,
553}
554
555#[proc_macro_derive(UncSchema, attributes(abi, serde, borsh, schemars, validate, inside_uncsdk))]
556pub fn derive_unc_schema(#[allow(unused)] input: TokenStream) -> TokenStream {
557 #[cfg(not(feature = "abi"))]
558 {
559 TokenStream::from(quote! {})
560 }
561
562 #[cfg(feature = "abi")]
563 {
564 use darling::FromDeriveInput;
565
566 let derive_input = syn::parse_macro_input!(input as syn::DeriveInput);
567 let generics = derive_input.generics.clone();
568 let args = match DeriveUncSchema::from_derive_input(&derive_input) {
569 Ok(v) => v,
570 Err(e) => {
571 return TokenStream::from(e.write_errors());
572 }
573 };
574
575 if args.borsh.is_none()
576 && args.json.is_none()
577 && derive_input.clone().attrs.iter().any(|attr| attr.path().is_ident("abi"))
578 {
579 return TokenStream::from(
580 syn::Error::new_spanned(
581 derive_input.to_token_stream(),
582 "At least one of `json` or `borsh` inside of `#[abi(...)]` must be specified",
583 )
584 .to_compile_error(),
585 );
586 }
587
588 let (json_schema, borsh_schema) = (args.json.unwrap_or(false), args.borsh.unwrap_or(false));
590 let mut input = derive_input.clone();
591 input.attrs = args.attrs;
592
593 let strip_unknown_attr = |attrs: &mut Vec<syn::Attribute>| {
594 attrs.retain(|attr| {
595 ["serde", "schemars", "validate", "borsh"]
596 .iter()
597 .any(|&path| attr.path().is_ident(path))
598 });
599 };
600
601 match &mut input.data {
602 syn::Data::Struct(data) => {
603 for field in &mut data.fields {
604 strip_unknown_attr(&mut field.attrs);
605 }
606 }
607 syn::Data::Enum(data) => {
608 for variant in &mut data.variants {
609 strip_unknown_attr(&mut variant.attrs);
610 for field in &mut variant.fields {
611 strip_unknown_attr(&mut field.attrs);
612 }
613 }
614 }
615 syn::Data::Union(_) => {
616 return TokenStream::from(
617 syn::Error::new_spanned(
618 input.to_token_stream(),
619 "`UncSchema` does not support derive for unions",
620 )
621 .to_compile_error(),
622 )
623 }
624 }
625
626 let unc_sdk_crate =
627 if derive_input.attrs.iter().any(|attr| attr.path().is_ident("inside_uncsdk")) {
628 quote! {crate}
629 } else {
630 quote! {::unc_sdk}
631 };
632
633 let json_schema = json_schema || !borsh_schema;
635
636 let derive = get_schema_derive(json_schema, borsh_schema, unc_sdk_crate.clone(), true);
637
638 let input_ident = &input.ident;
639
640 let input_ident_proxy = quote::format_ident!("{}__UNC_SCHEMA_PROXY", input_ident);
641
642 let json_impl = if json_schema {
643 let where_clause = get_where_clause(
644 &generics,
645 input_ident,
646 quote! {#unc_sdk_crate::schemars::JsonSchema},
647 );
648 quote! {
649 #[automatically_derived]
650 impl #generics #unc_sdk_crate::schemars::JsonSchema for #input_ident_proxy #generics #where_clause {
651 fn schema_name() -> ::std::string::String {
652 stringify!(#input_ident #generics).to_string()
653 }
654
655 fn json_schema(gen: &mut #unc_sdk_crate::schemars::gen::SchemaGenerator) -> #unc_sdk_crate::schemars::schema::Schema {
656 <#input_ident #generics as #unc_sdk_crate::schemars::JsonSchema>::json_schema(gen)
657 }
658 }
659 }
660 } else {
661 quote! {}
662 };
663
664 let borsh_impl = if borsh_schema {
665 let where_clause = get_where_clause(
666 &generics,
667 input_ident,
668 quote! {#unc_sdk_crate::borsh::BorshSchema},
669 );
670 quote! {
671 #[automatically_derived]
672 impl #generics #unc_sdk_crate::borsh::BorshSchema for #input_ident_proxy #generics #where_clause {
673 fn declaration() -> #unc_sdk_crate::borsh::schema::Declaration {
674 stringify!(#input_ident #generics).to_string()
675 }
676
677 fn add_definitions_recursively(
678 definitions: &mut #unc_sdk_crate::borsh::__private::maybestd::collections::BTreeMap<
679 #unc_sdk_crate::borsh::schema::Declaration,
680 #unc_sdk_crate::borsh::schema::Definition
681 >,
682 ) {
683 <#input_ident #generics as #unc_sdk_crate::borsh::BorshSchema>::add_definitions_recursively(definitions);
684 }
685 }
686 }
687 } else {
688 quote! {}
689 };
690
691 TokenStream::from(quote! {
692 #[cfg(not(target_arch = "wasm32"))]
693 const _: () = {
694 #[allow(non_camel_case_types)]
695 type #input_ident_proxy #generics = #input_ident #generics;
696 {
697 #derive
698 #[allow(dead_code)]
699 #input
700
701 #json_impl
702 #borsh_impl
703 };
704 };
705 })
706 }
707}
708
709#[allow(dead_code)]
710fn get_schema_derive(
711 json_schema: bool,
712 borsh_schema: bool,
713 unc_sdk_crate: proc_macro2::TokenStream,
714 need_borsh_crate: bool,
715) -> proc_macro2::TokenStream {
716 let string_borsh_crate = quote! {#unc_sdk_crate::borsh}.to_string();
717 let string_schemars_crate = quote! {#unc_sdk_crate::schemars}.to_string();
718
719 let mut derive = quote! {};
720 if borsh_schema {
721 derive = quote! {
722 #[cfg_attr(not(target_arch = "wasm32"), derive(#unc_sdk_crate::borsh::BorshSchema))]
723 };
724 if need_borsh_crate {
725 derive = quote! {
726 #derive
727 #[cfg_attr(not(target_arch = "wasm32"), borsh(crate = #string_borsh_crate))]
728 };
729 }
730 }
731 if json_schema {
732 derive = quote! {
733 #derive
734 #[cfg_attr(not(target_arch = "wasm32"), derive(#unc_sdk_crate::schemars::JsonSchema))]
735 #[cfg_attr(not(target_arch = "wasm32"), schemars(crate = #string_schemars_crate))]
736 };
737 }
738 derive
739}
740
741#[cfg(feature = "abi")]
742fn get_where_clause(
743 generics: &syn::Generics,
744 input_ident: &syn::Ident,
745 trait_name: proc_macro2::TokenStream,
746) -> WhereClause {
747 let (_, ty_generics, where_clause) = generics.split_for_impl();
748
749 let predicate = parse_quote!(#input_ident #ty_generics: #trait_name);
750
751 let where_clause: WhereClause = if let Some(mut w) = where_clause.cloned() {
752 w.predicates.push(predicate);
753 w
754 } else {
755 parse_quote!(where #predicate)
756 };
757 where_clause
758}
759
760#[proc_macro_derive(PanicOnDefault)]
765pub fn derive_no_default(item: TokenStream) -> TokenStream {
766 if let Ok(input) = syn::parse::<ItemStruct>(item) {
767 let name = &input.ident;
768 TokenStream::from(quote! {
769 impl ::std::default::Default for #name {
770 fn default() -> Self {
771 ::unc_sdk::env::panic_str("The contract is not initialized");
772 }
773 }
774 })
775 } else {
776 TokenStream::from(
777 syn::Error::new(
778 Span::call_site(),
779 "PanicOnDefault can only be used on type declarations sections.",
780 )
781 .to_compile_error(),
782 )
783 }
784}
785
786#[proc_macro_derive(BorshStorageKey)]
790pub fn borsh_storage_key(item: TokenStream) -> TokenStream {
791 let (name, generics) = if let Ok(input) = syn::parse::<ItemEnum>(item.clone()) {
792 (input.ident, input.generics)
793 } else if let Ok(input) = syn::parse::<ItemStruct>(item) {
794 (input.ident, input.generics)
795 } else {
796 return TokenStream::from(
797 syn::Error::new(
798 Span::call_site(),
799 "BorshStorageKey can only be used as a derive on enums or structs.",
800 )
801 .to_compile_error(),
802 );
803 };
804 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
805 let predicate = parse_quote!(#name #ty_generics: ::unc_sdk::borsh::BorshSerialize);
806 let where_clause: WhereClause = if let Some(mut w) = where_clause.cloned() {
807 w.predicates.push(predicate);
808 w
809 } else {
810 parse_quote!(where #predicate)
811 };
812 TokenStream::from(quote! {
813 impl #impl_generics ::unc_sdk::__private::BorshIntoStorageKey for #name #ty_generics #where_clause {}
814 })
815}
816
817#[proc_macro_derive(FunctionError)]
821pub fn function_error(item: TokenStream) -> TokenStream {
822 let name = if let Ok(input) = syn::parse::<ItemEnum>(item.clone()) {
823 input.ident
824 } else if let Ok(input) = syn::parse::<ItemStruct>(item) {
825 input.ident
826 } else {
827 return TokenStream::from(
828 syn::Error::new(
829 Span::call_site(),
830 "FunctionError can only be used as a derive on enums or structs.",
831 )
832 .to_compile_error(),
833 );
834 };
835 TokenStream::from(quote! {
836 impl ::unc_sdk::FunctionError for #name {
837 fn panic(&self) -> ! {
838 ::unc_sdk::env::panic_str(&::std::string::ToString::to_string(&self))
839 }
840 }
841 })
842}
843
844#[proc_macro_derive(EventMetadata, attributes(event_version))]
859pub fn derive_event_attributes(item: TokenStream) -> TokenStream {
860 if let Ok(input) = syn::parse::<ItemEnum>(item) {
861 let name = &input.ident;
862 let standard_name = format!("{}_event_standard", name);
864 let standard_ident = syn::Ident::new(&standard_name, Span::call_site());
865 let mut event_meta: Vec<proc_macro2::TokenStream> = vec![];
867 for var in &input.variants {
868 if let Some(version) = core_impl::get_event_version(var) {
869 let var_ident = &var.ident;
870 event_meta.push(quote! {
871 #name::#var_ident { .. } => {(::std::string::ToString::to_string(&#standard_ident), ::std::string::ToString::to_string(#version))}
872 })
873 } else {
874 return TokenStream::from(
875 syn::Error::new(
876 Span::call_site(),
877 "Unc events must have `event_version`. Must have a single string literal value.",
878 )
879 .to_compile_error(),
880 );
881 }
882 }
883
884 let (impl_generics, type_generics, where_clause) = &input.generics.split_for_impl();
886 let mut generics = input.generics.clone();
888 let event_lifetime = syn::Lifetime::new("'unc_event", Span::call_site());
889 generics.params.insert(
890 0,
891 syn::GenericParam::Lifetime(syn::LifetimeParam::new(event_lifetime.clone())),
892 );
893 let (custom_impl_generics, ..) = generics.split_for_impl();
894
895 TokenStream::from(quote! {
896 impl #impl_generics #name #type_generics #where_clause {
897 pub fn emit(&self) {
898 use ::std::string::String;
899
900 let (standard, version): (String, String) = match self {
901 #(#event_meta),*
902 };
903
904 #[derive(::unc_sdk::serde::Serialize)]
905 #[serde(crate="::unc_sdk::serde")]
906 #[serde(rename_all="snake_case")]
907 struct EventBuilder #custom_impl_generics #where_clause {
908 standard: String,
909 version: String,
910 #[serde(flatten)]
911 event_data: &#event_lifetime #name #type_generics
912 }
913 let event = EventBuilder { standard, version, event_data: self };
914 let json = ::unc_sdk::serde_json::to_string(&event)
915 .unwrap_or_else(|_| ::unc_sdk::env::abort());
916 ::unc_sdk::env::log_str(&::std::format!("EVENT_JSON:{}", json));
917 }
918 }
919 })
920 } else {
921 TokenStream::from(
922 syn::Error::new(
923 Span::call_site(),
924 "EventMetadata can only be used as a derive on enums.",
925 )
926 .to_compile_error(),
927 )
928 }
929}