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                    "Duration" => parse_quote!(::core::time::Duration),
220                    ident => panic!("Unknown prelude type '{ident}'"),
221                }
222            }
223            _ => {
224                // paths to generated types in the root types module
225                let mut ty_path = path_segments
226                    .iter()
227                    .map(|s| syn::PathSegment::from(format_ident!("{}", s)))
228                    .collect::<syn::punctuated::Punctuated<syn::PathSegment, syn::Token![::]>>();
229                ty_path.insert(0, syn::PathSegment::from(root_mod_ident));
230                parse_quote!( #ty_path )
231            }
232        };
233        Self::Path { path, params }
234    }
235
236    /// Visits a type path, collecting all the generic type parameters from the containing type.
237    ///
238    /// # Example
239    ///
240    /// ```rust
241    /// struct S<T> {
242    ///     a: Vec<Option<T>>, // the parent type param here is `T`
243    /// }
244    /// ```
245    fn parent_type_params(&self, acc: &mut BTreeSet<TypeParameter>) {
246        match self {
247            TypePathType::Path { params, .. } => {
248                for p in params {
249                    p.parent_type_params_recurse(acc)
250                }
251            }
252            TypePathType::Vec { of } => of.parent_type_params_recurse(acc),
253            TypePathType::Array { of, .. } => of.parent_type_params_recurse(acc),
254            TypePathType::Tuple { elements } => {
255                for e in elements {
256                    e.parent_type_params_recurse(acc)
257                }
258            }
259            TypePathType::Primitive { .. } => (),
260            TypePathType::Compact { inner, .. } => inner.parent_type_params_recurse(acc),
261            TypePathType::BitVec {
262                bit_order_type,
263                bit_store_type,
264                ..
265            } => {
266                bit_order_type.parent_type_params_recurse(acc);
267                bit_store_type.parent_type_params_recurse(acc);
268            }
269        }
270    }
271
272    /// Returns true, if this is a concrete compact type.
273    pub fn is_compact(&self) -> bool {
274        matches!(self, TypePathType::Compact { .. })
275    }
276
277    /// Returns true, if this is a string type.
278    pub fn is_string(&self) -> bool {
279        matches!(
280            self,
281            TypePathType::Primitive {
282                def: TypeDefPrimitive::Str
283            }
284        )
285    }
286
287    fn to_syn_type(&self, alloc_crate_path: &AllocCratePath) -> syn::Type {
288        match &self {
289            TypePathType::Path { path, params } => {
290                let path = if params.is_empty() {
291                    parse_quote! { #path }
292                } else {
293                    let params = params.iter().map(|e| e.to_syn_type(alloc_crate_path));
294                    parse_quote! { #path< #( #params ),* > }
295                };
296                syn::Type::Path(path)
297            }
298            TypePathType::Vec { of } => {
299                let of = of.to_syn_type(alloc_crate_path);
300                let type_path = parse_quote! { #alloc_crate_path::vec::Vec<#of> };
301                syn::Type::Path(type_path)
302            }
303            TypePathType::Array { len, of } => {
304                let of = of.to_syn_type(alloc_crate_path);
305                let array = parse_quote! { [#of; #len] };
306                syn::Type::Array(array)
307            }
308            TypePathType::Tuple { elements } => {
309                let elements = elements.iter().map(|e| e.to_syn_type(alloc_crate_path));
310                let tuple = parse_quote! { (#( # elements, )* ) };
311                syn::Type::Tuple(tuple)
312            }
313            TypePathType::Primitive { def } => syn::Type::Path(match def {
314                TypeDefPrimitive::Bool => parse_quote!(::core::primitive::bool),
315                TypeDefPrimitive::Char => parse_quote!(::core::primitive::char),
316                TypeDefPrimitive::Str => parse_quote!(#alloc_crate_path::string::String),
317                TypeDefPrimitive::U8 => parse_quote!(::core::primitive::u8),
318                TypeDefPrimitive::U16 => parse_quote!(::core::primitive::u16),
319                TypeDefPrimitive::U32 => parse_quote!(::core::primitive::u32),
320                TypeDefPrimitive::U64 => parse_quote!(::core::primitive::u64),
321                TypeDefPrimitive::U128 => parse_quote!(::core::primitive::u128),
322                TypeDefPrimitive::U256 => unimplemented!("not a rust primitive"),
323                TypeDefPrimitive::I8 => parse_quote!(::core::primitive::i8),
324                TypeDefPrimitive::I16 => parse_quote!(::core::primitive::i16),
325                TypeDefPrimitive::I32 => parse_quote!(::core::primitive::i32),
326                TypeDefPrimitive::I64 => parse_quote!(::core::primitive::i64),
327                TypeDefPrimitive::I128 => parse_quote!(::core::primitive::i128),
328                TypeDefPrimitive::I256 => unimplemented!("not a rust primitive"),
329            }),
330            TypePathType::Compact {
331                inner,
332                is_field,
333                compact_type_path,
334            } => {
335                let inner = inner.to_syn_type(alloc_crate_path);
336                let path = if *is_field {
337                    // compact fields can use the inner compact type directly and be annotated with
338                    // the `compact` attribute e.g. `#[codec(compact)] my_compact_field: u128`
339                    parse_quote! ( #inner )
340                } else {
341                    parse_quote! ( #compact_type_path<#inner> )
342                };
343                syn::Type::Path(path)
344            }
345            TypePathType::BitVec {
346                bit_order_type,
347                bit_store_type,
348                decoded_bits_type_path,
349            } => {
350                let bit_order_type = bit_order_type.to_syn_type(alloc_crate_path);
351                let bit_store_type = bit_store_type.to_syn_type(alloc_crate_path);
352                let type_path =
353                    parse_quote! { #decoded_bits_type_path<#bit_store_type, #bit_order_type> };
354                syn::Type::Path(type_path)
355            }
356        }
357    }
358}
359
360/// A generic type parameter
361#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
362pub struct TypeParameter {
363    pub(super) concrete_type_id: u32,
364    pub(super) original_name: String,
365    pub(super) name: Ident,
366}
367
368impl quote::ToTokens for TypeParameter {
369    fn to_tokens(&self, tokens: &mut TokenStream) {
370        self.name.to_tokens(tokens)
371    }
372}