polymesh_api_codegen/
generate.rs

1#![allow(deprecated)]
2use std::collections::{BTreeMap, BTreeSet};
3
4use heck::ToSnakeCase;
5
6use indexmap::IndexMap;
7
8use proc_macro2::{Ident, TokenStream};
9use quote::{format_ident, quote, TokenStreamExt};
10
11use codec::Decode;
12use frame_metadata::{RuntimeMetadata, RuntimeMetadataPrefixed};
13
14fn segments_ident(segments: &[String], import_types: bool) -> TokenStream {
15  let idents: Vec<_> = segments.into_iter().map(|s| format_ident!("{s}")).collect();
16  if import_types && idents.len() > 1 {
17    quote! {
18      types::#(#idents)::*
19    }
20  } else {
21    quote! {
22      #(#idents)::*
23    }
24  }
25}
26
27struct ModuleCode {
28  name: String,
29  sub_modules: BTreeMap<String, ModuleCode>,
30  types: BTreeMap<String, TokenStream>,
31}
32
33impl ModuleCode {
34  fn new(name: String) -> Self {
35    Self {
36      name,
37      sub_modules: BTreeMap::new(),
38      types: BTreeMap::new(),
39    }
40  }
41
42  fn add_type(&mut self, segments: &[String], ident: String, code: TokenStream) {
43    if let Some((mod_name, segments)) = segments.split_first() {
44      let entry = self.sub_modules.entry(mod_name.into());
45      let sub = entry.or_insert_with(|| ModuleCode::new(mod_name.into()));
46      sub.add_type(segments, ident, code);
47    } else if self.name.len() > 0 {
48      self.types.insert(ident, code);
49    }
50  }
51
52  fn gen(self) -> TokenStream {
53    let mut code = TokenStream::new();
54    for (name, sub) in self.sub_modules {
55      let ident = format_ident!("{name}");
56      let sub_code = sub.gen();
57      code.append_all(quote! {
58        pub mod #ident {
59          use super::*;
60          #sub_code
61        }
62      });
63    }
64    for (_, ty_code) in self.types {
65      code.append_all(ty_code);
66    }
67    code
68  }
69}
70
71#[cfg(feature = "v14")]
72mod v14 {
73  use super::*;
74  use frame_metadata::v14::{
75    RuntimeMetadataV14, StorageEntryMetadata, StorageEntryModifier, StorageEntryType, StorageHasher,
76  };
77  use scale_info::{form::PortableForm, Field, Path, Type, TypeDef, Variant};
78
79  #[derive(Default)]
80  struct TypeParameters {
81    names: IndexMap<u32, String>,
82    used: BTreeSet<String>,
83    need_bounds: BTreeMap<u32, BTreeMap<String, TokenStream>>,
84  }
85
86  impl TypeParameters {
87    fn new(ty: &Type<PortableForm>) -> Self {
88      let mut names = IndexMap::new();
89
90      let ty_params = ty.type_params();
91      if ty_params.len() > 0 {
92        for p in ty_params {
93          if let Some(p_ty) = p.ty() {
94            let name = p.name();
95            names.insert(p_ty.id(), name.into());
96          }
97        }
98      }
99
100      Self {
101        names,
102        used: Default::default(),
103        need_bounds: Default::default(),
104      }
105    }
106
107    fn add_param_bounds(&mut self, id: u32, bound_name: &str, type_bound: TokenStream) -> bool {
108      if self.names.contains_key(&id) {
109        let bounds = self.need_bounds.entry(id).or_default();
110        bounds.insert(bound_name.to_string(), type_bound);
111        true
112      } else {
113        false
114      }
115    }
116
117    fn get_param(&mut self, id: u32) -> Option<TokenStream> {
118      self.names.get(&id).map(|name| {
119        self.used.insert(name.to_string());
120        let name = format_ident!("{name}");
121        quote! { #name }
122      })
123    }
124
125    fn get_type_params(&self) -> TokenStream {
126      if self.names.len() > 0 {
127        let params = self.names.iter().map(|(id, name)| {
128          let ident = format_ident!("{name}");
129          if let Some(with_bounds) = self.need_bounds.get(&id) {
130            let bounds: Vec<_> = with_bounds.values().collect();
131            quote!(#ident: #(#bounds) + *)
132          } else {
133            quote!(#ident)
134          }
135        });
136        quote!(<#(#params),*>)
137      } else {
138        TokenStream::new()
139      }
140    }
141
142    fn get_unused_params(&self) -> Option<TokenStream> {
143      if self.used.len() < self.names.len() {
144        let params = self
145          .names
146          .values()
147          .filter(|name| !self.used.contains(*name))
148          .map(|name| {
149            let ident = format_ident!("{name}");
150            quote!(#ident)
151          })
152          .collect::<Vec<_>>();
153        // Return a tuple type with the unused params.
154        if params.len() > 1 {
155          Some(quote! { core::marker::PhantomData<(#(#params),*)> })
156        } else {
157          Some(quote! { core::marker::PhantomData<#(#params),*> })
158        }
159      } else {
160        None
161      }
162    }
163  }
164
165  struct Generator {
166    md: RuntimeMetadataV14,
167    external_modules: BTreeSet<String>,
168    pallet_types: BTreeMap<u32, (String, String)>,
169    max_error_size: usize,
170    rename_types: BTreeMap<String, TokenStream>,
171    remap_namespaces: BTreeMap<String, String>,
172    ord_types: BTreeSet<String>,
173    custom_derives: BTreeMap<String, TokenStream>,
174    runtime_namespace: Vec<String>,
175    call: TokenStream,
176    event: TokenStream,
177    v2_weights: bool,
178    api_interface: TokenStream,
179  }
180
181  impl Generator {
182    fn new(md: RuntimeMetadataV14) -> Self {
183      // Detect the chain runtime path.
184      let runtime_ty = md.types.resolve(md.ty.id()).unwrap();
185      let runtime_namespace = runtime_ty.path().namespace();
186      #[cfg(feature = "ink")]
187      let api_interface = quote!(::polymesh_api_ink);
188      #[cfg(not(feature = "ink"))]
189      let api_interface = quote!(::polymesh_api_client);
190
191      let call = quote! { runtime::RuntimeCall };
192      let event = quote! { runtime::RuntimeEvent };
193      let external_modules = BTreeSet::from_iter(
194        ["sp_version", "sp_weights", "bounded_collections"]
195          .iter()
196          .map(|t| t.to_string()),
197      );
198      let rename_types = BTreeMap::from_iter(
199        [
200          (
201            "sp_core::crypto::AccountId32",
202            quote!(#api_interface::AccountId),
203          ),
204          (
205            "polymesh_primitives::identity_id::IdentityId",
206            quote!(#api_interface::IdentityId),
207          ),
208          (
209            "polymesh_primitives::asset::AssetId",
210            quote!(#api_interface::AssetId),
211          ),
212          (
213            "sp_runtime::multiaddress::MultiAddress",
214            quote!(#api_interface::MultiAddress),
215          ),
216          (
217            "sp_runtime::MultiSignature",
218            quote!(#api_interface::MultiSignature),
219          ),
220          #[cfg(not(feature = "ink"))]
221          ("sp_runtime::generic::era::Era", quote!(#api_interface::Era)),
222          (
223            "sp_arithmetic::per_things::Perbill",
224            quote!(#api_interface::per_things::Perbill),
225          ),
226          (
227            "sp_arithmetic::per_things::Permill",
228            quote!(#api_interface::per_things::Permill),
229          ),
230          (
231            "sp_arithmetic::per_things::PerU16",
232            quote!(#api_interface::per_things::PerU16),
233          ),
234          (
235            "sp_arithmetic::per_things::Percent",
236            quote!(#api_interface::per_things::Percent),
237          ),
238          ("BTreeSet", quote!(::alloc::collections::BTreeSet)),
239          ("BTreeMap", quote!(::alloc::collections::BTreeMap)),
240          ("String", quote!(::alloc::string::String)),
241          ("Vec", quote!(::alloc::vec::Vec)),
242          (
243            "bounded_collections::bounded_btree_map::BoundedBTreeMap",
244            quote!(::alloc::collections::BTreeMap),
245          ),
246          (
247            "bounded_collections::bounded_btree_set::BoundedBTreeSet",
248            quote!(::alloc::collections::BTreeSet),
249          ),
250          (
251            "bounded_collections::bounded_vec::BoundedVec",
252            quote!(::alloc::vec::Vec),
253          ),
254          (
255            "bounded_collections::weak_bounded_vec::WeakBoundedVec",
256            quote!(::alloc::vec::Vec),
257          ),
258          (
259            "types::frame_support::storage::weak_bounded_vec::WeakBoundedVec",
260            quote!(::alloc::vec::Vec),
261          ),
262          (
263            "types::frame_support::storage::bounded_vec::BoundedVec",
264            quote!(::alloc::vec::Vec),
265          ),
266          (
267            "types::frame_system::EventRecord",
268            quote!(#api_interface::EventRecord),
269          ),
270          ("sp_weights::OldWeight", quote!(#api_interface::OldWeight)),
271          (
272            "sp_weights::Weight",
273            quote!(#api_interface::sp_weights::Weight),
274          ),
275          (
276            "sp_weights::weight_v2::Weight",
277            quote!(#api_interface::sp_weights::Weight),
278          ),
279        ]
280        .into_iter()
281        .map(|(name, code)| (name.to_string(), code)),
282      );
283      let remap_namespaces = BTreeMap::from_iter(
284        [
285          (
286            "polymesh_common_utilities::traits::balances",
287            "pallet_balances",
288          ),
289          (
290            "polymesh_common_utilities::traits::checkpoint",
291            "polymesh_primitives::checkpoint",
292          ),
293          (
294            "polymesh_common_utilities::traits::identity",
295            "polymesh_primitives::identity",
296          ),
297          (
298            "polymesh_common_utilities::traits::group",
299            "polymesh_primitives::group",
300          ),
301        ]
302        .into_iter()
303        .map(|(old, new)| (old.to_string(), new.to_string())),
304      );
305      let ink_derives = quote! {
306        #[cfg_attr(all(feature = "ink", feature = "std"), derive(::ink::storage::traits::StorageLayout))]
307      };
308      let ink_enum_derives = quote! {
309        #[derive(Copy)]
310        #[cfg_attr(all(feature = "ink", feature = "std"), derive(::ink::storage::traits::StorageLayout))]
311      };
312      let ink_extra_derives = quote! {
313        #[derive(Default)]
314        #[cfg_attr(all(feature = "ink", feature = "std"), derive(::ink::storage::traits::StorageLayout))]
315      };
316      let ink_id_derives = quote! {
317        #[derive(Copy, Default)]
318        #[cfg_attr(all(feature = "ink", feature = "std"), derive(::ink::storage::traits::StorageLayout))]
319      };
320      let custom_derives = BTreeMap::from_iter(
321        [
322          // Asset types.
323          ("AssetName", &ink_extra_derives),
324          ("AssetType", &ink_enum_derives),
325          ("NonFungibleType", &ink_enum_derives),
326          ("AssetIdentifier", &ink_enum_derives),
327          ("CustomAssetTypeId", &ink_id_derives),
328          ("FundingRoundName", &ink_extra_derives),
329          ("Ticker", &ink_id_derives),
330          ("AssetId", &ink_id_derives),
331          ("AssetID", &ink_id_derives),
332          // Settlement leg and portfolio move types.
333          ("Fund", &ink_derives),
334          ("FundDescription", &ink_derives),
335          ("FungibleToken", &ink_derives),
336          ("NFTId", &ink_id_derives),
337          ("NFTs", &ink_derives),
338          ("Leg", &ink_derives),
339          ("FungibleLeg", &ink_derives),
340          ("NonFungibleLeg", &ink_derives),
341          ("OffChainLeg", &ink_derives),
342          // Portfolio
343          ("PortfolioId", &ink_enum_derives),
344          ("PortfolioKind", &ink_enum_derives),
345          ("PortfolioNumber", &ink_id_derives),
346          ("MovePortfolioItem", &ink_derives),
347          ("Memo", &ink_derives),
348          // Settlement types.
349          ("VenueId", &ink_id_derives),
350          ("VenueDetails", &ink_extra_derives),
351          ("VenueType", &ink_enum_derives),
352          ("LegId", &ink_id_derives),
353          ("InstructionId", &ink_id_derives),
354          ("AffirmationStatus", &ink_enum_derives),
355          ("InstructionStatus", &ink_enum_derives),
356          ("LegStatus", &ink_enum_derives),
357          ("SettlementType", &ink_enum_derives),
358          // Confidential Asset types.
359          ("LegParty", &ink_enum_derives),
360          ("TransactionStatus", &ink_enum_derives),
361          ("TransactionId", &ink_id_derives),
362          ("TransactionLegId", &ink_id_derives),
363          ("ConfidentialAccount", &ink_id_derives),
364          ("MediatorAccount", &ink_id_derives),
365          ("ConfidentialTransactionRole", &ink_enum_derives),
366          ("CompressedElgamalPublicKey", &ink_id_derives),
367        ]
368        .into_iter()
369        .map(|(name, code)| (name.to_string(), code.clone())),
370      );
371
372      let mut gen = Self {
373        runtime_namespace: runtime_namespace.iter().cloned().collect(),
374        md,
375        external_modules,
376        pallet_types: BTreeMap::new(),
377        max_error_size: 4,
378        rename_types,
379        remap_namespaces,
380        ord_types: Default::default(),
381        custom_derives,
382        call,
383        event,
384        v2_weights: false,
385        api_interface,
386      };
387
388      // Process namespace remappings.
389      gen.remap_namespaces();
390
391      // Manually enable `Ord` for `Ticker`.
392      gen.ord_types.insert("Ticker".into());
393      // Try a limited number of times to mark all types needing the `Ord` type.
394      let mut ord_type_ids = BTreeSet::new();
395      for _ in 0..10 {
396        if !gen.check_for_ord_types(&mut ord_type_ids) {
397          // Finished, no new ord types.
398          break;
399        }
400      }
401      // Convert ord type ids to full names.
402      for id in ord_type_ids {
403        match gen.id_to_full_name(id) {
404          Some(name) if name != "Option" => {
405            gen.ord_types.insert(name);
406          }
407          _ => (),
408        }
409      }
410
411      gen.detect_v2_weights();
412
413      // Rename pallet types.
414      gen.rename_pallet_types();
415
416      gen
417    }
418
419    fn remap_namespaces(&mut self) {
420      for ty in self.md.types.types() {
421        let path = ty.ty.path();
422        let ns = path.namespace().join("::");
423        if ns.is_empty() {
424          continue;
425        }
426        if let Some(new_ns) = self.remap_namespaces.get(&ns) {
427          let name = path
428            .ident()
429            .expect("Namespace wasn't empty, so there should be an ident.");
430          let mut new_segments = new_ns
431            .split("::")
432            .map(|s| s.to_string())
433            .collect::<Vec<_>>();
434          new_segments.push(name.clone());
435          let old_name = format!("{ns}::{name}");
436          let new_name = segments_ident(&new_segments, false);
437          self.rename_types.insert(old_name, new_name);
438        }
439      }
440    }
441
442    fn rename_pallet_type(&mut self, id: u32, p_name: &str, kind: &str) {
443      let ty = self.md.types.resolve(id).unwrap();
444      let path = ty.path();
445      let mut segments: Vec<_> = path.segments().into_iter().cloned().collect();
446      let old_name = segments.join("::");
447      // pop ident.
448      segments.pop();
449
450      // Check for remapped namespace
451      let ns = segments.join("::");
452      if let Some(new_ns) = self.remap_namespaces.get(&ns) {
453        segments = new_ns.split("::").map(|s| s.to_string()).collect();
454      }
455
456      // Build new name.
457      let new_name = format!("{}{}", p_name, kind);
458      segments.push(new_name.clone());
459
460      // Add mapping from old name to new name.
461      let new_ident = segments_ident(&segments, false);
462      self.rename_types.insert(old_name, new_ident);
463      self.pallet_types.insert(id, (p_name.to_string(), new_name));
464    }
465
466    // Rename pallet types Call/Event/Error.
467    fn rename_pallet_types(&mut self) {
468      // Collect pallet type ids.
469      let types: Vec<_> = self
470        .md
471        .pallets
472        .iter()
473        .map(|p| {
474          (
475            p.name.to_string(),
476            p.calls.clone(),
477            p.event.clone(),
478            p.error.clone(),
479          )
480        })
481        .collect();
482      for (p_name, call, event, error) in types {
483        if let Some(c) = call {
484          self.rename_pallet_type(c.ty.id(), &p_name, "Call");
485        }
486        if let Some(e) = event {
487          self.rename_pallet_type(e.ty.id(), &p_name, "Event");
488        }
489        if let Some(e) = error {
490          self.rename_pallet_type(e.ty.id(), &p_name, "Error");
491        }
492      }
493    }
494
495    // Detect if chain is using V2 Weights.
496    fn detect_v2_weights(&mut self) {
497      for ty in self.md.types.types() {
498        let id = ty.id();
499        let full_name = self.id_to_full_name(id).unwrap_or_default();
500        if full_name == "frame_support::dispatch::DispatchInfo" {
501          self.v2_weights = true;
502          return;
503        }
504      }
505    }
506
507    fn id_to_full_name(&self, id: u32) -> Option<String> {
508      let ty = self.md.types.resolve(id)?;
509      let segments = ty.path().segments();
510      if segments.len() > 0 {
511        Some(segments.join("::"))
512      } else {
513        None
514      }
515    }
516
517    fn check_for_ord_types(&self, ord_type_ids: &mut BTreeSet<u32>) -> bool {
518      let count = ord_type_ids.len();
519      for ty in self.md.types.types() {
520        let id = ty.id();
521        let ty = ty.ty();
522        let full_name = self.id_to_full_name(id).unwrap_or_default();
523        // Check for `BTreeSet` and `BTreeMap`.
524        match full_name.as_str() {
525          "BTreeSet" | "BTreeMap" => {
526            // Mark the first type parameter as needing `Ord`.
527            ty.type_params()
528              .first()
529              .and_then(|param| param.ty())
530              .map(|ty| {
531                ord_type_ids.insert(ty.id());
532              });
533            continue;
534          }
535          _ => (),
536        }
537        // Check if this type needs `Ord`.
538        if ord_type_ids.contains(&id) {
539          // Mark fields and used types as needing `Ord`.
540          match ty.type_def() {
541            TypeDef::Composite(struct_ty) => {
542              for field in struct_ty.fields() {
543                ord_type_ids.insert(field.ty().id());
544              }
545            }
546            TypeDef::Variant(enum_ty) => {
547              for variant in enum_ty.variants() {
548                for field in variant.fields() {
549                  ord_type_ids.insert(field.ty().id());
550                }
551              }
552            }
553            TypeDef::Sequence(ty) => {
554              ord_type_ids.insert(ty.type_param().id());
555            }
556            TypeDef::Array(ty) => {
557              ord_type_ids.insert(ty.type_param().id());
558            }
559            TypeDef::Tuple(ty) => {
560              for field in ty.fields() {
561                ord_type_ids.insert(field.id());
562              }
563            }
564            TypeDef::Primitive(_) => (),
565            TypeDef::Compact(ty) => {
566              ord_type_ids.insert(ty.type_param().id());
567            }
568            _ => {}
569          }
570        }
571      }
572      let new_count = ord_type_ids.len();
573      count != new_count
574    }
575
576    fn is_boxed(field: &Field<PortableForm>) -> bool {
577      if let Some(type_name) = field.type_name() {
578        type_name.contains("Box<")
579      } else {
580        false
581      }
582    }
583
584    fn need_field_attributes(&self, field: &Field<PortableForm>) -> TokenStream {
585      if let Some(ty) = self.md.types.resolve(field.ty().id()) {
586        match ty.type_def() {
587          TypeDef::Compact(_) => {
588            return quote! { #[codec(compact)] };
589          }
590          TypeDef::Array(ty) => {
591            let len = ty.len() as usize;
592            if len > 32 {
593              return quote! { #[cfg_attr(feature = "serde", serde(with = "::serde_big_array::BigArray"))] };
594            }
595          }
596          _ => (),
597        }
598      }
599      quote! {}
600    }
601
602    fn type_name(&self, id: u32, compact_wrap: bool, import_types: bool) -> Option<TokenStream> {
603      let mut scope = TypeParameters::default();
604      self.type_name_scoped(id, &mut scope, compact_wrap, import_types)
605    }
606
607    fn type_name_scoped(
608      &self,
609      id: u32,
610      scope: &mut TypeParameters,
611      compact_wrap: bool,
612      import_types: bool,
613    ) -> Option<TokenStream> {
614      if let Some(scope_type) = scope.get_param(id) {
615        return Some(scope_type);
616      }
617      let ty = self.md.types.resolve(id)?;
618      let path = ty.path();
619      let (type_ident, is_btree) = match self.is_runtime_type(path) {
620        Some(name) => {
621          // Remap runtime types to namespace `runtime`.
622          let ident = format_ident!("{name}");
623          (quote!(runtime::#ident), false)
624        }
625        None => {
626          let segments = ty.path().segments();
627          let full_name = segments.join("::");
628          let is_btree = match full_name.as_str() {
629            "BTreeSet" | "BTreeMap" => true,
630            _ => false,
631          };
632          let type_ident = self
633            .rename_types
634            .get(&full_name)
635            .cloned()
636            .unwrap_or_else(|| segments_ident(segments, import_types));
637          (type_ident, is_btree)
638        }
639      };
640
641      match ty.type_def() {
642        TypeDef::Sequence(ty) => {
643          return self
644            .type_name_scoped(ty.type_param().id(), scope, true, import_types)
645            .map(|elem_ty| {
646              quote! { ::alloc::vec::Vec<#elem_ty> }
647            });
648        }
649        TypeDef::Array(ty) => {
650          let len = ty.len() as usize;
651          return self
652            .type_name_scoped(ty.type_param().id(), scope, true, import_types)
653            .map(|elem_ty| {
654              quote! { [#elem_ty; #len] }
655            });
656        }
657        TypeDef::Tuple(ty) => {
658          let fields = ty
659            .fields()
660            .into_iter()
661            .filter_map(|field| self.type_name_scoped(field.id(), scope, true, import_types))
662            .collect::<Vec<_>>();
663          return Some(quote! { (#(#fields),*) });
664        }
665        TypeDef::Primitive(prim) => {
666          use scale_info::TypeDefPrimitive::*;
667          let name = format_ident!(
668            "{}",
669            match prim {
670              Bool => "bool",
671              Char => "char",
672              Str => return Some(quote!(::alloc::string::String)),
673              U8 => "u8",
674              U16 => "u16",
675              U32 => "u32",
676              U64 => "u64",
677              U128 => "u128",
678              U256 => "u256",
679              I8 => "i8",
680              I16 => "i16",
681              I32 => "i32",
682              I64 => "i64",
683              I128 => "i128",
684              I256 => "i256",
685            }
686          );
687          return Some(quote! { #name });
688        }
689        TypeDef::Compact(ty) => {
690          return self
691            .type_name_scoped(ty.type_param().id(), scope, true, import_types)
692            .map(|ty| {
693              if compact_wrap {
694                quote! { ::codec::Compact<#ty> }
695              } else {
696                ty
697              }
698            });
699        }
700        _ => {}
701      }
702
703      // Check if `BTreeSet` or `BTreeMap` use a scoped paramter for the key.
704      if is_btree {
705        ty.type_params()
706          .first()
707          .and_then(|param| param.ty())
708          .map(|ty| scope.add_param_bounds(ty.id(), "Ord", quote!(Ord)));
709      }
710
711      let type_params = ty
712        .type_params()
713        .iter()
714        .filter_map(|param| {
715          param
716            .ty()
717            .map(|ty| self.type_name_scoped(ty.id(), scope, true, import_types))
718        })
719        .collect::<Vec<_>>();
720
721      if type_params.len() > 0 {
722        Some(quote! {
723          #type_ident<#(#type_params),*>
724        })
725      } else {
726        Some(type_ident)
727      }
728    }
729
730    fn gen_storage_func(
731      &self,
732      mod_prefix: &str,
733      md: &StorageEntryMetadata<PortableForm>,
734    ) -> TokenStream {
735      let storage_name = &md.name;
736      let storage_ident = format_ident!("{}", storage_name.to_snake_case());
737      let api_interface = &self.api_interface;
738      let mut key_prefix = Vec::with_capacity(512);
739      key_prefix.extend(sp_crypto_hashing::twox_128(mod_prefix.as_bytes()));
740      key_prefix.extend(sp_crypto_hashing::twox_128(storage_name.as_bytes()));
741
742      let (hashers, value_ty) = match &md.ty {
743        StorageEntryType::Plain(value) => (vec![], value.id()),
744        StorageEntryType::Map {
745          hashers,
746          key,
747          value,
748        } => match hashers.as_slice() {
749          [hasher] => {
750            // 1 key.
751            (vec![(key, hasher)], value.id())
752          }
753          hashers => {
754            // >=2 keys.
755            let keys_ty = self.md.types.resolve(key.id()).unwrap();
756            let key_hashers = if let TypeDef::Tuple(ty) = keys_ty.type_def() {
757              ty.fields().iter().zip(hashers).collect()
758            } else {
759              vec![]
760            };
761            (key_hashers, value.id())
762          }
763        },
764      };
765      let keys_len = hashers.len();
766      let mut keys = TokenStream::new();
767      let mut hashing = TokenStream::new();
768      for (idx, (key, hasher)) in hashers.into_iter().enumerate() {
769        let key_ident = format_ident!("key_{}", idx);
770        let type_name = self
771          .type_name(key.id(), false, true)
772          .expect("Missing Storage key type");
773        keys.append_all(quote! {#key_ident: #type_name,});
774        hashing.append_all(match hasher {
775          StorageHasher::Blake2_128 => quote! {
776            buf.extend(#api_interface::hashing::blake2_128(&#key_ident.encode()));
777          },
778          StorageHasher::Blake2_256 => quote! {
779            buf.extend(#api_interface::hashing::blake2_256(&#key_ident.encode()));
780          },
781          StorageHasher::Blake2_128Concat => quote! {
782            let key = #key_ident.encode();
783            buf.extend(#api_interface::hashing::blake2_128(&key));
784            buf.extend(key.into_iter());
785          },
786          StorageHasher::Twox128 => quote! {
787            buf.extend(#api_interface::hashing::twox_128(&#key_ident.encode()));
788          },
789          StorageHasher::Twox256 => quote! {
790            buf.extend(#api_interface::hashing::twox_256(&#key_ident.encode()));
791          },
792          StorageHasher::Twox64Concat => quote! {
793            let key = #key_ident.encode();
794            buf.extend(#api_interface::hashing::twox_64(&key));
795            buf.extend(key.into_iter());
796          },
797          StorageHasher::Identity => quote! {
798            buf.extend(#key_ident.encode());
799          },
800        });
801      }
802      let value_ty = if mod_prefix == "System" && storage_name == "Events" {
803        let event_ty = &self.event;
804        quote!(::alloc::vec::Vec<#api_interface::EventRecord<types::#event_ty>>)
805      } else {
806        self.type_name(value_ty, false, true).unwrap()
807      };
808
809      let (return_ty, return_value) = match md.modifier {
810        StorageEntryModifier::Optional => (quote! { Option<#value_ty>}, quote! { Ok(value) }),
811        StorageEntryModifier::Default => {
812          let default_value = &md.default;
813          (
814            quote! { #value_ty },
815            quote! {
816              Ok(value.unwrap_or_else(|| {
817                use ::codec::Decode;
818                const DEFAULT: &'static [u8] = &[#(#default_value,)*];
819                <#value_ty>::decode(&mut &DEFAULT[..]).unwrap()
820              }))
821            },
822          )
823        }
824      };
825
826      let docs = &md.docs;
827      if keys_len > 0 {
828        quote! {
829          #(#[doc = #docs])*
830          #[cfg(not(feature = "ink"))]
831          pub async fn #storage_ident(&self, #keys) -> ::polymesh_api_client::error::Result<#return_ty> {
832            use ::codec::Encode;
833            let mut buf = ::alloc::vec::Vec::with_capacity(512);
834            buf.extend([#(#key_prefix,)*]);
835            #hashing
836            let key = ::polymesh_api_client::StorageKey(buf);
837            let value = self.api.client.get_storage_by_key(key, self.at).await?;
838            #return_value
839          }
840
841          #(#[doc = #docs])*
842          #[cfg(feature = "ink")]
843          pub fn #storage_ident(&self, #keys) -> ::polymesh_api_ink::error::Result<#return_ty> {
844            use ::codec::Encode;
845            let mut buf = ::alloc::vec::Vec::with_capacity(512);
846            buf.extend([#(#key_prefix,)*]);
847            #hashing
848            let value = self.api.read_storage(buf)?;
849            #return_value
850          }
851        }
852      } else {
853        quote! {
854          #(#[doc = #docs])*
855          #[cfg(not(feature = "ink"))]
856          pub async fn #storage_ident(&self) -> ::polymesh_api_client::error::Result<#return_ty> {
857            let key = ::polymesh_api_client::StorageKey(::alloc::vec![#(#key_prefix,)*]);
858            let value = self.api.client.get_storage_by_key(key, self.at).await?;
859            #return_value
860          }
861
862          #(#[doc = #docs])*
863          #[cfg(feature = "ink")]
864          pub fn #storage_ident(&self) -> ::polymesh_api_ink::error::Result<#return_ty> {
865            let value = self.api.read_storage(::alloc::vec![#(#key_prefix,)*])?;
866            #return_value
867          }
868        }
869      }
870    }
871
872    fn gen_paged_storage_func(
873      &self,
874      mod_prefix: &str,
875      md: &StorageEntryMetadata<PortableForm>,
876    ) -> TokenStream {
877      let storage_name = &md.name;
878      let storage_ident = format_ident!("{}", storage_name.to_snake_case());
879      let api_interface = &self.api_interface;
880      let mut key_prefix = Vec::with_capacity(32);
881      key_prefix.extend(sp_crypto_hashing::twox_128(mod_prefix.as_bytes()));
882      key_prefix.extend(sp_crypto_hashing::twox_128(storage_name.as_bytes()));
883
884      let (mut hashers, value_ty) = match &md.ty {
885        StorageEntryType::Plain(value) => (vec![], value.id()),
886        StorageEntryType::Map {
887          hashers,
888          key,
889          value,
890        } => match hashers.as_slice() {
891          [hasher] => {
892            // 1 key.
893            (vec![(key, hasher)], value.id())
894          }
895          hashers => {
896            // >=2 keys.
897            let keys_ty = self.md.types.resolve(key.id()).unwrap();
898            let key_hashers = if let TypeDef::Tuple(ty) = keys_ty.type_def() {
899              ty.fields().iter().zip(hashers).collect()
900            } else {
901              vec![]
902            };
903            (key_hashers, value.id())
904          }
905        },
906      };
907      // Get the last key_hasher.
908      let (key_ty, key_hash_len) = if let Some((key, hasher)) = hashers.pop() {
909        let type_name = self
910          .type_name(key.id(), false, true)
911          .expect("Missing Storage key type");
912        let hash_len = match hasher {
913          StorageHasher::Blake2_128Concat => quote! { Some(16) },
914          StorageHasher::Twox64Concat => quote! { Some(8) },
915          StorageHasher::Identity => quote! { Some(0) },
916          _ => quote! { None },
917        };
918        (type_name, hash_len)
919      } else {
920        return quote! {};
921      };
922      let mut keys = TokenStream::new();
923      let mut hashing = TokenStream::new();
924      for (idx, (key, hasher)) in hashers.into_iter().enumerate() {
925        let key_ident = format_ident!("key_{}", idx);
926        let type_name = self
927          .type_name(key.id(), false, true)
928          .expect("Missing Storage key type");
929        keys.append_all(quote! {#key_ident: #type_name,});
930        hashing.append_all(match hasher {
931          StorageHasher::Blake2_128 => quote! {
932            buf.extend(#api_interface::hashing::blake2_128(&#key_ident.encode()));
933          },
934          StorageHasher::Blake2_256 => quote! {
935            buf.extend(#api_interface::hashing::blake2_256(&#key_ident.encode()));
936          },
937          StorageHasher::Blake2_128Concat => quote! {
938            let key = #key_ident.encode();
939            buf.extend(#api_interface::hashing::blake2_128(&key));
940            buf.extend(key.into_iter());
941          },
942          StorageHasher::Twox128 => quote! {
943            buf.extend(#api_interface::hashing::twox_128(&#key_ident.encode()));
944          },
945          StorageHasher::Twox256 => quote! {
946            buf.extend(#api_interface::hashing::twox_256(&#key_ident.encode()));
947          },
948          StorageHasher::Twox64Concat => quote! {
949            let key = #key_ident.encode();
950            buf.extend(#api_interface::hashing::twox_64(&key));
951            buf.extend(key.into_iter());
952          },
953          StorageHasher::Identity => quote! {
954            buf.extend(#key_ident.encode());
955          },
956        });
957      }
958      let value_ty = self.type_name(value_ty, false, true).unwrap();
959
960      let docs = &md.docs;
961      quote! {
962        #(#[doc = #docs])*
963        pub fn #storage_ident(&self, #keys) -> ::polymesh_api_client::StoragePaged<#key_ty, #value_ty> {
964          use ::codec::Encode;
965          let mut buf = ::alloc::vec::Vec::with_capacity(512);
966          buf.extend([#(#key_prefix,)*]);
967          #hashing
968          let prefix = ::polymesh_api_client::StorageKey(buf);
969          ::polymesh_api_client::StoragePaged::new(&self.api.client, prefix, #key_hash_len, self.at)
970        }
971      }
972    }
973
974    fn gen_func(
975      &self,
976      mod_name: &str,
977      mod_idx: u8,
978      mod_call_ty: u32,
979      md: &Variant<PortableForm>,
980    ) -> TokenStream {
981      let mod_call_ident = format_ident!("{mod_name}");
982      let mod_call = self.type_name(mod_call_ty, false, true).unwrap();
983      let func_name = md.name();
984      let func_idx = md.index();
985      let func_ident = format_ident!("{}", func_name.to_snake_case());
986
987      let mut fields = TokenStream::new();
988      let mut field_names = TokenStream::new();
989      let mut fields_encode = TokenStream::new();
990      for (idx, field) in md.fields().iter().enumerate() {
991        let name = field
992          .name()
993          .map(|n| format_ident!("{n}"))
994          .unwrap_or_else(|| format_ident!("param_{idx}"));
995        let type_name = self
996          .type_name(field.ty().id(), false, true)
997          .expect("Missing Extrinsic param type");
998        fields.append_all(quote! {#name: #type_name,});
999        if Self::is_boxed(field) {
1000          field_names.append_all(quote! {#name: ::alloc::boxed::Box::new(#name),});
1001        } else {
1002          field_names.append_all(quote! {#name,});
1003        }
1004        fields_encode.append_all(quote! {
1005          #name.encode_to(&mut buf);
1006        });
1007      }
1008
1009      let docs = md.docs();
1010      let call_ty = &self.call;
1011      if md.fields().len() > 0 {
1012        quote! {
1013          #(#[doc = #docs])*
1014          #[cfg(not(feature = "ink"))]
1015          pub fn #func_ident(&self, #fields) -> ::polymesh_api_client::error::Result<super::super::WrappedCall> {
1016            self.api.wrap_call(#call_ty::#mod_call_ident(types::#mod_call::#func_ident { #field_names }))
1017          }
1018
1019          #(#[doc = #docs])*
1020          #[cfg(feature = "ink")]
1021          pub fn #func_ident(&self, #fields) -> super::super::WrappedCall {
1022            use ::codec::Encode;
1023            let mut buf = ::alloc::vec![#mod_idx, #func_idx];
1024            #fields_encode
1025            self.api.wrap_call(buf)
1026          }
1027        }
1028      } else {
1029        quote! {
1030          #(#[doc = #docs])*
1031          #[cfg(not(feature = "ink"))]
1032          pub fn #func_ident(&self) -> ::polymesh_api_client::error::Result<super::super::WrappedCall> {
1033            self.api.wrap_call(#call_ty::#mod_call_ident(types::#mod_call::#func_ident))
1034          }
1035
1036          #(#[doc = #docs])*
1037          #[cfg(feature = "ink")]
1038          pub fn #func_ident(&self) -> super::super::WrappedCall {
1039            self.api.wrap_call(::alloc::vec![#mod_idx, #func_idx])
1040          }
1041        }
1042      }
1043    }
1044
1045    fn gen_module(
1046      &self,
1047      md: &frame_metadata::v14::PalletMetadata<PortableForm>,
1048    ) -> (Ident, Ident, Ident, Ident, TokenStream) {
1049      let mod_idx = md.index;
1050      let mod_name = &md.name;
1051      let mod_call_api = format_ident!("{}CallApi", mod_name);
1052      let mod_query_api = format_ident!("{}QueryApi", mod_name);
1053      let mod_paged_query_api = format_ident!("{}PagedQueryApi", mod_name);
1054      let mod_ident = format_ident!("{}", mod_name.to_snake_case());
1055
1056      let mut call_fields = TokenStream::new();
1057      let mut query_fields = TokenStream::new();
1058      let mut paged_query_fields = TokenStream::new();
1059
1060      // Generate module functions.
1061      if let Some(calls) = &md.calls {
1062        let call_ty = self
1063          .md
1064          .types
1065          .resolve(calls.ty.id())
1066          .expect("Missing Pallet call type");
1067        match call_ty.type_def() {
1068          TypeDef::Variant(v) => {
1069            let mod_call_ty = calls.ty.id();
1070            for v in v.variants() {
1071              let code = self.gen_func(mod_name, mod_idx, mod_call_ty, v);
1072              call_fields.append_all(code);
1073            }
1074          }
1075          _ => {
1076            unimplemented!("Only Variant type supported for Pallet Call type.");
1077          }
1078        }
1079      }
1080
1081      // Generate module storage query functions.
1082      if let Some(storage) = &md.storage {
1083        let mod_prefix = &storage.prefix;
1084        for md in &storage.entries {
1085          query_fields.append_all(self.gen_storage_func(mod_prefix, md));
1086          paged_query_fields.append_all(self.gen_paged_storage_func(mod_prefix, md));
1087        }
1088      }
1089
1090      let code = quote! {
1091        pub mod #mod_ident {
1092          use super::*;
1093
1094          #[derive(Clone)]
1095          pub struct #mod_call_api<'api> {
1096            api: &'api super::super::Api,
1097          }
1098
1099          impl<'api> #mod_call_api<'api> {
1100            #call_fields
1101          }
1102
1103          impl<'api> From<&'api super::super::Api> for #mod_call_api<'api> {
1104            fn from(api: &'api super::super::Api) -> Self {
1105              Self { api }
1106            }
1107          }
1108
1109          #[derive(Clone)]
1110          pub struct #mod_query_api<'api> {
1111            pub(crate) api: &'api super::super::Api,
1112            #[cfg(not(feature = "ink"))]
1113            pub(crate) at: Option<::polymesh_api_client::BlockHash>,
1114          }
1115
1116          impl<'api> #mod_query_api<'api> {
1117            #query_fields
1118          }
1119
1120          #[derive(Clone)]
1121          #[cfg(not(feature = "ink"))]
1122          pub struct #mod_paged_query_api<'api> {
1123            pub(crate) api: &'api super::super::Api,
1124            pub(crate) at: Option<::polymesh_api_client::BlockHash>,
1125          }
1126
1127          #[cfg(not(feature = "ink"))]
1128          impl<'api> #mod_paged_query_api<'api> {
1129            #paged_query_fields
1130          }
1131        }
1132      };
1133      (
1134        mod_ident,
1135        mod_call_api,
1136        mod_query_api,
1137        mod_paged_query_api,
1138        code,
1139      )
1140    }
1141
1142    fn gen_struct_fields(
1143      &self,
1144      fields: &[Field<PortableForm>],
1145      scope: &mut TypeParameters,
1146    ) -> Option<(bool, TokenStream)> {
1147      let mut is_tuple = false;
1148      let mut named = Vec::new();
1149      let mut unnamed = Vec::new();
1150
1151      // Check for unit type (i.e. empty field list).
1152      if fields.len() == 0 {
1153        return Some((true, quote! {}));
1154      }
1155
1156      for field in fields {
1157        let mut field_ty = self.type_name_scoped(field.ty().id(), scope, false, false)?;
1158        if Self::is_boxed(field) {
1159          field_ty = quote!(::alloc::boxed::Box<#field_ty>);
1160        }
1161        let attr = self.need_field_attributes(field);
1162        unnamed.push(quote! { #attr pub #field_ty });
1163        if let Some(name) = field.name() {
1164          let docs = field.docs();
1165          let name = format_ident!("{name}");
1166          named.push(quote! {
1167              #(#[doc = #docs])*
1168              #attr
1169              pub #name: #field_ty
1170          });
1171        } else {
1172          // If there are any unnamed fields, then make it a tuple.
1173          is_tuple = true;
1174        }
1175      }
1176
1177      if is_tuple {
1178        Some((true, quote! { #(#unnamed),* }))
1179      } else {
1180        Some((
1181          false,
1182          quote! {
1183            #(#named),*
1184          },
1185        ))
1186      }
1187    }
1188
1189    fn gen_enum_match_fields(&self, fields: &[Field<PortableForm>]) -> TokenStream {
1190      let mut is_tuple = false;
1191      let mut unnamed = Vec::new();
1192
1193      // Check for unit type (i.e. empty field list).
1194      if fields.len() == 0 {
1195        return quote!();
1196      }
1197
1198      for field in fields {
1199        unnamed.push(quote!(_));
1200        if field.name().is_none() {
1201          // If there are any unnamed fields, then make it a tuple.
1202          is_tuple = true;
1203        }
1204      }
1205
1206      if is_tuple {
1207        quote! { (#(#unnamed),*) }
1208      } else {
1209        quote! {
1210          {
1211            ..
1212          }
1213        }
1214      }
1215    }
1216
1217    fn gen_enum_fields(
1218      &self,
1219      fields: &[Field<PortableForm>],
1220      scope: &mut TypeParameters,
1221    ) -> Option<(bool, Vec<TokenStream>)> {
1222      let mut is_tuple = false;
1223      let mut named = Vec::new();
1224      let mut unnamed = Vec::new();
1225
1226      // Check for unit type (i.e. empty field list).
1227      if fields.len() == 0 {
1228        return Some((true, unnamed));
1229      }
1230
1231      for field in fields {
1232        let mut field_ty = self.type_name_scoped(field.ty().id(), scope, false, false)?;
1233        if Self::is_boxed(field) {
1234          field_ty = quote!(::alloc::boxed::Box<#field_ty>);
1235        }
1236        let docs = field.docs();
1237        let attr = self.need_field_attributes(field);
1238        unnamed.push(quote! {
1239            #(#[doc = #docs])*
1240            #attr
1241            #field_ty
1242        });
1243        if let Some(name) = field.name() {
1244          let name = format_ident!("{name}");
1245          named.push(quote! {
1246              #(#[doc = #docs])*
1247              #attr
1248              #name: #field_ty
1249          });
1250        } else {
1251          // If there are any unnamed fields, then make it a tuple.
1252          is_tuple = true;
1253        }
1254      }
1255
1256      if is_tuple {
1257        Some((true, unnamed))
1258      } else {
1259        Some((false, named))
1260      }
1261    }
1262
1263    fn gen_enum_as_static_str(
1264      &self,
1265      ty_ident: &Ident,
1266      params: &TokenStream,
1267      ty: &Type<PortableForm>,
1268      prefix: Option<&String>,
1269    ) -> Option<TokenStream> {
1270      let mut as_str_arms = TokenStream::new();
1271      let mut as_docs_arms = TokenStream::new();
1272      match (prefix, ty.type_def()) {
1273        (None, TypeDef::Variant(enum_ty)) => {
1274          for variant in enum_ty.variants() {
1275            let top_name = variant.name();
1276            let top_ident = format_ident!("{}", top_name);
1277            let fields = variant.fields().len();
1278            match fields {
1279              0 => {
1280                as_str_arms.append_all(quote! {
1281                  Self::#top_ident => {
1282                    #top_name
1283                  },
1284                });
1285                as_docs_arms.append_all(quote! {
1286                  Self::#top_ident => {
1287                    &[""]
1288                  },
1289                });
1290              }
1291              1 => {
1292                as_str_arms.append_all(quote! {
1293                  Self::#top_ident(val) => {
1294                    val.as_static_str()
1295                  },
1296                });
1297                as_docs_arms.append_all(quote! {
1298                  Self::#top_ident(val) => {
1299                    val.as_docs()
1300                  },
1301                });
1302              }
1303              _ => {
1304                as_str_arms.append_all(quote! {
1305                  Self::#top_ident(_) => {
1306                    #top_name
1307                  },
1308                });
1309                as_docs_arms.append_all(quote! {
1310                  Self::#top_ident(_) => {
1311                    &[""]
1312                  },
1313                });
1314              }
1315            }
1316          }
1317        }
1318        (Some(prefix), TypeDef::Variant(enum_ty)) => {
1319          for variant in enum_ty.variants() {
1320            let var_name = variant.name();
1321            let var_ident = format_ident!("{}", var_name);
1322            let mut docs = variant.docs().to_vec();
1323            if docs.len() == 0 {
1324              docs.push("".to_string());
1325            }
1326            let as_str_name = format!("{}.{}", prefix, var_name);
1327            let match_fields = self.gen_enum_match_fields(variant.fields());
1328            as_str_arms.append_all(quote! {
1329              Self:: #var_ident #match_fields => {
1330                #as_str_name
1331              },
1332            });
1333            as_docs_arms.append_all(quote! {
1334              Self:: #var_ident #match_fields => {
1335                &[#(#docs,)*]
1336              },
1337            });
1338          }
1339        }
1340        _ => {
1341          return None;
1342        }
1343      };
1344      Some(quote! {
1345        impl #params #ty_ident #params {
1346          pub fn as_static_str(&self) -> &'static str {
1347            #[allow(unreachable_patterns)]
1348            match self {
1349              #as_str_arms
1350              _ => "Unknown",
1351            }
1352          }
1353        }
1354
1355        #[cfg(not(feature = "ink"))]
1356        impl #params ::polymesh_api_client::EnumInfo for #ty_ident #params {
1357          fn as_name(&self) -> &'static str {
1358            self.as_static_str()
1359          }
1360
1361          fn as_docs(&self) -> &'static [&'static str] {
1362            #[allow(unreachable_patterns)]
1363            match self {
1364              #as_docs_arms
1365              _ => &[""],
1366            }
1367          }
1368        }
1369
1370        impl #params From<#ty_ident #params> for &'static str {
1371          fn from(v: #ty_ident #params) -> Self {
1372            v.as_static_str()
1373          }
1374        }
1375
1376        impl #params From<&#ty_ident #params> for &'static str {
1377          fn from(v: &#ty_ident #params) -> Self {
1378            v.as_static_str()
1379          }
1380        }
1381      })
1382    }
1383
1384    fn is_runtime_type(&self, path: &Path<PortableForm>) -> Option<String> {
1385      if self.runtime_namespace == path.namespace() {
1386        let ident = path.ident();
1387        match ident.as_deref() {
1388          Some("Event") => Some("RuntimeEvent".into()),
1389          Some("Call") => Some("RuntimeCall".into()),
1390          Some(name) => Some(name.into()),
1391          _ => None,
1392        }
1393      } else {
1394        None
1395      }
1396    }
1397
1398    fn gen_module_error(
1399      &self,
1400      _id: u32,
1401      ty: &Type<PortableForm>,
1402      ident: &str,
1403    ) -> Option<TokenStream> {
1404      let ty_ident = format_ident!("{ident}");
1405      let mut scope = TypeParameters::new(ty);
1406
1407      let mut variants = TokenStream::new();
1408      let mut as_str_arms = TokenStream::new();
1409      let mut as_docs_arms = TokenStream::new();
1410      for p in &self.md.pallets {
1411        let idx = p.index;
1412        let mod_ident = format_ident!("{}", p.name);
1413        let error_ty = p.error.as_ref().and_then(|e| {
1414          self
1415            .type_name_scoped(e.ty.id(), &mut scope, false, false)
1416            .map(|ident| quote! { (#ident) })
1417        });
1418        if let Some(error_ty) = error_ty {
1419          variants.append_all(quote! {
1420            #[codec(index = #idx)]
1421            #mod_ident #error_ty,
1422          });
1423          as_str_arms.append_all(quote! {
1424            RuntimeError:: #mod_ident(err) => err.as_static_str(),
1425          });
1426          as_docs_arms.append_all(quote! {
1427            RuntimeError:: #mod_ident(err) => err.as_docs(),
1428          });
1429        }
1430      }
1431
1432      let docs = ty.docs();
1433      let max_error_size = self.max_error_size + 1;
1434      let code = quote! {
1435        #[derive(Clone, Debug, PartialEq, Eq)]
1436        #[derive(::codec::Encode, ::codec::Decode)]
1437        #[cfg_attr(all(feature = "std", feature = "type_info"), derive(::scale_info::TypeInfo))]
1438        #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
1439        pub enum RuntimeError {
1440          #variants
1441        }
1442
1443        impl RuntimeError {
1444          pub fn as_static_str(&self) -> &'static str {
1445            match self {
1446              #as_str_arms
1447            }
1448          }
1449        }
1450
1451        impl From<RuntimeError> for &'static str {
1452          fn from(v: RuntimeError) -> Self {
1453            v.as_static_str()
1454          }
1455        }
1456
1457        impl From<&RuntimeError> for &'static str {
1458          fn from(v: &RuntimeError) -> Self {
1459            v.as_static_str()
1460          }
1461        }
1462
1463        #[cfg(not(feature = "ink"))]
1464        impl ::polymesh_api_client::EnumInfo for RuntimeError {
1465          fn as_name(&self) -> &'static str {
1466            self.as_static_str()
1467          }
1468
1469          fn as_docs(&self) -> &'static [&'static str] {
1470            match self {
1471              #as_docs_arms
1472            }
1473          }
1474        }
1475
1476        #(#[doc = #docs])*
1477        #[derive(Clone, Debug, PartialEq, Eq)]
1478        #[cfg_attr(all(feature = "std", feature = "type_info"), derive(::scale_info::TypeInfo))]
1479        #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
1480        pub struct #ty_ident(pub RuntimeError);
1481
1482        impl ::codec::Encode for #ty_ident {
1483          fn encode_to<T: ::codec::Output + ?Sized>(&self, output: &mut T) {
1484            let mut raw = self.0.encode();
1485            raw.resize(#max_error_size, 0);
1486            output.write(raw.as_slice());
1487          }
1488        }
1489
1490        impl ::codec::Decode for #ty_ident {
1491          fn decode<I: ::codec::Input>(input: &mut I) -> Result<Self, ::codec::Error> {
1492            let raw: [u8; #max_error_size] = ::codec::Decode::decode(input)?;
1493            Ok(Self(RuntimeError::decode(&mut &raw[..])?))
1494          }
1495        }
1496
1497        impl #ty_ident {
1498          pub fn as_static_str(&self) -> &'static str {
1499            self.0.as_static_str()
1500          }
1501        }
1502
1503        impl From<#ty_ident> for &'static str {
1504          fn from(v: #ty_ident) -> Self {
1505            v.as_static_str()
1506          }
1507        }
1508
1509        impl From<&#ty_ident> for &'static str {
1510          fn from(v: &#ty_ident) -> Self {
1511            v.as_static_str()
1512          }
1513        }
1514
1515        #[cfg(not(feature = "ink"))]
1516        impl ::polymesh_api_client::EnumInfo for #ty_ident {
1517          fn as_name(&self) -> &'static str {
1518            self.as_static_str()
1519          }
1520
1521          fn as_docs(&self) -> &'static [&'static str] {
1522            self.0.as_docs()
1523          }
1524        }
1525      };
1526      Some(code)
1527    }
1528
1529    fn gen_dispatch_error(
1530      &self,
1531      _id: u32,
1532      ty: &Type<PortableForm>,
1533      ident: &str,
1534    ) -> Option<TokenStream> {
1535      let ty_ident = format_ident!("{ident}");
1536      let mut scope = TypeParameters::new(ty);
1537
1538      let mut variants = TokenStream::new();
1539      let mut as_str_arms = TokenStream::new();
1540      let mut as_docs_arms = TokenStream::new();
1541      for p in &self.md.pallets {
1542        let idx = p.index;
1543        let mod_ident = format_ident!("{}", p.name);
1544        let error_ty = p.error.as_ref().and_then(|e| {
1545          self
1546            .type_name_scoped(e.ty.id(), &mut scope, false, false)
1547            .map(|ident| quote! { (#ident) })
1548        });
1549        if let Some(error_ty) = error_ty {
1550          variants.append_all(quote! {
1551            #[codec(index = #idx)]
1552            #mod_ident #error_ty,
1553          });
1554          as_str_arms.append_all(quote! {
1555            RuntimeError:: #mod_ident(err) => err.as_static_str(),
1556          });
1557          as_docs_arms.append_all(quote! {
1558            RuntimeError:: #mod_ident(err) => err.as_docs(),
1559          });
1560        }
1561      }
1562
1563      let docs = ty.docs();
1564      let code = quote! {
1565        #(#[doc = #docs])*
1566        #[derive(Clone, Debug, PartialEq, Eq)]
1567        #[derive(::codec::Encode, ::codec::Decode)]
1568        #[cfg_attr(all(feature = "std", feature = "type_info"), derive(::scale_info::TypeInfo))]
1569        #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
1570        pub enum #ty_ident {
1571          Other,
1572          CannotLookup,
1573          BadOrigin,
1574          Module(ModuleError),
1575          ConsumerRemaining,
1576          NoProviders,
1577          TooManyConsumers,
1578          Token(sp_runtime::TokenError),
1579          Arithmetic(sp_arithmetic::ArithmeticError),
1580          Transactional(sp_runtime::TransactionalError),
1581          Exhausted,
1582          Corruption,
1583          Unavailable,
1584          RootNotAllowed,
1585        }
1586
1587        impl #ty_ident {
1588          pub fn as_static_str(&self) -> &'static str {
1589            match self {
1590              Self::Other => "Other",
1591              Self::CannotLookup => "CannotLookup",
1592              Self::BadOrigin => "BadOrigin",
1593              Self::Module(err) => err.as_static_str(),
1594              Self::ConsumerRemaining => "ConsumerRemaining",
1595              Self::NoProviders => "NoProviders",
1596              Self::TooManyConsumers => "TooManyConsumers",
1597              Self::Token(err) => err.as_static_str(),
1598              Self::Arithmetic(err) => err.as_static_str(),
1599              Self::Transactional(err) => err.as_static_str(),
1600              Self::Exhausted => "Exhausted",
1601              Self::Corruption => "Corruption",
1602              Self::Unavailable => "Unavailable",
1603              Self::RootNotAllowed => "RootNotAllowed",
1604            }
1605          }
1606        }
1607
1608        impl From<#ty_ident> for &'static str {
1609          fn from(v: #ty_ident) -> Self {
1610            v.as_static_str()
1611          }
1612        }
1613
1614        impl From<&#ty_ident> for &'static str {
1615          fn from(v: &#ty_ident) -> Self {
1616            v.as_static_str()
1617          }
1618        }
1619
1620        #[cfg(not(feature = "ink"))]
1621        impl ::polymesh_api_client::EnumInfo for #ty_ident {
1622          fn as_name(&self) -> &'static str {
1623            self.as_static_str()
1624          }
1625
1626          fn as_docs(&self) -> &'static [&'static str] {
1627            match self {
1628              Self::Other => &["Some error occurred."],
1629              Self::CannotLookup => &["Failed to lookup some data."],
1630              Self::BadOrigin => &["A bad origin."],
1631              Self::Module(err) => err.as_docs(),
1632              Self::ConsumerRemaining => &["At least one consumer is remaining so the account cannot be destroyed."],
1633              Self::NoProviders => &["There are no providers so the account cannot be created."],
1634              Self::TooManyConsumers => &["There are too many consumers so the account cannot be created."],
1635              Self::Token(err) => err.as_docs(),
1636              Self::Arithmetic(err) => err.as_docs(),
1637              Self::Transactional(err) => err.as_docs(),
1638              Self::Exhausted => &["Resources exhausted, e.g. attempt to read/write data which is too large to manipulate."],
1639              Self::Corruption => &["The state is corrupt; this is generally not going to fix itself."],
1640              Self::Unavailable => &["Some resource (e.g. a preimage) is unavailable right now. This might fix itself later."],
1641              Self::RootNotAllowed => &["Root origin is not allowed."],
1642            }
1643          }
1644        }
1645      };
1646      Some(code)
1647    }
1648
1649    fn gen_type(
1650      &self,
1651      id: u32,
1652      ty: &Type<PortableForm>,
1653      ident: &str,
1654      is_runtime_type: bool,
1655    ) -> Option<TokenStream> {
1656      let full_name = self.id_to_full_name(id)?;
1657      if full_name == "sp_runtime::ModuleError" {
1658        return self.gen_module_error(id, ty, ident);
1659      }
1660      if full_name == "sp_runtime::DispatchError" {
1661        return self.gen_dispatch_error(id, ty, ident);
1662      }
1663      let (pallet_name, ident) = match self.pallet_types.get(&id) {
1664        Some((pallet_name, ident)) => (Some(pallet_name), ident.as_str()),
1665        None => (None, ident),
1666      };
1667      let ty_ident = format_ident!("{ident}");
1668
1669      let mut scope = TypeParameters::new(ty);
1670      let derive_ord = if self.ord_types.contains(&full_name) {
1671        quote! {
1672          #[derive(PartialOrd, Ord)]
1673        }
1674      } else {
1675        quote!()
1676      };
1677      let custom_derive = self
1678        .custom_derives
1679        .get(ident)
1680        .cloned()
1681        .unwrap_or_else(|| quote!());
1682
1683      let docs = ty.docs();
1684      let (mut code, params) = match ty.type_def() {
1685        TypeDef::Composite(struct_ty) => {
1686          let (is_tuple, mut fields) = self.gen_struct_fields(struct_ty.fields(), &mut scope)?;
1687          if let Some(unused_params) = scope.get_unused_params() {
1688            if is_tuple {
1689              fields.append_all(quote! {
1690                , #unused_params
1691              });
1692            } else {
1693              fields.append_all(quote! {
1694                , _phantom_data: #unused_params
1695              });
1696            }
1697          }
1698          let params = scope.get_type_params();
1699          if is_tuple {
1700            (
1701              quote! {
1702                #(#[doc = #docs])*
1703                #[derive(Clone, Debug, PartialEq, Eq)]
1704                #derive_ord
1705                #custom_derive
1706                #[derive(::codec::Encode, ::codec::Decode)]
1707                #[cfg_attr(all(feature = "std", feature = "type_info"), derive(::scale_info::TypeInfo))]
1708                #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
1709                pub struct #ty_ident #params (#fields);
1710              },
1711              params,
1712            )
1713          } else {
1714            (
1715              quote! {
1716                #(#[doc = #docs])*
1717                #[derive(Clone, Debug, PartialEq, Eq)]
1718                #derive_ord
1719                #custom_derive
1720                #[derive(::codec::Encode, ::codec::Decode)]
1721                #[cfg_attr(all(feature = "std", feature = "type_info"), derive(::scale_info::TypeInfo))]
1722                #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
1723                pub struct #ty_ident #params { #fields }
1724              },
1725              params,
1726            )
1727          }
1728        }
1729        TypeDef::Variant(enum_ty) => {
1730          let mut variants = TokenStream::new();
1731          let mut runtime_events = TokenStream::new();
1732          let is_runtime_event = is_runtime_type && ident == "RuntimeEvent";
1733          for variant in enum_ty.variants() {
1734            let idx = variant.index();
1735            let docs = variant.docs();
1736            let name = variant.name();
1737            let ident = format_ident!("{}", name);
1738            let (is_tuple, fields) = self.gen_enum_fields(variant.fields(), &mut scope)?;
1739            let variant_ty = if is_runtime_event {
1740              let event_ident = format_ident!("{}Event", name);
1741              runtime_events.append_all(quote! {
1742                pub type #event_ident = #(#fields),*;
1743              });
1744              quote! {
1745                (events::#event_ident),
1746              }
1747            } else {
1748              if is_tuple {
1749                if fields.len() > 0 {
1750                  quote! { (#(#fields),*), }
1751                } else {
1752                  quote! { , }
1753                }
1754              } else {
1755                quote! {
1756                  {
1757                    #(#fields),*
1758                  },
1759                }
1760              }
1761            };
1762            variants.append_all(quote! {
1763              #(#[doc = #docs])*
1764              #[codec(index = #idx)]
1765              #ident #variant_ty
1766            });
1767          }
1768          if let Some(unused_params) = scope.get_unused_params() {
1769            variants.append_all(quote! {
1770              PhantomDataVariant(#unused_params)
1771            });
1772          }
1773          if is_runtime_event {
1774            runtime_events = quote! {
1775              pub mod events {
1776                use super::*;
1777                #runtime_events
1778              }
1779            }
1780          }
1781          let params = scope.get_type_params();
1782          (
1783            quote! {
1784              #runtime_events
1785
1786              #(#[doc = #docs])*
1787              #[derive(Clone, Debug, PartialEq, Eq)]
1788              #derive_ord
1789              #custom_derive
1790              #[derive(::codec::Encode, ::codec::Decode)]
1791              #[cfg_attr(all(feature = "std", feature = "type_info"), derive(::scale_info::TypeInfo))]
1792              #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
1793              pub enum #ty_ident #params {
1794                #variants
1795              }
1796            },
1797            params,
1798          )
1799        }
1800        _ => {
1801          return None;
1802        }
1803      };
1804
1805      let is_error_sub_type = match full_name.as_str() {
1806        "sp_runtime::TokenError" => true,
1807        "sp_runtime::TransactionalError" => true,
1808        "sp_arithmetic::ArithmeticError" => true,
1809        _ => false,
1810      };
1811      // Generate `as_static_str` for some enum types:
1812      // * Pallet types: *Call, *Event, *Error.
1813      // * Runtime types: RuntimeCall, RuntimeEvent.
1814      // * Sub error type: TokenError, TransactionalError, ArithmeticError.
1815      let gen_as_static_str = pallet_name.is_some()
1816        || (is_runtime_type && (ident == "RuntimeCall" || ident == "RuntimeEvent"))
1817        || is_error_sub_type;
1818
1819      if gen_as_static_str {
1820        if let Some(as_static_str) =
1821          self.gen_enum_as_static_str(&ty_ident, &params, ty, pallet_name)
1822        {
1823          code.append_all(as_static_str);
1824        }
1825      }
1826
1827      Some(code)
1828    }
1829
1830    fn generate_types(&self) -> TokenStream {
1831      // Start with empty namespace.
1832      let mut modules = ModuleCode::new("".into());
1833      let runtime_ns = [String::from("runtime")];
1834
1835      for ty in self.md.types.types() {
1836        let ty_id = ty.id();
1837        let ty = ty.ty();
1838        let ty_path = ty.path();
1839        let mut ty_ns = ty_path.namespace();
1840        // Only generate type code for types with namespaces.  Basic rust types like
1841        // `Result` and `Option` have no namespace.
1842        if let Some(ns_top) = ty_ns.first() {
1843          // Don't generate code for external types.
1844          if !self.external_modules.contains(ns_top) {
1845            let (ident, is_runtime_type) = match self.is_runtime_type(ty_path) {
1846              Some(name) => {
1847                ty_ns = &runtime_ns;
1848                (name, true)
1849              }
1850              None => (ty_path.ident().unwrap(), false),
1851            };
1852
1853            if let Some(code) = self.gen_type(ty_id, ty, &ident, is_runtime_type) {
1854              if ty_ns.is_empty() {
1855                // No namespace
1856                modules.add_type(ty_ns, ident, code);
1857              } else {
1858                let old_ns = ty_ns.join("::");
1859                if let Some(new_ns) = self.remap_namespaces.get(&old_ns) {
1860                  let remapped = new_ns
1861                    .split("::")
1862                    .map(|s| s.to_string())
1863                    .collect::<Vec<_>>();
1864                  modules.add_type(&remapped, ident, code);
1865                } else {
1866                  // No remap.
1867                  modules.add_type(ty_ns, ident, code);
1868                }
1869              }
1870            }
1871          }
1872        }
1873      }
1874
1875      modules.gen()
1876    }
1877
1878    pub fn generate(self) -> TokenStream {
1879      let mut call_fields = TokenStream::new();
1880      let mut query_fields = TokenStream::new();
1881      let mut paged_query_fields = TokenStream::new();
1882
1883      // Generate module code.
1884      let modules: Vec<_> = self
1885        .md
1886        .pallets
1887        .iter()
1888        .map(|m| {
1889          let (ident, call_api, query_api, paged_query_api, code) = self.gen_module(m);
1890          call_fields.append_all(quote! {
1891            pub fn #ident(&self) -> api::#ident::#call_api<'api> {
1892              api::#ident::#call_api::from(self.api)
1893            }
1894          });
1895          query_fields.append_all(quote! {
1896            pub fn #ident(&self) -> api::#ident::#query_api<'api> {
1897              api::#ident::#query_api {
1898                api: self.api,
1899                #[cfg(not(feature = "ink"))]
1900                at: self.at,
1901              }
1902            }
1903          });
1904
1905          paged_query_fields.append_all(quote! {
1906            #[cfg(not(feature = "ink"))]
1907            pub fn #ident(&self) -> api::#ident::#paged_query_api<'api> {
1908              api::#ident::#paged_query_api {
1909                api: self.api,
1910                at: self.at,
1911              }
1912            }
1913          });
1914
1915          code
1916        })
1917        .collect();
1918
1919      let types_code = self.generate_types();
1920
1921      let dispatch_info = if self.v2_weights {
1922        quote! { frame_support::dispatch::DispatchInfo }
1923      } else {
1924        quote! { frame_support::weights::DispatchInfo }
1925      };
1926
1927      let call_ty = &self.call;
1928      let event_ty = &self.event;
1929      quote! {
1930        #[allow(dead_code, unused_imports, non_camel_case_types)]
1931        pub mod types {
1932          use super::WrappedCall;
1933          #types_code
1934        }
1935
1936        #[allow(dead_code, unused_imports, non_camel_case_types)]
1937        pub mod api {
1938          use super::types;
1939          use super::types::*;
1940          use super::WrappedCall;
1941
1942          #( #modules )*
1943        }
1944
1945        #[derive(Clone)]
1946        pub struct Api {
1947          #[cfg(not(feature = "ink"))]
1948          client: ::polymesh_api_client::Client,
1949        }
1950
1951        impl Api {
1952          #[cfg(feature = "ink")]
1953          pub fn new() -> Self {
1954            Self {}
1955          }
1956
1957          #[cfg(feature = "ink")]
1958          pub fn runtime(&self) -> ::polymesh_api_ink::extension::PolymeshRuntimeInstance {
1959            ::polymesh_api_ink::extension::new_instance()
1960          }
1961
1962          #[cfg(feature = "ink")]
1963          pub fn read_storage<T: ::codec::Decode>(&self, key: ::alloc::vec::Vec<u8>) -> ::polymesh_api_ink::error::Result<Option<T>> {
1964            let runtime = self.runtime();
1965            let value = runtime.read_storage(key.into())?
1966              .map(|data| T::decode(&mut data.as_slice()))
1967              .transpose()?;
1968            Ok(value)
1969          }
1970
1971          #[cfg(not(feature = "ink"))]
1972          pub async fn new(url: &str) -> ::polymesh_api_client::error::Result<Self> {
1973            Ok(Self {
1974              client: ::polymesh_api_client::Client::new(url).await?
1975            })
1976          }
1977
1978          pub fn call(&self) -> CallApi {
1979            CallApi { api: self }
1980          }
1981
1982          #[cfg(not(feature = "ink"))]
1983          pub fn query(&self) -> QueryApi {
1984            QueryApi { api: self, at: None }
1985          }
1986
1987          #[cfg(feature = "ink")]
1988          pub fn query(&self) -> QueryApi {
1989            QueryApi { api: self }
1990          }
1991
1992          #[cfg(not(feature = "ink"))]
1993          pub fn query_at(&self, block: ::polymesh_api_client::BlockHash) -> QueryApi {
1994            QueryApi { api: self, at: Some(block) }
1995          }
1996
1997          #[cfg(not(feature = "ink"))]
1998          pub fn paged_query(&self) -> PagedQueryApi {
1999            PagedQueryApi { api: self, at: None }
2000          }
2001
2002          #[cfg(not(feature = "ink"))]
2003          pub fn paged_query_at(&self, block: ::polymesh_api_client::BlockHash) -> PagedQueryApi {
2004            PagedQueryApi { api: self, at: Some(block) }
2005          }
2006
2007          #[cfg(not(feature = "ink"))]
2008          pub fn wrap_call(&self, call: types::#call_ty) -> ::polymesh_api_client::Result<WrappedCall> {
2009            Ok(WrappedCall::new(self, call))
2010          }
2011
2012          #[cfg(feature = "ink")]
2013          pub fn wrap_call(&self, call: ::alloc::vec::Vec<u8>) -> WrappedCall {
2014            WrappedCall::new(call)
2015          }
2016        }
2017
2018        #[cfg(not(feature = "ink"))]
2019        use alloc::boxed::Box;
2020        #[async_trait::async_trait]
2021        #[cfg(not(feature = "ink"))]
2022        impl ::polymesh_api_client::ChainApi for Api {
2023          type RuntimeCall = types::#call_ty;
2024          type RuntimeEvent = types::#event_ty;
2025          type DispatchInfo = types::#dispatch_info;
2026          type DispatchError = types::sp_runtime::DispatchError;
2027
2028          async fn get_nonce(&self, account: ::polymesh_api_client::AccountId) -> ::polymesh_api_client::Result<u32> {
2029            let info = self.query().system().account(account).await?;
2030            Ok(info.nonce)
2031          }
2032
2033          async fn block_events(&self, block: Option<::polymesh_api_client::BlockHash>) -> ::polymesh_api_client::Result<::alloc::vec::Vec<::polymesh_api_client::EventRecord<Self::RuntimeEvent>>> {
2034            let system = match block {
2035              Some(block) => self.query_at(block).system(),
2036              None => self.query().system(),
2037            };
2038            Ok(system.events().await?)
2039          }
2040
2041          fn event_to_extrinsic_result(event: &::polymesh_api_client::EventRecord<Self::RuntimeEvent>) -> Option<::polymesh_api_client::ExtrinsicResult<Self>> {
2042            match &event.event {
2043              types::#event_ty::System(types::frame_system::pallet::SystemEvent::ExtrinsicSuccess { dispatch_info }) =>
2044                Some(::polymesh_api_client::ExtrinsicResult::Success(dispatch_info.clone())),
2045              types::#event_ty::System(types::frame_system::pallet::SystemEvent::ExtrinsicFailed { dispatch_info, dispatch_error }) =>
2046                Some(::polymesh_api_client::ExtrinsicResult::Failed(dispatch_info.clone(), dispatch_error.clone())),
2047              _ => None,
2048            }
2049          }
2050
2051          fn client(&self) -> &::polymesh_api_client::Client {
2052            &self.client
2053          }
2054        }
2055
2056        #[derive(Clone)]
2057        pub struct CallApi<'api> {
2058          api: &'api Api,
2059        }
2060
2061        impl<'api> CallApi<'api> {
2062          #call_fields
2063        }
2064
2065        #[cfg(not(feature = "ink"))]
2066        pub type WrappedCall = ::polymesh_api_client::Call<Api>;
2067        #[cfg(not(feature = "ink"))]
2068        pub type TransactionResults = ::polymesh_api_client::TransactionResults<Api>;
2069
2070        #[cfg(feature = "ink")]
2071        pub type WrappedCall = ::polymesh_api_ink::Call;
2072
2073        #[cfg(not(feature = "ink"))]
2074        impl From<WrappedCall> for types::#call_ty {
2075          fn from(wrapped: WrappedCall) -> Self {
2076            wrapped.into_runtime_call()
2077          }
2078        }
2079
2080        #[cfg(not(feature = "ink"))]
2081        impl From<&WrappedCall> for types::#call_ty {
2082          fn from(wrapped: &WrappedCall) -> Self {
2083            wrapped.runtime_call().clone()
2084          }
2085        }
2086
2087        #[derive(Clone)]
2088        pub struct QueryApi<'api> {
2089          api: &'api Api,
2090          #[cfg(not(feature = "ink"))]
2091          at: Option<::polymesh_api_client::BlockHash>,
2092        }
2093
2094        impl<'api> QueryApi<'api> {
2095          #query_fields
2096        }
2097
2098        #[derive(Clone)]
2099        #[cfg(not(feature = "ink"))]
2100        pub struct PagedQueryApi<'api> {
2101          api: &'api Api,
2102          at: Option<::polymesh_api_client::BlockHash>,
2103        }
2104
2105        #[cfg(not(feature = "ink"))]
2106        impl<'api> PagedQueryApi<'api> {
2107          #paged_query_fields
2108        }
2109      }
2110    }
2111  }
2112
2113  pub fn generate(md: RuntimeMetadataV14) -> TokenStream {
2114    Generator::new(md).generate()
2115  }
2116}
2117
2118pub fn generate(metadata: RuntimeMetadataPrefixed) -> Result<TokenStream, String> {
2119  match metadata.1 {
2120    #[cfg(feature = "v14")]
2121    RuntimeMetadata::V14(v14) => Ok(v14::generate(v14)),
2122    _ => {
2123      return Err(format!("Unsupported metadata version"));
2124    }
2125  }
2126}
2127
2128pub fn macro_codegen(mut buf: &[u8], mod_ident: TokenStream) -> Result<TokenStream, String> {
2129  let metadata = RuntimeMetadataPrefixed::decode(&mut buf).map_err(|e| e.to_string())?;
2130
2131  let code = generate(metadata)?;
2132  Ok(quote! {
2133    pub mod #mod_ident {
2134      #code
2135    }
2136  })
2137}