scale_typegen/typegen/
type_path.rs

1// Copyright 2019-2023 Parity Technologies (UK) Ltd.
2// This file is dual-licensed as Apache-2.0 or GPL-3.0.
3// see LICENSE for license details.
4
5use proc_macro2::{Ident, TokenStream};
6use quote::{format_ident, ToTokens};
7use scale_info::{form::PortableForm, Path, TypeDefPrimitive};
8use std::collections::BTreeSet;
9use syn::parse_quote;
10
11use crate::TypeGeneratorSettings;
12
13use super::ir::ToTokensWithSettings;
14use super::settings::AllocCratePath;
15
16/// An opaque struct representing a type path. The main usage of this is
17/// to spit out as tokens in some `quote!{ ... }` macro; the inner structure
18/// should be unimportant.
19#[derive(Clone, Debug)]
20pub struct TypePath(TypePathInner);
21
22/// The type path to either a concrete type or a generic type parameter
23#[derive(Clone, Debug)]
24pub enum TypePathInner {
25    /// Generic type parameter
26    Parameter(TypeParameter),
27    /// Concrete type
28    Type(TypePathType),
29}
30
31impl ToTokensWithSettings for TypePath {
32    fn to_tokens(&self, tokens: &mut TokenStream, settings: &TypeGeneratorSettings) {
33        let syn_type = self.to_syn_type(&settings.alloc_crate_path);
34        syn_type.to_tokens(tokens)
35    }
36}
37
38impl TypePath {
39    /// Construct a [`TypePath`] from a [`TypeParameter`]
40    pub fn from_parameter(param: TypeParameter) -> TypePath {
41        TypePath(TypePathInner::Parameter(param))
42    }
43
44    /// Construct a [`TypePath`] from a [`TypeParameter`]
45    pub fn from_type(ty: TypePathType) -> TypePath {
46        TypePath(TypePathInner::Type(ty))
47    }
48
49    /// Construct a [`TypePath`] from a [`syn::TypePath`]
50    pub fn from_syn_path(path: syn::Path) -> TypePath {
51        // Note; this doesn't parse the parameters or anything, but since nothing external
52        // can inspect this structure, and the ToTokens impl works either way, it should be ok.
53        TypePath(TypePathInner::Type(TypePathType::Path {
54            path,
55            params: Vec::new(),
56        }))
57    }
58
59    pub(crate) fn to_syn_type(&self, alloc_crate_path: &AllocCratePath) -> syn::Type {
60        match &self.0 {
61            TypePathInner::Parameter(ty_param) => syn::Type::Path(parse_quote! { #ty_param }),
62            TypePathInner::Type(ty) => ty.to_syn_type(alloc_crate_path),
63        }
64    }
65
66    /// Returns true, if this is a concrete compact type.
67    pub fn is_compact(&self) -> bool {
68        matches!(&self.0, TypePathInner::Type(ty) if ty.is_compact())
69    }
70
71    /// Returns true, if this is a concrete string type.
72    pub fn is_string(&self) -> bool {
73        matches!(&self.0, TypePathInner::Type(ty) if ty.is_string())
74    }
75
76    /// Returns true, if this is an unsigned integer (anywhere between u8 and u128).
77    pub fn is_uint_up_to_u128(&self) -> bool {
78        matches!(
79            &self.0,
80            TypePathInner::Type(TypePathType::Primitive {
81                def: TypeDefPrimitive::U8
82                    | TypeDefPrimitive::U16
83                    | TypeDefPrimitive::U32
84                    | TypeDefPrimitive::U64
85                    | TypeDefPrimitive::U128
86            })
87        )
88    }
89
90    /// Returns the type parameters in a path which are inherited from the containing type.
91    ///
92    /// # Example
93    ///
94    /// ```rust
95    /// struct S<T> {
96    ///     a: Vec<Option<T>>, // the parent type param here is `T`
97    /// }
98    /// ```
99    pub fn parent_type_params(&self) -> BTreeSet<TypeParameter> {
100        let mut acc = BTreeSet::<TypeParameter>::new();
101        self.parent_type_params_recurse(&mut acc);
102        acc
103    }
104
105    fn parent_type_params_recurse(&self, acc: &mut BTreeSet<TypeParameter>) {
106        match &self.0 {
107            TypePathInner::Parameter(type_parameter) => {
108                acc.insert(type_parameter.clone());
109            }
110            TypePathInner::Type(type_path) => type_path.parent_type_params(acc),
111        }
112    }
113
114    /// Gets the vector type parameter if the data is represented as `TypeDef::Sequence`.
115    ///
116    /// **Note:** Utilized for transforming `std::vec::Vec<T>` into slices `&[T]` for the storage API.
117    pub fn vec_type_param(&self) -> Option<&TypePath> {
118        let ty = match &self.0 {
119            TypePathInner::Type(ty) => ty,
120            _ => return None,
121        };
122
123        match ty {
124            TypePathType::Vec { of } => Some(of),
125            _ => None,
126        }
127    }
128}
129
130/// The path of a Concrete type
131#[derive(Clone, Debug)]
132pub enum TypePathType {
133    /// A user-defined type (non-builtin struct or enum)
134    Path {
135        /// Type path
136        path: syn::Path,
137        /// Generic type parameters
138        params: Vec<TypePath>,
139    },
140    /// A variable sized sequences of elements of some type. See [`std::vec::Vec`].
141    Vec {
142        /// Type of elements in the vector.
143        of: Box<TypePath>,
144    },
145    /// A fixed length array that contains `len` elements of some type.
146    Array {
147        /// number of elements in the array
148        len: usize,
149        /// Type path
150        of: Box<TypePath>,
151    },
152    /// A Tuple type
153    Tuple {
154        /// Types that make up this tuple
155        elements: Vec<TypePath>,
156    },
157    /// Primitive type
158    Primitive {
159        /// A primitive Rust type.
160        def: TypeDefPrimitive,
161    },
162    /// A compact encoded type
163    Compact {
164        /// The type that is being compact encoded
165        inner: Box<TypePath>,
166        /// is this type used as a field of a struct or enum right now?
167        is_field: bool,
168        /// path to the `Compact` type (usually [`parity_scale_codec::Compact`])
169        compact_type_path: syn::Path,
170    },
171    /// A bit vector
172    BitVec {
173        /// Order type
174        bit_order_type: Box<TypePath>,
175        /// Store type
176        bit_store_type: Box<TypePath>,
177        /// A user defined wrapper type around scale_bits::Bits. Should be generic over the `order` and `store` types.
178        decoded_bits_type_path: syn::Path,
179    },
180}
181
182impl TypePathType {
183    /// Constructs a [`TypePathType`] from some context information.
184    pub fn from_type_def_path(
185        path: &Path<PortableForm>,
186        root_mod_ident: Ident,
187        params: Vec<TypePath>,
188        alloc_crate_path: &AllocCratePath,
189    ) -> Self {
190        let path_segments = &*path.segments;
191
192        let path: syn::Path = match path_segments {
193            [] => panic!("Type has no ident"),
194            [ident] => {
195                // paths to prelude types
196                match ident.as_str() {
197                    "Option" => parse_quote!(::core::option::Option),
198                    "Result" => parse_quote!(::core::result::Result),
199                    "Cow" => parse_quote!(#alloc_crate_path::borrow::Cow),
200                    "BTreeMap" => parse_quote!(#alloc_crate_path::collections::BTreeMap),
201                    "BTreeSet" => parse_quote!(#alloc_crate_path::collections::BTreeSet),
202                    "BinaryHeap" => parse_quote!(#alloc_crate_path::collections::BinaryHeap),
203                    "VecDeque" => parse_quote!(#alloc_crate_path::collections::VecDeque),
204                    "LinkedList" => parse_quote!(#alloc_crate_path::collections::LinkedList),
205                    "Range" => parse_quote!(::core::ops::Range),
206                    "RangeInclusive" => parse_quote!(::core::ops::RangeInclusive),
207                    "NonZeroI8" => parse_quote!(::core::num::NonZeroI8),
208                    "NonZeroU8" => parse_quote!(::core::num::NonZeroU8),
209                    "NonZeroI16" => parse_quote!(::core::num::NonZeroI16),
210                    "NonZeroU16" => parse_quote!(::core::num::NonZeroU16),
211                    "NonZeroI32" => parse_quote!(::core::num::NonZeroI32),
212                    "NonZeroU32" => parse_quote!(::core::num::NonZeroU32),
213                    "NonZeroI64" => parse_quote!(::core::num::NonZeroI64),
214                    "NonZeroU64" => parse_quote!(::core::num::NonZeroU64),
215                    "NonZeroI128" => parse_quote!(::core::num::NonZeroI128),
216                    "NonZeroU128" => parse_quote!(::core::num::NonZeroU128),
217                    "NonZeroIsize" => parse_quote!(::core::num::NonZeroIsize),
218                    "NonZeroUsize" => parse_quote!(::core::num::NonZeroUsize),
219                    ident => panic!("Unknown prelude type '{ident}'"),
220                }
221            }
222            _ => {
223                // paths to generated types in the root types module
224                let mut ty_path = path_segments
225                    .iter()
226                    .map(|s| syn::PathSegment::from(format_ident!("{}", s)))
227                    .collect::<syn::punctuated::Punctuated<syn::PathSegment, syn::Token![::]>>();
228                ty_path.insert(0, syn::PathSegment::from(root_mod_ident));
229                parse_quote!( #ty_path )
230            }
231        };
232        Self::Path { path, params }
233    }
234
235    /// Visits a type path, collecting all the generic type parameters from the containing type.
236    ///
237    /// # Example
238    ///
239    /// ```rust
240    /// struct S<T> {
241    ///     a: Vec<Option<T>>, // the parent type param here is `T`
242    /// }
243    /// ```
244    fn parent_type_params(&self, acc: &mut BTreeSet<TypeParameter>) {
245        match self {
246            TypePathType::Path { params, .. } => {
247                for p in params {
248                    p.parent_type_params_recurse(acc)
249                }
250            }
251            TypePathType::Vec { of } => of.parent_type_params_recurse(acc),
252            TypePathType::Array { of, .. } => of.parent_type_params_recurse(acc),
253            TypePathType::Tuple { elements } => {
254                for e in elements {
255                    e.parent_type_params_recurse(acc)
256                }
257            }
258            TypePathType::Primitive { .. } => (),
259            TypePathType::Compact { inner, .. } => inner.parent_type_params_recurse(acc),
260            TypePathType::BitVec {
261                bit_order_type,
262                bit_store_type,
263                ..
264            } => {
265                bit_order_type.parent_type_params_recurse(acc);
266                bit_store_type.parent_type_params_recurse(acc);
267            }
268        }
269    }
270
271    /// Returns true, if this is a concrete compact type.
272    pub fn is_compact(&self) -> bool {
273        matches!(self, TypePathType::Compact { .. })
274    }
275
276    /// Returns true, if this is a string type.
277    pub fn is_string(&self) -> bool {
278        matches!(
279            self,
280            TypePathType::Primitive {
281                def: TypeDefPrimitive::Str
282            }
283        )
284    }
285
286    fn to_syn_type(&self, alloc_crate_path: &AllocCratePath) -> syn::Type {
287        match &self {
288            TypePathType::Path { path, params } => {
289                let path = if params.is_empty() {
290                    parse_quote! { #path }
291                } else {
292                    let params = params.iter().map(|e| e.to_syn_type(alloc_crate_path));
293                    parse_quote! { #path< #( #params ),* > }
294                };
295                syn::Type::Path(path)
296            }
297            TypePathType::Vec { of } => {
298                let of = of.to_syn_type(alloc_crate_path);
299                let type_path = parse_quote! { #alloc_crate_path::vec::Vec<#of> };
300                syn::Type::Path(type_path)
301            }
302            TypePathType::Array { len, of } => {
303                let of = of.to_syn_type(alloc_crate_path);
304                let array = parse_quote! { [#of; #len] };
305                syn::Type::Array(array)
306            }
307            TypePathType::Tuple { elements } => {
308                let elements = elements.iter().map(|e| e.to_syn_type(alloc_crate_path));
309                let tuple = parse_quote! { (#( # elements, )* ) };
310                syn::Type::Tuple(tuple)
311            }
312            TypePathType::Primitive { def } => syn::Type::Path(match def {
313                TypeDefPrimitive::Bool => parse_quote!(::core::primitive::bool),
314                TypeDefPrimitive::Char => parse_quote!(::core::primitive::char),
315                TypeDefPrimitive::Str => parse_quote!(#alloc_crate_path::string::String),
316                TypeDefPrimitive::U8 => parse_quote!(::core::primitive::u8),
317                TypeDefPrimitive::U16 => parse_quote!(::core::primitive::u16),
318                TypeDefPrimitive::U32 => parse_quote!(::core::primitive::u32),
319                TypeDefPrimitive::U64 => parse_quote!(::core::primitive::u64),
320                TypeDefPrimitive::U128 => parse_quote!(::core::primitive::u128),
321                TypeDefPrimitive::U256 => unimplemented!("not a rust primitive"),
322                TypeDefPrimitive::I8 => parse_quote!(::core::primitive::i8),
323                TypeDefPrimitive::I16 => parse_quote!(::core::primitive::i16),
324                TypeDefPrimitive::I32 => parse_quote!(::core::primitive::i32),
325                TypeDefPrimitive::I64 => parse_quote!(::core::primitive::i64),
326                TypeDefPrimitive::I128 => parse_quote!(::core::primitive::i128),
327                TypeDefPrimitive::I256 => unimplemented!("not a rust primitive"),
328            }),
329            TypePathType::Compact {
330                inner,
331                is_field,
332                compact_type_path,
333            } => {
334                let inner = inner.to_syn_type(alloc_crate_path);
335                let path = if *is_field {
336                    // compact fields can use the inner compact type directly and be annotated with
337                    // the `compact` attribute e.g. `#[codec(compact)] my_compact_field: u128`
338                    parse_quote! ( #inner )
339                } else {
340                    parse_quote! ( #compact_type_path<#inner> )
341                };
342                syn::Type::Path(path)
343            }
344            TypePathType::BitVec {
345                bit_order_type,
346                bit_store_type,
347                decoded_bits_type_path,
348            } => {
349                let bit_order_type = bit_order_type.to_syn_type(alloc_crate_path);
350                let bit_store_type = bit_store_type.to_syn_type(alloc_crate_path);
351                let type_path =
352                    parse_quote! { #decoded_bits_type_path<#bit_store_type, #bit_order_type> };
353                syn::Type::Path(type_path)
354            }
355        }
356    }
357}
358
359/// A generic type parameter
360#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
361pub struct TypeParameter {
362    pub(super) concrete_type_id: u32,
363    pub(super) original_name: String,
364    pub(super) name: Ident,
365}
366
367impl quote::ToTokens for TypeParameter {
368    fn to_tokens(&self, tokens: &mut TokenStream) {
369        self.name.to_tokens(tokens)
370    }
371}