scale_typegen_description/
lib.rs

1// Copyright 2019-2022 Parity Technologies (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! A crate for turning a type from a [`scale_info::PortableRegistry`] into some other, fully resolved, tree-like representation.
16//! Currently we can generate these representations for a type:
17//! - A human readable description of the type via [`crate::type_description`].
18//! - An exemplary rust value of the type via [`crate::rust_value`].
19//! - An exemplary scale value of the type via [`crate::scale_value`].
20
21mod description;
22mod formatting;
23pub mod transformer;
24
25#[cfg(feature = "type-example")]
26pub use scale_typegen;
27
28/// Create type examples for a type registry.
29#[cfg(feature = "type-example")]
30pub mod type_example;
31
32#[cfg(feature = "type-example")]
33pub use type_example::{
34    rust_value::{example as rust_value, example_from_seed as rust_value_from_seed},
35    scale_value::{example as scale_value, example_from_seed as scale_value_from_seed},
36};
37
38pub use description::type_description;
39pub use formatting::format_type_description;
40
41#[cfg(test)]
42mod tests {
43    // Note: indoc is used to make it easier to represent multi-line strings.
44    use indoc::indoc;
45
46    use parity_scale_codec::Compact;
47    use pretty_assertions::assert_eq;
48    use proc_macro2::TokenStream;
49    use scale_info::{PortableRegistry, TypeInfo};
50    use scale_typegen::TypeGeneratorSettings;
51
52    use crate::{type_description, type_example::rust_value};
53
54    fn make_type<T: TypeInfo + 'static>() -> (u32, PortableRegistry) {
55        let mut registry = scale_info::Registry::new();
56        let m = scale_info::MetaType::new::<T>();
57        let ty = registry.register_type(&m);
58        (ty.id, registry.into())
59    }
60
61    #[test]
62    fn structs() {
63        #[allow(unused)]
64        #[derive(TypeInfo)]
65        struct Human {
66            name: String,
67            age: u32,
68            male: bool,
69        }
70
71        let (type_id, type_registry) = make_type::<Human>();
72
73        assert_eq!(
74            type_description(type_id, &type_registry, true).unwrap(),
75            indoc! {
76            "struct Human {
77                name: String,
78                age: u32,
79                male: bool
80            }"}
81        );
82    }
83
84    #[test]
85    fn enums() {
86        use parity_scale_codec::Compact;
87
88        #[allow(unused)]
89        #[derive(TypeInfo)]
90        enum Shape<T> {
91            Inivisible,
92            Circle(u64),
93            Rect(Compact<u64>, Compact<u64>),
94            Polygon {
95                corners: u8,
96                radius: u64,
97            },
98            Multi {
99                shapes: Vec<Shape<u64>>,
100                t: T,
101                operation: Operation,
102            },
103        }
104
105        #[allow(unused)]
106        #[derive(TypeInfo)]
107        enum Operation {
108            Add,
109            Intersect,
110            Difference,
111        }
112
113        let (type_id, type_registry) = make_type::<Shape<u8>>();
114        assert_eq!(
115            type_description(type_id, &type_registry, true).unwrap(),
116            indoc! {
117            "enum Shape<u8> {
118                Inivisible,
119                Circle(u64),
120                Rect(Compact<u64>, Compact<u64>),
121                Polygon {
122                    corners: u8,
123                    radius: u64
124                },
125                Multi {
126                    shapes: Vec<
127                        enum Shape<u64> {
128                            Inivisible,
129                            Circle(u64),
130                            Rect(Compact<u64>, Compact<u64>),
131                            Polygon {
132                                corners: u8,
133                                radius: u64
134                            },
135                            Multi {
136                                shapes: Vec<Shape<u64>>,
137                                t: u64,
138                                operation: enum Operation {
139                                    Add,
140                                    Intersect,
141                                    Difference
142                                }
143                            }
144                        }
145                    >,
146                    t: u8,
147                    operation: Operation
148                }
149            }"}
150        );
151    }
152
153    #[test]
154    fn recursive_structs() {
155        #[allow(unused)]
156        #[derive(TypeInfo)]
157        struct Human {
158            name: String,
159            friends: Vec<Human>,
160            dad: Box<Human>,
161            home: House,
162        }
163
164        #[allow(unused)]
165        #[derive(TypeInfo)]
166        struct House {
167            inhabitants: Vec<Human>,
168        }
169
170        let (type_id, type_registry) = make_type::<Human>();
171
172        assert_eq!(
173            type_description(type_id, &type_registry, true).unwrap(),
174            indoc! {
175            "struct Human {
176                name: String,
177                friends: Vec<Human>,
178                dad: Box<Human>,
179                home: struct House {
180                    inhabitants: Vec<Human>
181                }
182            }"}
183        );
184    }
185
186    #[test]
187    fn recursive_containers() {
188        #[allow(unused)]
189        #[derive(TypeInfo)]
190        struct A {
191            bees: Vec<B>,
192        }
193
194        #[allow(unused)]
195        #[derive(TypeInfo)]
196        struct B {
197            id: u8,
198            others: Vec<B>,
199        }
200
201        let (type_id, type_registry) = make_type::<A>();
202
203        assert_eq!(
204            type_description(type_id, &type_registry, true).unwrap(),
205            indoc! {
206            "struct A {
207                bees: Vec<
208                    struct B {
209                        id: u8,
210                        others: Vec<B>
211                    }
212                >
213            }"}
214        );
215    }
216
217    #[test]
218    fn recursive_generics() {
219        #[allow(unused)]
220        #[derive(TypeInfo)]
221        struct Vec2<T> {
222            x: Box<T>,
223            y: Box<T>,
224        }
225
226        #[allow(unused)]
227        #[derive(TypeInfo)]
228        struct A {
229            bees: Vec2<B>,
230        }
231
232        #[allow(unused)]
233        #[derive(TypeInfo)]
234        struct B {
235            id: u8,
236            others: Vec2<B>,
237        }
238
239        let (type_id, type_registry) = make_type::<A>();
240
241        assert_eq!(
242            type_description(type_id, &type_registry, true).unwrap(),
243            indoc! {
244            "struct A {
245                bees: struct Vec2<B> {
246                    x: Box<
247                        struct B {
248                            id: u8,
249                            others: Vec2<B>
250                        }
251                    >,
252                    y: Box<B>
253                }
254            }"}
255        );
256    }
257
258    #[test]
259    fn multiple_fields_with_same_type() {
260        #[allow(unused)]
261        #[derive(TypeInfo)]
262        struct A {
263            x: B,
264            y: B,
265            z: B,
266        }
267
268        #[allow(unused)]
269        #[derive(TypeInfo)]
270        struct B {
271            id: u8,
272        }
273
274        let (type_id, type_registry) = make_type::<A>();
275
276        assert_eq!(
277            type_description(type_id, &type_registry, true).unwrap(),
278            indoc! {
279            "struct A {
280                x: struct B {
281                    id: u8
282                },
283                y: B,
284                z: B
285            }"}
286        );
287    }
288
289    #[test]
290    fn tuple_fields_with_same_type() {
291        #[allow(unused)]
292        #[derive(TypeInfo)]
293        struct A {
294            tup: (B, B, B),
295        }
296
297        #[allow(unused)]
298        #[derive(TypeInfo)]
299        struct B {
300            id: u8,
301        }
302
303        let (type_id, type_registry) = make_type::<A>();
304
305        assert_eq!(
306            type_description(type_id, &type_registry, true).unwrap(),
307            indoc! {
308            "struct A {
309                tup: (
310                    struct B {
311                        id: u8
312                    },
313                    B,
314                    B
315                )
316            }"}
317        );
318    }
319
320    #[test]
321    fn rust_value_compact() {
322        #[allow(unused)]
323        #[derive(TypeInfo)]
324        struct S0 {
325            #[codec(compact)]
326            n: u8,
327        }
328
329        #[allow(unused)]
330        #[derive(TypeInfo)]
331        struct S1 {
332            n: Compact<u8>,
333        }
334
335        #[allow(unused)]
336        #[derive(TypeInfo)]
337        struct T0(#[codec(compact)] u8);
338
339        #[allow(unused)]
340        #[derive(TypeInfo)]
341        struct T1(Compact<u8>);
342
343        use quote::quote;
344        use syn::parse_quote;
345
346        fn get_example<T: TypeInfo + 'static>() -> TokenStream {
347            let (type_id, type_registry) = make_type::<T>();
348            let settings = TypeGeneratorSettings::new().compact_type_path(parse_quote!(Compact));
349            rust_value::example(type_id, &type_registry, &settings).unwrap()
350        }
351
352        // Note: The 161 is pretty random and depends on the default seed of the RNG.
353        assert_eq!(
354            get_example::<S0>().to_string(),
355            quote! {types::scale_typegen_description::tests::S0{ n: 161u8, }}.to_string()
356        );
357        assert_eq!(
358            get_example::<S1>().to_string(),
359            quote! {types::scale_typegen_description::tests::S1{ n: Compact(161u8), }}.to_string()
360        );
361        assert_eq!(
362            get_example::<T0>().to_string(),
363            quote! {types::scale_typegen_description::tests::T0( 161u8, )}.to_string()
364        );
365        assert_eq!(
366            get_example::<T1>().to_string(),
367            quote! {types::scale_typegen_description::tests::T1( Compact(161u8), )}.to_string()
368        );
369    }
370}