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
24pub mod error;
26pub mod ir;
28pub mod settings;
30pub mod type_params;
32pub mod type_path;
34pub mod validation;
37
38#[derive(Debug, Clone, Copy)]
40pub struct TypeGenerator<'a> {
41 type_registry: &'a PortableRegistry,
42 settings: &'a TypeGeneratorSettings,
43}
44
45impl<'a> TypeGenerator<'a> {
46 pub fn new(type_registry: &'a PortableRegistry, settings: &'a TypeGeneratorSettings) -> Self {
48 Self {
49 type_registry,
50 settings,
51 }
52 }
53
54 pub fn types_mod_ident(&self) -> &Ident {
56 &self.settings.types_mod_ident
57 }
58
59 pub fn settings(&self) -> &TypeGeneratorSettings {
61 self.settings
62 }
63
64 pub fn types(&self) -> &PortableRegistry {
66 self.type_registry
67 }
68
69 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 if self.settings.substitutes.contains(&path.segments) {
89 continue;
90 }
91
92 let namespace = path.namespace();
93 if namespace.is_empty() {
95 continue;
96 }
97
98 let ty_id = ty.id;
100 if let Some(type_ir) = self.create_type_ir(&ty.ty, &flat_derives_registry)? {
101 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 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 pub fn create_type_ir(
125 &self,
126 ty: &Type<PortableForm>,
127 flat_derives_registry: &FlatDerivesRegistry,
128 ) -> Result<Option<TypeIR>, TypegenError> {
129 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 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 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 pub fn upcast_composite(&self, composite: &CompositeIR) -> TypeIR {
291 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 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 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 pub fn resolve_type_path(&self, id: u32) -> Result<TypePath, TypegenError> {
334 self.resolve_type_path_recurse(id, false, &[], None)
335 }
336
337 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, ¶ms)
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 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 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}