scale_typegen/typegen/
mod.rs

1use std::collections::btree_map::Entry;
2
3use crate::{
4    utils::{sanity_pass, types_equal},
5    TypegenError,
6};
7
8use self::{
9    ir::module_ir::ModuleIR,
10    ir::type_ir::{CompositeFieldIR, CompositeIR, CompositeIRKind, EnumIR, TypeIR, TypeIRKind},
11    settings::{
12        derives::{Derives, FlatDerivesRegistry},
13        TypeGeneratorSettings,
14    },
15    type_params::TypeParameters,
16    type_path::{TypeParameter, TypePath, TypePathType},
17};
18
19use proc_macro2::{Ident, TokenStream};
20use quote::quote;
21use scale_info::{form::PortableForm, PortableRegistry, Type, TypeDef};
22use syn::parse_quote;
23
24/// Custom error types.
25pub mod error;
26/// Intermediate representation of types and modules.
27pub mod ir;
28/// Settings passed into the `TypeGenerator`.
29pub mod settings;
30/// Logic for dealing with used and unused generic type parameters.
31pub mod type_params;
32/// Type path definition and conversion into tokens.
33pub mod type_path;
34/// Utility functions to validate that type paths in the user defined
35/// derives and substitutes exist in a type registry.
36pub mod validation;
37
38/// An interface for generating a types module.
39#[derive(Debug, Clone, Copy)]
40pub struct TypeGenerator<'a> {
41    type_registry: &'a PortableRegistry,
42    settings: &'a TypeGeneratorSettings,
43}
44
45impl<'a> TypeGenerator<'a> {
46    /// Construct a new [`TypeGenerator`].
47    pub fn new(type_registry: &'a PortableRegistry, settings: &'a TypeGeneratorSettings) -> Self {
48        Self {
49            type_registry,
50            settings,
51        }
52    }
53
54    /// The name of the generated module which will contain the generated types.
55    pub fn types_mod_ident(&self) -> &Ident {
56        &self.settings.types_mod_ident
57    }
58
59    /// The settings used by this type generator.
60    pub fn settings(&self) -> &TypeGeneratorSettings {
61        self.settings
62    }
63
64    /// The type registry backing this type generator.
65    pub fn types(&self) -> &PortableRegistry {
66        self.type_registry
67    }
68
69    /// Generate a module containing all types defined in the supplied type registry.
70    pub fn generate_types_mod(&self) -> Result<ModuleIR, TypegenError> {
71        sanity_pass(self.type_registry)?;
72
73        let flat_derives_registry = self
74            .settings
75            .derives
76            .clone()
77            .flatten_recursive_derives(self.type_registry)?;
78
79        let mut root_mod = ModuleIR::new(
80            self.settings.types_mod_ident.clone(),
81            self.settings.types_mod_ident.clone(),
82        );
83
84        for ty in &self.type_registry.types {
85            let path = &ty.ty.path;
86            // Don't generate a type if it was substituted - the target type might
87            // not be in the type registry + our resolution already performs the substitution.
88            if self.settings.substitutes.contains(&path.segments) {
89                continue;
90            }
91
92            let namespace = path.namespace();
93            // prelude types e.g. Option/Result have no namespace, so we don't generate them
94            if namespace.is_empty() {
95                continue;
96            }
97
98            // if the type is not a builtin type, insert it into the respective module
99            let ty_id = ty.id;
100            if let Some(type_ir) = self.create_type_ir(&ty.ty, &flat_derives_registry)? {
101                // Create the module this type should go into
102                let innermost_module = root_mod.get_or_insert_submodule(namespace);
103                match innermost_module.types.entry(path.clone()) {
104                    Entry::Vacant(e) => {
105                        e.insert((ty_id, type_ir));
106                    }
107                    Entry::Occupied(e) => {
108                        // There is already a type with the same type path present.
109                        // We do not just want to override it, so we check if the two types are semantically similar (structure + generics).
110                        // If not, return an error, if yes, just keep the first one.
111                        let other_ty_id = e.get().0;
112                        if !types_equal(ty_id, other_ty_id, self.type_registry) {
113                            return Err(TypegenError::DuplicateTypePath(ty.ty.path.to_string()));
114                        }
115                    }
116                };
117            }
118        }
119
120        Ok(root_mod)
121    }
122
123    /// Creates an intermediate representation of a type that can later be converted into rust tokens.
124    pub fn create_type_ir(
125        &self,
126        ty: &Type<PortableForm>,
127        flat_derives_registry: &FlatDerivesRegistry,
128    ) -> Result<Option<TypeIR>, TypegenError> {
129        // if the type is some builtin, early return, we are only interested in generating structs and enums.
130        if !matches!(ty.type_def, TypeDef::Composite(_) | TypeDef::Variant(_)) {
131            return Ok(None);
132        }
133
134        let mut type_params = TypeParameters::from_scale_info(&ty.type_params);
135
136        let name = {
137            let ident = ty.path.ident().expect(
138                "Structs and enums should have a name. Checked with namespace.is_empty() above. qed;",
139            );
140            syn::parse_str::<Ident>(&ident).map_err(|e| TypegenError::SynParseError {
141                string: ident,
142                target: core::any::type_name::<Ident>(),
143                error: e,
144            })
145        }?;
146
147        let docs = self.docs_from_scale_info(&ty.docs);
148
149        let mut could_derive_as_compact: bool = false;
150        let kind = match &ty.type_def {
151            TypeDef::Composite(composite) => {
152                let kind = self.create_composite_ir_kind(&composite.fields, &mut type_params)?;
153
154                if kind.could_derive_as_compact() {
155                    could_derive_as_compact = true;
156                }
157
158                TypeIRKind::Struct(CompositeIR { name, kind, docs })
159            }
160            TypeDef::Variant(variant) => {
161                let variants = variant
162                    .variants
163                    .iter()
164                    .map(|v| {
165                        let name = syn::parse_str::<Ident>(&v.name).map_err(|e| {
166                            TypegenError::SynParseError {
167                                string: v.name.clone(),
168                                target: core::any::type_name::<Ident>(),
169                                error: e,
170                            }
171                        })?;
172                        let kind = self.create_composite_ir_kind(&v.fields, &mut type_params)?;
173                        let docs = self.docs_from_scale_info(&v.docs);
174                        Ok((v.index, CompositeIR { kind, name, docs }))
175                    })
176                    .collect::<Result<Vec<(u8, CompositeIR)>, TypegenError>>()?;
177                TypeIRKind::Enum(EnumIR {
178                    name,
179                    variants,
180                    docs,
181                })
182            }
183            _ => unreachable!("Other variants early return before. qed."),
184        };
185
186        let mut derives = flat_derives_registry.resolve_derives_for_type(ty)?;
187        if could_derive_as_compact {
188            self.add_as_compact_derive(&mut derives);
189        }
190
191        let type_ir = TypeIR {
192            kind,
193            derives,
194            type_params,
195            insert_codec_attributes: self.settings.insert_codec_attributes,
196        };
197        Ok(Some(type_ir))
198    }
199
200    /// takes into account the settings value for `should_gen_docs`
201    pub fn docs_from_scale_info(&self, docs: &[String]) -> TokenStream {
202        self.settings
203            .should_gen_docs
204            .then_some(quote! { #( #[doc = #docs ] )* })
205            .unwrap_or_default()
206    }
207
208    /// Creates an intermediate representation of a composite.
209    pub fn create_composite_ir_kind(
210        &self,
211        fields: &[scale_info::Field<PortableForm>],
212        type_params: &mut TypeParameters,
213    ) -> Result<CompositeIRKind, TypegenError> {
214        if fields.is_empty() {
215            return Ok(CompositeIRKind::NoFields);
216        }
217
218        let all_fields_named = fields.iter().all(|f| f.name.is_some());
219        let all_fields_unnamed = fields.iter().all(|f| f.name.is_none());
220
221        if !(all_fields_named || all_fields_unnamed) {
222            return Err(TypegenError::InvalidFields(format!("{:?}", fields)));
223        }
224
225        if all_fields_named {
226            let named_fields = fields
227                .iter()
228                .map(|field| {
229                    let field_name = field.name.as_ref().unwrap();
230                    let ident = syn::parse_str::<Ident>(field_name).map_err(|e| {
231                        TypegenError::SynParseError {
232                            string: field_name.clone(),
233                            target: core::any::type_name::<Ident>(),
234                            error: e,
235                        }
236                    })?;
237
238                    let path = self.resolve_field_type_path(
239                        field.ty.id,
240                        type_params.params(),
241                        field.type_name.as_deref(),
242                    )?;
243                    let is_compact = path.is_compact();
244                    let is_boxed = field
245                        .type_name
246                        .as_ref()
247                        .map(|e| e.contains("Box<"))
248                        .unwrap_or_default();
249
250                    for param in path.parent_type_params().iter() {
251                        type_params.mark_used(param);
252                    }
253
254                    Ok((ident, CompositeFieldIR::new(path, is_compact, is_boxed)))
255                })
256                .collect::<Result<Vec<(Ident, CompositeFieldIR)>, TypegenError>>()?;
257            Ok(CompositeIRKind::Named(named_fields))
258        } else if all_fields_unnamed {
259            let unnamed_fields = fields
260                .iter()
261                .map(|field| {
262                    let path = self.resolve_field_type_path(
263                        field.ty.id,
264                        type_params.params(),
265                        field.type_name.as_deref(),
266                    )?;
267
268                    let is_compact = path.is_compact();
269                    let is_boxed = field
270                        .type_name
271                        .as_ref()
272                        .map(|e| e.contains("Box<"))
273                        .unwrap_or_default();
274
275                    for param in path.parent_type_params().iter() {
276                        type_params.mark_used(param);
277                    }
278
279                    Ok(CompositeFieldIR::new(path, is_compact, is_boxed))
280                })
281                .collect::<Result<Vec<CompositeFieldIR>, TypegenError>>()?;
282            Ok(CompositeIRKind::Unnamed(unnamed_fields))
283        } else {
284            unreachable!("Is either all unnamed or all named. qed.")
285        }
286    }
287
288    /// Creates the intermediate representation of a type from just a composite definition.
289    /// This uses just the default derives and type params are left empty.
290    pub fn upcast_composite(&self, composite: &CompositeIR) -> TypeIR {
291        // just use Default Derives + AsCompact. No access to type specific derives here. (Mainly used in subxt to create structs from enum variants...)
292        let mut derives = self.settings.derives.default_derives().clone();
293        if composite.kind.could_derive_as_compact() {
294            self.add_as_compact_derive(&mut derives)
295        }
296        TypeIR {
297            type_params: TypeParameters::from_scale_info(&[]),
298            derives,
299            insert_codec_attributes: self.settings.insert_codec_attributes,
300            kind: TypeIRKind::Struct(composite.clone()),
301        }
302    }
303
304    /// Adds a AsCompact derive, if a path to AsCompact trait/derive macro set in settings.
305    fn add_as_compact_derive(&self, derives: &mut Derives) {
306        if let Some(compact_as_type_path) = &self.settings.compact_as_type_path {
307            derives.insert_derive(parse_quote!(#compact_as_type_path));
308        }
309    }
310
311    /// Get the type path for a field of a struct or an enum variant, providing any generic
312    /// type parameters from the containing type. This is for identifying where a generic type
313    /// parameter is used in a field type e.g.
314    ///
315    /// ```rust
316    /// struct S<T> {
317    ///     a: T, // `T` is the "parent" type param from the containing type.
318    ///     b: Vec<Option<T>>, // nested use of generic type param `T`.
319    /// }
320    /// ```
321    ///
322    /// This allows generating the correct generic field type paths.
323    pub fn resolve_field_type_path(
324        &self,
325        id: u32,
326        parent_type_params: &[TypeParameter],
327        original_name: Option<&str>,
328    ) -> Result<TypePath, TypegenError> {
329        self.resolve_type_path_recurse(id, true, parent_type_params, original_name)
330    }
331
332    /// Get the type path for the given type identifier.
333    pub fn resolve_type_path(&self, id: u32) -> Result<TypePath, TypegenError> {
334        self.resolve_type_path_recurse(id, false, &[], None)
335    }
336
337    /// Visit each node in a possibly nested type definition to produce a type path.
338    ///
339    /// e.g `Result<GenericStruct<NestedGenericStruct<T>>, String>`
340    ///
341    /// if `original_name` is `Some(original_name)`, the resolved type needs to have the same `original_name`.
342    fn resolve_type_path_recurse(
343        &self,
344        id: u32,
345        is_field: bool,
346        parent_type_params: &[TypeParameter],
347        original_name: Option<&str>,
348    ) -> Result<TypePath, TypegenError> {
349        if let Some(parent_type_param) = parent_type_params.iter().find(|tp| {
350            tp.concrete_type_id == id
351                && original_name.is_none_or(|original_name| tp.original_name == original_name)
352        }) {
353            let type_path = TypePath::from_parameter(parent_type_param.clone());
354            return Ok(type_path);
355        }
356
357        let mut ty = self.resolve_type(id)?;
358
359        if ty.path.ident() == Some("Cow".to_string()) {
360            let inner_ty_id = ty.type_params[0]
361                .ty
362                .ok_or_else(|| {
363                    TypegenError::InvalidType(
364                        "type parameters to Cow are not expected to be skipped".into(),
365                    )
366                })?
367                .id;
368            ty = self.resolve_type(inner_ty_id)?
369        }
370
371        let params: Vec<TypePath> = ty
372            .type_params
373            .iter()
374            .filter_map(|f| {
375                f.ty.map(|f| self.resolve_type_path_recurse(f.id, false, parent_type_params, None))
376            })
377            .collect::<Result<Vec<TypePath>, TypegenError>>()?;
378
379        let ty = match &ty.type_def {
380            TypeDef::Composite(_) | TypeDef::Variant(_) => {
381                self.type_path_maybe_with_substitutes(&ty.path, &params)
382            }
383            TypeDef::Primitive(primitive) => TypePathType::Primitive {
384                def: primitive.clone(),
385            },
386            TypeDef::Array(arr) => {
387                let inner_type = self.resolve_type_path_recurse(
388                    arr.type_param.id,
389                    false,
390                    parent_type_params,
391                    None,
392                )?;
393                TypePathType::Array {
394                    len: arr.len as usize,
395                    of: Box::new(inner_type),
396                }
397            }
398            TypeDef::Sequence(seq) => {
399                let inner_type = self.resolve_type_path_recurse(
400                    seq.type_param.id,
401                    false,
402                    parent_type_params,
403                    None,
404                )?;
405                TypePathType::Vec {
406                    of: Box::new(inner_type),
407                }
408            }
409            TypeDef::Tuple(tuple) => {
410                let elements = tuple
411                    .fields
412                    .iter()
413                    .map(|f| self.resolve_type_path_recurse(f.id, false, parent_type_params, None))
414                    .collect::<Result<Vec<TypePath>, TypegenError>>()?;
415
416                TypePathType::Tuple { elements }
417            }
418            TypeDef::Compact(compact) => {
419                let inner_type = self.resolve_type_path_recurse(
420                    compact.type_param.id,
421                    false,
422                    parent_type_params,
423                    None,
424                )?;
425
426                let compact_type_path = self
427                    .settings
428                    .compact_type_path
429                    .as_ref()
430                    .ok_or(TypegenError::CompactPathNone)?
431                    .clone();
432
433                TypePathType::Compact {
434                    inner: Box::new(inner_type),
435                    is_field,
436                    compact_type_path,
437                }
438            }
439            TypeDef::BitSequence(bitseq) => {
440                let decoded_bits_type_path = self
441                    .settings
442                    .decoded_bits_type_path
443                    .as_ref()
444                    .ok_or(TypegenError::DecodedBitsPathNone)?
445                    .clone();
446
447                let bit_order_type = self.resolve_type_path_recurse(
448                    bitseq.bit_order_type.id,
449                    false,
450                    parent_type_params,
451                    None,
452                )?;
453
454                let bit_store_type = self.resolve_type_path_recurse(
455                    bitseq.bit_store_type.id,
456                    false,
457                    parent_type_params,
458                    None,
459                )?;
460
461                TypePathType::BitVec {
462                    bit_order_type: Box::new(bit_order_type),
463                    bit_store_type: Box::new(bit_store_type),
464                    decoded_bits_type_path,
465                }
466            }
467        };
468        Ok(TypePath::from_type(ty))
469    }
470
471    /// Converts a [`scale_info::Path`] into a [`TypePathType`], replacing all types that should be substituted.
472    pub fn type_path_maybe_with_substitutes(
473        &self,
474        path: &scale_info::Path<PortableForm>,
475        params: &[TypePath],
476    ) -> TypePathType {
477        if let Some(substitute) =
478            self.settings
479                .substitutes
480                .for_path_with_params(&path.segments, params, self.settings)
481        {
482            substitute
483        } else {
484            TypePathType::from_type_def_path(
485                path,
486                self.settings.types_mod_ident.clone(),
487                params.to_vec(),
488                &self.settings.alloc_crate_path,
489            )
490        }
491    }
492
493    /// Resolves a type, given some type id.
494    pub fn resolve_type(&self, id: u32) -> Result<&Type<PortableForm>, TypegenError> {
495        let ty = self
496            .type_registry
497            .resolve(id)
498            .ok_or(TypegenError::TypeNotFound(id))?;
499        Ok(ty)
500    }
501}