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 = ty
137            .path
138            .ident()
139            .map(|e| syn::parse_str::<Ident>(&e))
140            .expect(
141            "Structs and enums should have a name. Checked with namespace.is_empty() above. qed;",
142        )?;
143
144        let docs = self.docs_from_scale_info(&ty.docs);
145
146        let mut could_derive_as_compact: bool = false;
147        let kind = match &ty.type_def {
148            TypeDef::Composite(composite) => {
149                let kind = self.create_composite_ir_kind(&composite.fields, &mut type_params)?;
150
151                if kind.could_derive_as_compact() {
152                    could_derive_as_compact = true;
153                }
154
155                TypeIRKind::Struct(CompositeIR { name, kind, docs })
156            }
157            TypeDef::Variant(variant) => {
158                let variants = variant
159                    .variants
160                    .iter()
161                    .map(|v| {
162                        let name = syn::parse_str::<Ident>(&v.name)?;
163                        let kind = self.create_composite_ir_kind(&v.fields, &mut type_params)?;
164                        let docs = self.docs_from_scale_info(&v.docs);
165                        Ok((v.index, CompositeIR { kind, name, docs }))
166                    })
167                    .collect::<Result<Vec<(u8, CompositeIR)>, TypegenError>>()?;
168                TypeIRKind::Enum(EnumIR {
169                    name,
170                    variants,
171                    docs,
172                })
173            }
174            _ => unreachable!("Other variants early return before. qed."),
175        };
176
177        let mut derives = flat_derives_registry.resolve_derives_for_type(ty)?;
178        if could_derive_as_compact {
179            self.add_as_compact_derive(&mut derives);
180        }
181
182        let type_ir = TypeIR {
183            kind,
184            derives,
185            type_params,
186            insert_codec_attributes: self.settings.insert_codec_attributes,
187        };
188        Ok(Some(type_ir))
189    }
190
191    /// takes into account the settings value for `should_gen_docs`
192    pub fn docs_from_scale_info(&self, docs: &[String]) -> TokenStream {
193        self.settings
194            .should_gen_docs
195            .then_some(quote! { #( #[doc = #docs ] )* })
196            .unwrap_or_default()
197    }
198
199    /// Creates an intermediate representation of a composite.
200    pub fn create_composite_ir_kind(
201        &self,
202        fields: &[scale_info::Field<PortableForm>],
203        type_params: &mut TypeParameters,
204    ) -> Result<CompositeIRKind, TypegenError> {
205        if fields.is_empty() {
206            return Ok(CompositeIRKind::NoFields);
207        }
208
209        let all_fields_named = fields.iter().all(|f| f.name.is_some());
210        let all_fields_unnamed = fields.iter().all(|f| f.name.is_none());
211
212        if !(all_fields_named || all_fields_unnamed) {
213            return Err(TypegenError::InvalidFields(format!("{:?}", fields)));
214        }
215
216        if all_fields_named {
217            let named_fields = fields
218                .iter()
219                .map(|field| {
220                    let field_name = field.name.as_ref().unwrap();
221                    let ident = syn::parse_str::<Ident>(field_name)?;
222
223                    let path = self.resolve_field_type_path(
224                        field.ty.id,
225                        type_params.params(),
226                        field.type_name.as_deref(),
227                    )?;
228                    let is_compact = path.is_compact();
229                    let is_boxed = field
230                        .type_name
231                        .as_ref()
232                        .map(|e| e.contains("Box<"))
233                        .unwrap_or_default();
234
235                    for param in path.parent_type_params().iter() {
236                        type_params.mark_used(param);
237                    }
238
239                    Ok((ident, CompositeFieldIR::new(path, is_compact, is_boxed)))
240                })
241                .collect::<Result<Vec<(Ident, CompositeFieldIR)>, TypegenError>>()?;
242            Ok(CompositeIRKind::Named(named_fields))
243        } else if all_fields_unnamed {
244            let unnamed_fields = fields
245                .iter()
246                .map(|field| {
247                    let path = self.resolve_field_type_path(
248                        field.ty.id,
249                        type_params.params(),
250                        field.type_name.as_deref(),
251                    )?;
252
253                    let is_compact = path.is_compact();
254                    let is_boxed = field
255                        .type_name
256                        .as_ref()
257                        .map(|e| e.contains("Box<"))
258                        .unwrap_or_default();
259
260                    for param in path.parent_type_params().iter() {
261                        type_params.mark_used(param);
262                    }
263
264                    Ok(CompositeFieldIR::new(path, is_compact, is_boxed))
265                })
266                .collect::<Result<Vec<CompositeFieldIR>, TypegenError>>()?;
267            Ok(CompositeIRKind::Unnamed(unnamed_fields))
268        } else {
269            unreachable!("Is either all unnamed or all named. qed.")
270        }
271    }
272
273    /// Creates the intermediate representation of a type from just a composite definition.
274    /// This uses just the default derives and type params are left empty.
275    pub fn upcast_composite(&self, composite: &CompositeIR) -> TypeIR {
276        // just use Default Derives + AsCompact. No access to type specific derives here. (Mainly used in subxt to create structs from enum variants...)
277        let mut derives = self.settings.derives.default_derives().clone();
278        if composite.kind.could_derive_as_compact() {
279            self.add_as_compact_derive(&mut derives)
280        }
281        TypeIR {
282            type_params: TypeParameters::from_scale_info(&[]),
283            derives,
284            insert_codec_attributes: self.settings.insert_codec_attributes,
285            kind: TypeIRKind::Struct(composite.clone()),
286        }
287    }
288
289    /// Adds a AsCompact derive, if a path to AsCompact trait/derive macro set in settings.
290    fn add_as_compact_derive(&self, derives: &mut Derives) {
291        if let Some(compact_as_type_path) = &self.settings.compact_as_type_path {
292            derives.insert_derive(parse_quote!(#compact_as_type_path));
293        }
294    }
295
296    /// Get the type path for a field of a struct or an enum variant, providing any generic
297    /// type parameters from the containing type. This is for identifying where a generic type
298    /// parameter is used in a field type e.g.
299    ///
300    /// ```rust
301    /// struct S<T> {
302    ///     a: T, // `T` is the "parent" type param from the containing type.
303    ///     b: Vec<Option<T>>, // nested use of generic type param `T`.
304    /// }
305    /// ```
306    ///
307    /// This allows generating the correct generic field type paths.
308    pub fn resolve_field_type_path(
309        &self,
310        id: u32,
311        parent_type_params: &[TypeParameter],
312        original_name: Option<&str>,
313    ) -> Result<TypePath, TypegenError> {
314        self.resolve_type_path_recurse(id, true, parent_type_params, original_name)
315    }
316
317    /// Get the type path for the given type identifier.
318    pub fn resolve_type_path(&self, id: u32) -> Result<TypePath, TypegenError> {
319        self.resolve_type_path_recurse(id, false, &[], None)
320    }
321
322    /// Visit each node in a possibly nested type definition to produce a type path.
323    ///
324    /// e.g `Result<GenericStruct<NestedGenericStruct<T>>, String>`
325    ///
326    /// if `original_name` is `Some(original_name)`, the resolved type needs to have the same `original_name`.
327    fn resolve_type_path_recurse(
328        &self,
329        id: u32,
330        is_field: bool,
331        parent_type_params: &[TypeParameter],
332        original_name: Option<&str>,
333    ) -> Result<TypePath, TypegenError> {
334        if let Some(parent_type_param) = parent_type_params.iter().find(|tp| {
335            tp.concrete_type_id == id
336                && original_name.is_none_or(|original_name| tp.original_name == original_name)
337        }) {
338            let type_path = TypePath::from_parameter(parent_type_param.clone());
339            return Ok(type_path);
340        }
341
342        let mut ty = self.resolve_type(id)?;
343
344        if ty.path.ident() == Some("Cow".to_string()) {
345            let inner_ty_id = ty.type_params[0]
346                .ty
347                .ok_or_else(|| {
348                    TypegenError::InvalidType(
349                        "type parameters to Cow are not expected to be skipped".into(),
350                    )
351                })?
352                .id;
353            ty = self.resolve_type(inner_ty_id)?
354        }
355
356        let params: Vec<TypePath> = ty
357            .type_params
358            .iter()
359            .filter_map(|f| {
360                f.ty.map(|f| self.resolve_type_path_recurse(f.id, false, parent_type_params, None))
361            })
362            .collect::<Result<Vec<TypePath>, TypegenError>>()?;
363
364        let ty = match &ty.type_def {
365            TypeDef::Composite(_) | TypeDef::Variant(_) => {
366                self.type_path_maybe_with_substitutes(&ty.path, &params)
367            }
368            TypeDef::Primitive(primitive) => TypePathType::Primitive {
369                def: primitive.clone(),
370            },
371            TypeDef::Array(arr) => {
372                let inner_type = self.resolve_type_path_recurse(
373                    arr.type_param.id,
374                    false,
375                    parent_type_params,
376                    None,
377                )?;
378                TypePathType::Array {
379                    len: arr.len as usize,
380                    of: Box::new(inner_type),
381                }
382            }
383            TypeDef::Sequence(seq) => {
384                let inner_type = self.resolve_type_path_recurse(
385                    seq.type_param.id,
386                    false,
387                    parent_type_params,
388                    None,
389                )?;
390                TypePathType::Vec {
391                    of: Box::new(inner_type),
392                }
393            }
394            TypeDef::Tuple(tuple) => {
395                let elements = tuple
396                    .fields
397                    .iter()
398                    .map(|f| self.resolve_type_path_recurse(f.id, false, parent_type_params, None))
399                    .collect::<Result<Vec<TypePath>, TypegenError>>()?;
400
401                TypePathType::Tuple { elements }
402            }
403            TypeDef::Compact(compact) => {
404                let inner_type = self.resolve_type_path_recurse(
405                    compact.type_param.id,
406                    false,
407                    parent_type_params,
408                    None,
409                )?;
410
411                let compact_type_path = self
412                    .settings
413                    .compact_type_path
414                    .as_ref()
415                    .ok_or(TypegenError::CompactPathNone)?
416                    .clone();
417
418                TypePathType::Compact {
419                    inner: Box::new(inner_type),
420                    is_field,
421                    compact_type_path,
422                }
423            }
424            TypeDef::BitSequence(bitseq) => {
425                let decoded_bits_type_path = self
426                    .settings
427                    .decoded_bits_type_path
428                    .as_ref()
429                    .ok_or(TypegenError::DecodedBitsPathNone)?
430                    .clone();
431
432                let bit_order_type = self.resolve_type_path_recurse(
433                    bitseq.bit_order_type.id,
434                    false,
435                    parent_type_params,
436                    None,
437                )?;
438
439                let bit_store_type = self.resolve_type_path_recurse(
440                    bitseq.bit_store_type.id,
441                    false,
442                    parent_type_params,
443                    None,
444                )?;
445
446                TypePathType::BitVec {
447                    bit_order_type: Box::new(bit_order_type),
448                    bit_store_type: Box::new(bit_store_type),
449                    decoded_bits_type_path,
450                }
451            }
452        };
453        Ok(TypePath::from_type(ty))
454    }
455
456    /// Converts a [`scale_info::Path`] into a [`TypePathType`], replacing all types that should be substituted.
457    pub fn type_path_maybe_with_substitutes(
458        &self,
459        path: &scale_info::Path<PortableForm>,
460        params: &[TypePath],
461    ) -> TypePathType {
462        if let Some(substitute) =
463            self.settings
464                .substitutes
465                .for_path_with_params(&path.segments, params, self.settings)
466        {
467            substitute
468        } else {
469            TypePathType::from_type_def_path(
470                path,
471                self.settings.types_mod_ident.clone(),
472                params.to_vec(),
473                &self.settings.alloc_crate_path,
474            )
475        }
476    }
477
478    /// Resolves a type, given some type id.
479    pub fn resolve_type(&self, id: u32) -> Result<&Type<PortableForm>, TypegenError> {
480        let ty = self
481            .type_registry
482            .resolve(id)
483            .ok_or(TypegenError::TypeNotFound(id))?;
484        Ok(ty)
485    }
486}