scale_info/
build.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//! Builders for defining metadata for variant types (enums), and composite types (structs).
16//! They are designed to allow only construction of valid definitions.
17//!
18//! In most cases we recommend using the `scale-info-derive` crate to auto generate the builder
19//! constructions.
20//!
21//! # Examples
22//!
23//! ## Generic struct
24//! ```
25//! # use scale_info::{build::Fields, type_params, MetaType, Path, Type, TypeInfo};
26//! struct Foo<T> {
27//!     bar: T,
28//!     data: u64,
29//! }
30//!
31//! impl<T> TypeInfo for Foo<T>
32//! where
33//!     T: TypeInfo + 'static,
34//! {
35//!     type Identity = Self;
36//!
37//!     fn type_info() -> Type {
38//!         Type::builder()
39//!             .path(Path::new("Foo", module_path!()))
40//!             .type_params(type_params!(T))
41//!             .composite(Fields::named()
42//!                 .field(|f| f.ty::<T>().name("bar").type_name("T"))
43//!                 .field(|f| f.ty::<u64>().name("data").type_name("u64"))
44//!             )
45//!     }
46//! }
47//! ```
48//! ## Tuple struct
49//! ```
50//! # use scale_info::{build::Fields, MetaType, Path, Type, TypeInfo};
51//! struct Foo(u32, bool);
52//!
53//! impl TypeInfo for Foo {
54//!     type Identity = Self;
55//!
56//!     fn type_info() -> Type {
57//!         Type::builder()
58//!             .path(Path::new("Foo", module_path!()))
59//!             .composite(Fields::unnamed()
60//!                 .field(|f| f.ty::<u32>().type_name("u32"))
61//!                 .field(|f| f.ty::<bool>().type_name("bool"))
62//!             )
63//!     }
64//! }
65//! ```
66//! ## Enum with fields
67//! ```
68//! # use scale_info::{build::{Fields, Variants}, type_params, MetaType, Path, Type, TypeInfo, Variant};
69//! enum Foo<T>{
70//!     A(T),
71//!     B { f: u32 },
72//!     C,
73//! }
74//!
75//! impl<T> TypeInfo for Foo<T>
76//! where
77//!     T: TypeInfo + 'static,
78//! {
79//!     type Identity = Self;
80//!
81//!     fn type_info() -> Type {
82//!         Type::builder()
83//!             .path(Path::new("Foo", module_path!()))
84//!                .type_params(type_params!(T))
85//!             .variant(
86//!                 Variants::new()
87//!                     .variant("A", |v| v
88//!                         .index(0)
89//!                         .fields(Fields::unnamed().field(|f| f.ty::<T>().type_name("T")))
90//!                     )
91//!                     .variant("B", |v| v
92//!                         .index(1)
93//!                         .fields(Fields::named().field(|f| f.ty::<u32>().name("f").type_name("u32")))
94//!                     )
95//!                     .variant_unit("A", 2)
96//!             )
97//!     }
98//! }
99//! ```
100//! ## Enum without fields, aka C-style enums.
101//! ```
102//! # use scale_info::{build::{Fields, Variants}, MetaType, Path, Type, TypeInfo, Variant};
103//! enum Foo {
104//!     A,
105//!     B,
106//!     C = 33,
107//! }
108//!
109//! impl TypeInfo for Foo {
110//!     type Identity = Self;
111//!
112//!     fn type_info() -> Type {
113//!         Type::builder()
114//!             .path(Path::new("Foo", module_path!()))
115//!             .variant(
116//!                 Variants::new()
117//!                     .variant("A", |v| v.index(1))
118//!                     .variant("B", |v| v.index(2))
119//!                     .variant("C", |v| v.index(33))
120//!             )
121//!     }
122//! }
123//! ```
124
125use crate::prelude::{marker::PhantomData, vec::Vec};
126
127use crate::{
128    form::{Form, MetaForm, PortableForm},
129    Field, MetaType, Path, Type, TypeDef, TypeDefComposite, TypeDefVariant, TypeInfo,
130    TypeParameter, Variant,
131};
132
133/// State types for type builders which require a Path.
134pub mod state {
135    /// State where the builder has not assigned a Path to the type
136    pub enum PathNotAssigned {}
137    /// State where the builder has assigned a Path to the type
138    pub enum PathAssigned {}
139}
140
141/// Builds a [`Type`](`crate::Type`)
142#[must_use]
143pub struct TypeBuilder<F: Form = MetaForm, S = state::PathNotAssigned> {
144    path: Option<Path<F>>,
145    type_params: Vec<TypeParameter<F>>,
146    docs: Vec<F::String>,
147    marker: PhantomData<fn() -> (F, S)>,
148}
149
150impl<F: Form, S> Default for TypeBuilder<F, S> {
151    fn default() -> Self {
152        TypeBuilder {
153            path: Default::default(),
154            type_params: Default::default(),
155            docs: Default::default(),
156            marker: Default::default(),
157        }
158    }
159}
160
161impl<F: Form> TypeBuilder<F, state::PathNotAssigned> {
162    /// Set the Path for the type
163    pub fn path(self, path: Path<F>) -> TypeBuilder<F, state::PathAssigned> {
164        TypeBuilder {
165            path: Some(path),
166            type_params: self.type_params,
167            docs: self.docs,
168            marker: Default::default(),
169        }
170    }
171}
172
173impl<F: Form> TypeBuilder<F, state::PathAssigned> {
174    fn build<D>(self, type_def: D) -> Type<F>
175    where
176        D: Into<TypeDef<F>>,
177    {
178        let path = self.path.expect("Path not assigned");
179        Type::new(path, self.type_params, type_def, self.docs)
180    }
181
182    /// Construct a "variant" type i.e an `enum`
183    pub fn variant(self, builder: Variants<F>) -> Type<F> {
184        self.build(builder.finalize())
185    }
186
187    /// Construct a "composite" type i.e. a `struct`
188    pub fn composite<T>(self, fields: FieldsBuilder<F, T>) -> Type<F> {
189        self.build(TypeDefComposite::new(fields.finalize()))
190    }
191}
192
193impl<F: Form, S> TypeBuilder<F, S> {
194    /// Set the type parameters if it's a generic type
195    pub fn type_params<I>(mut self, type_params: I) -> Self
196    where
197        I: IntoIterator<Item = TypeParameter<F>>,
198    {
199        self.type_params = type_params.into_iter().collect();
200        self
201    }
202}
203
204impl<S> TypeBuilder<PortableForm, S> {
205    #[cfg(feature = "docs")]
206    /// Set the type documentation (for types in portable form).
207    pub fn docs_portable<I>(mut self, docs: I) -> Self
208    where
209        I: IntoIterator<Item = <PortableForm as Form>::String>,
210    {
211        self.docs = docs.into_iter().collect();
212        self
213    }
214}
215
216impl<S> TypeBuilder<MetaForm, S> {
217    #[cfg(feature = "docs")]
218    /// Set the type documentation
219    pub fn docs(mut self, docs: &[&'static str]) -> Self {
220        self.docs = docs.to_vec();
221        self
222    }
223
224    #[cfg(not(feature = "docs"))]
225    #[inline]
226    /// Doc capture is not enabled via the "docs" feature so this is a no-op.
227    pub fn docs(self, _docs: &'static [&'static str]) -> Self {
228        self
229    }
230
231    /// Set the type documentation, always captured even if the "docs" feature is not enabled.
232    pub fn docs_always(mut self, docs: &[&'static str]) -> Self {
233        self.docs = docs.to_vec();
234        self
235    }
236}
237
238/// A fields builder has no fields (e.g. a unit struct)
239pub enum NoFields {}
240/// A fields builder only allows named fields (e.g. a struct)
241pub enum NamedFields {}
242/// A fields builder only allows unnamed fields (e.g. a tuple)
243pub enum UnnamedFields {}
244
245/// Provides FieldsBuilder constructors
246pub struct Fields<F: Form>(PhantomData<fn() -> F>);
247
248impl<F: Form> Fields<F> {
249    /// The type construct has no fields
250    pub fn unit() -> FieldsBuilder<F, NoFields> {
251        FieldsBuilder::<F, NoFields>::default()
252    }
253
254    /// Fields for a type construct with named fields
255    pub fn named() -> FieldsBuilder<F, NamedFields> {
256        FieldsBuilder::default()
257    }
258
259    /// Fields for a type construct with unnamed fields
260    pub fn unnamed() -> FieldsBuilder<F, UnnamedFields> {
261        FieldsBuilder::default()
262    }
263}
264
265/// Build a set of either all named (e.g. for a struct) or all unnamed (e.g. for a tuple struct)
266#[must_use]
267pub struct FieldsBuilder<F: Form, T> {
268    fields: Vec<Field<F>>,
269    marker: PhantomData<fn() -> T>,
270}
271
272impl<F: Form, T> Default for FieldsBuilder<F, T> {
273    fn default() -> Self {
274        Self {
275            fields: Vec::new(),
276            marker: Default::default(),
277        }
278    }
279}
280
281impl<F: Form, T> FieldsBuilder<F, T> {
282    /// Complete building and return the set of fields
283    pub fn finalize(self) -> Vec<Field<F>> {
284        self.fields
285    }
286}
287
288impl<T> FieldsBuilder<MetaForm, T> {
289    fn push_field(mut self, field: Field) -> Self {
290        // filter out fields of PhantomData
291        if !field.ty.is_phantom() {
292            self.fields.push(field);
293        }
294        self
295    }
296}
297
298impl FieldsBuilder<MetaForm, NamedFields> {
299    /// Add a named field constructed using the builder.
300    pub fn field<B>(self, builder: B) -> Self
301    where
302        B: Fn(
303            FieldBuilder,
304        )
305            -> FieldBuilder<MetaForm, field_state::NameAssigned, field_state::TypeAssigned>,
306    {
307        let builder = builder(FieldBuilder::new());
308        self.push_field(builder.finalize())
309    }
310}
311
312impl FieldsBuilder<MetaForm, UnnamedFields> {
313    /// Add an unnamed field constructed using the builder.
314    pub fn field<B>(self, builder: B) -> Self
315    where
316        B: Fn(
317            FieldBuilder,
318        )
319            -> FieldBuilder<MetaForm, field_state::NameNotAssigned, field_state::TypeAssigned>,
320    {
321        let builder = builder(FieldBuilder::new());
322        self.push_field(builder.finalize())
323    }
324}
325
326impl<T> FieldsBuilder<PortableForm, T> {
327    fn push_field(mut self, field: Field<PortableForm>) -> Self {
328        self.fields.push(field);
329        self
330    }
331}
332
333impl FieldsBuilder<PortableForm, NamedFields> {
334    /// Add a named field constructed using the builder.
335    pub fn field_portable<B>(self, builder: B) -> Self
336    where
337        B: Fn(
338            FieldBuilder<PortableForm, field_state::NameNotAssigned, field_state::TypeNotAssigned>,
339        )
340            -> FieldBuilder<PortableForm, field_state::NameAssigned, field_state::TypeAssigned>,
341    {
342        let builder = builder(FieldBuilder::new());
343        self.push_field(builder.finalize())
344    }
345}
346
347impl FieldsBuilder<PortableForm, UnnamedFields> {
348    /// Add an unnamed field constructed using the builder.
349    pub fn field_portable<B>(self, builder: B) -> Self
350    where
351        B: Fn(
352            FieldBuilder<PortableForm, field_state::NameNotAssigned, field_state::TypeNotAssigned>,
353        ) -> FieldBuilder<
354            PortableForm,
355            field_state::NameNotAssigned,
356            field_state::TypeAssigned,
357        >,
358    {
359        let builder = builder(FieldBuilder::new());
360        self.push_field(builder.finalize())
361    }
362}
363
364/// Type states for building a field.
365pub mod field_state {
366    /// A name has not been assigned to the field.
367    pub enum NameNotAssigned {}
368    /// A name has been assigned to the field.
369    pub enum NameAssigned {}
370    /// A type has not been assigned to the field.
371    pub enum TypeNotAssigned {}
372    /// A type has been assigned to the field.
373    pub enum TypeAssigned {}
374}
375
376/// Construct a valid [`Field`].
377#[must_use]
378pub struct FieldBuilder<
379    F: Form = MetaForm,
380    N = field_state::NameNotAssigned,
381    T = field_state::TypeNotAssigned,
382> {
383    name: Option<F::String>,
384    ty: Option<F::Type>,
385    type_name: Option<F::String>,
386    docs: Vec<F::String>,
387    marker: PhantomData<fn() -> (N, T)>,
388}
389
390impl<F: Form, N, T> Default for FieldBuilder<F, N, T> {
391    fn default() -> Self {
392        FieldBuilder {
393            name: Default::default(),
394            ty: Default::default(),
395            type_name: Default::default(),
396            docs: Default::default(),
397            marker: Default::default(),
398        }
399    }
400}
401
402impl<F: Form> FieldBuilder<F> {
403    /// Create a new FieldBuilder.
404    pub fn new() -> Self {
405        Default::default()
406    }
407}
408
409impl<F: Form, T> FieldBuilder<F, field_state::NameNotAssigned, T> {
410    /// Initialize the field name.
411    pub fn name(self, name: F::String) -> FieldBuilder<F, field_state::NameAssigned, T> {
412        FieldBuilder {
413            name: Some(name),
414            ty: self.ty,
415            type_name: self.type_name,
416            docs: self.docs,
417            marker: PhantomData,
418        }
419    }
420}
421
422impl<N> FieldBuilder<MetaForm, N, field_state::TypeNotAssigned> {
423    /// Initialize the type of the field.
424    pub fn ty<TY>(self) -> FieldBuilder<MetaForm, N, field_state::TypeAssigned>
425    where
426        TY: TypeInfo + 'static + ?Sized,
427    {
428        FieldBuilder {
429            name: self.name,
430            ty: Some(MetaType::new::<TY>()),
431            type_name: self.type_name,
432            docs: self.docs,
433            marker: PhantomData,
434        }
435    }
436
437    /// Initializes the type of the field as a compact type.
438    pub fn compact<TY>(self) -> FieldBuilder<MetaForm, N, field_state::TypeAssigned>
439    where
440        TY: scale::HasCompact + TypeInfo + 'static,
441    {
442        FieldBuilder {
443            name: self.name,
444            ty: Some(MetaType::new::<scale::Compact<TY>>()),
445            type_name: self.type_name,
446            docs: self.docs,
447            marker: PhantomData,
448        }
449    }
450}
451
452impl<N> FieldBuilder<PortableForm, N, field_state::TypeNotAssigned> {
453    /// Initialize the type of the field.
454    pub fn ty<T>(self, ty: T) -> FieldBuilder<PortableForm, N, field_state::TypeAssigned>
455    where
456        T: Into<<PortableForm as Form>::Type>,
457    {
458        FieldBuilder {
459            name: self.name,
460            ty: Some(ty.into()),
461            type_name: self.type_name,
462            docs: self.docs,
463            marker: PhantomData,
464        }
465    }
466}
467
468impl<F: Form, N, T> FieldBuilder<F, N, T> {
469    /// Initialize the type name of a field (optional).
470    pub fn type_name(self, type_name: F::String) -> FieldBuilder<F, N, T> {
471        FieldBuilder {
472            name: self.name,
473            ty: self.ty,
474            type_name: Some(type_name),
475            docs: self.docs,
476            marker: PhantomData,
477        }
478    }
479}
480
481impl<N, T> FieldBuilder<PortableForm, N, T> {
482    #[cfg(feature = "docs")]
483    /// Initialize the documentation of a field (for types in portable form, optional).
484    pub fn docs_portable<I>(mut self, docs: I) -> Self
485    where
486        I: IntoIterator<Item = <PortableForm as Form>::String>,
487    {
488        self.docs = docs.into_iter().collect();
489        self
490    }
491}
492
493impl<N, T> FieldBuilder<MetaForm, N, T> {
494    #[cfg(feature = "docs")]
495    /// Initialize the documentation of a field (optional).
496    pub fn docs(self, docs: &'static [&'static str]) -> Self {
497        FieldBuilder {
498            name: self.name,
499            ty: self.ty,
500            type_name: self.type_name,
501            docs: docs.to_vec(),
502            marker: PhantomData,
503        }
504    }
505
506    #[cfg(not(feature = "docs"))]
507    #[inline]
508    /// Doc capture is not enabled via the "docs" feature so this is a no-op.
509    pub fn docs(self, _docs: &'static [&'static str]) -> Self {
510        self
511    }
512
513    /// Initialize the documentation of a field, always captured even if the "docs" feature is not
514    /// enabled.
515    pub fn docs_always(self, docs: &'static [&'static str]) -> Self {
516        FieldBuilder {
517            name: self.name,
518            ty: self.ty,
519            type_name: self.type_name,
520            docs: docs.to_vec(),
521            marker: PhantomData,
522        }
523    }
524}
525
526impl<F: Form, N> FieldBuilder<F, N, field_state::TypeAssigned> {
527    /// Complete building and return a new [`Field`].
528    pub fn finalize(self) -> Field<F> {
529        Field::new(
530            self.name,
531            self.ty.expect("Type should be set by builder"),
532            self.type_name,
533            self.docs,
534        )
535    }
536}
537
538/// Builds a definition of a variant type i.e an `enum`
539#[derive(Default)]
540#[must_use]
541pub struct Variants<F: Form = MetaForm> {
542    variants: Vec<Variant<F>>,
543}
544
545impl<F: Form> Variants<F> {
546    /// Create a new [`VariantsBuilder`].
547    pub fn new() -> Self {
548        Self {
549            variants: Vec::new(),
550        }
551    }
552
553    /// Add a variant
554    pub fn variant<B>(mut self, name: F::String, builder: B) -> Self
555    where
556        B: Fn(VariantBuilder<F>) -> VariantBuilder<F, variant_state::IndexAssigned>,
557    {
558        let builder = builder(VariantBuilder::new(name));
559        self.variants.push(builder.finalize());
560        self
561    }
562
563    /// Add a unit variant (without fields).
564    pub fn variant_unit(mut self, name: F::String, index: u8) -> Self {
565        let builder = VariantBuilder::new(name).index(index);
566        self.variants.push(builder.finalize());
567        self
568    }
569
570    /// Construct a new [`TypeDefVariant`] from the initialized builder variants.
571    pub fn finalize(self) -> TypeDefVariant<F> {
572        TypeDefVariant::new(self.variants)
573    }
574}
575
576/// State types for the `VariantBuilder` which requires an index.
577pub mod variant_state {
578    /// State where the builder has not assigned an index to a variant.
579    pub enum IndexNotAssigned {}
580    /// State where the builder has assigned an index to a variant.
581    pub enum IndexAssigned {}
582}
583
584/// Build a [`Variant`].
585#[must_use]
586pub struct VariantBuilder<F: Form, S = variant_state::IndexNotAssigned> {
587    name: F::String,
588    index: Option<u8>,
589    fields: Vec<Field<F>>,
590    discriminant: Option<u64>,
591    docs: Vec<F::String>,
592    marker: PhantomData<S>,
593}
594
595impl<F: Form> VariantBuilder<F, variant_state::IndexNotAssigned> {
596    /// Create a new [`VariantBuilder`].
597    pub fn new(name: F::String) -> Self {
598        Self {
599            name,
600            fields: Vec::new(),
601            discriminant: None,
602            index: None,
603            docs: Vec::new(),
604            marker: Default::default(),
605        }
606    }
607
608    /// Set the variant's codec index.
609    pub fn index(self, index: u8) -> VariantBuilder<F, variant_state::IndexAssigned> {
610        VariantBuilder {
611            name: self.name,
612            index: Some(index),
613            fields: self.fields,
614            discriminant: self.discriminant,
615            docs: self.docs,
616            marker: Default::default(),
617        }
618    }
619}
620
621impl<F: Form, S> VariantBuilder<F, S> {
622    /// Set the variant's discriminant.
623    pub fn discriminant(mut self, discriminant: u64) -> Self {
624        self.discriminant = Some(discriminant);
625        self
626    }
627
628    /// Initialize the variant's fields.
629    pub fn fields<T>(mut self, fields_builder: FieldsBuilder<F, T>) -> Self {
630        self.fields = fields_builder.finalize();
631        self
632    }
633}
634
635impl<S> VariantBuilder<PortableForm, S> {
636    #[cfg(feature = "docs")]
637    /// Initialize the variant's documentation (for types in portable form).
638    pub fn docs_portable<I>(mut self, docs: I) -> Self
639    where
640        I: IntoIterator<Item = <PortableForm as Form>::String>,
641    {
642        self.docs = docs.into_iter().collect();
643        self
644    }
645}
646
647impl<S> VariantBuilder<MetaForm, S> {
648    #[cfg(feature = "docs")]
649    /// Initialize the variant's documentation.
650    pub fn docs(mut self, docs: &[&'static str]) -> Self {
651        self.docs = docs.to_vec();
652        self
653    }
654
655    #[cfg(not(feature = "docs"))]
656    #[inline]
657    /// Doc capture is not enabled via the "docs" feature so this is a no-op.
658    pub fn docs(self, _docs: &[&'static str]) -> Self {
659        self
660    }
661
662    /// Initialize the variant's documentation, always captured even if the "docs" feature is not
663    /// enabled.
664    pub fn docs_always(mut self, docs: &[&'static str]) -> Self {
665        self.docs = docs.to_vec();
666        self
667    }
668}
669
670impl<F: Form> VariantBuilder<F, variant_state::IndexAssigned> {
671    /// Complete building and create final [`Variant`] instance.
672    pub fn finalize(self) -> Variant<F> {
673        Variant::new(
674            self.name,
675            self.fields,
676            self.index.expect("Index should be assigned by the builder"),
677            self.docs,
678        )
679    }
680}