Skip to content

Commit

Permalink
Optional serde feature (#34)
Browse files Browse the repository at this point in the history
* Serde optional and CompactForm<String>

* Make serde its own feature

* Remove CompactForm String param, make this a separate PR

* Use serde feature from tests

* Revert From<Registry> impl

* Fmt

* Put derive attributes all in the place above type

* Add missing ToString import

* Fmt

* Update examples in README

* Update README.md

* Missing full stop

* More missing full stops
  • Loading branch information
ascjones authored Dec 8, 2020
1 parent afba705 commit bb031f6
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 152 deletions.
5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@ include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"]
[dependencies]
cfg-if = "1.0"
scale-info-derive = { version = "0.2.1", path = "derive", default-features = false, optional = true }
serde = { version = "1", default-features = false, features = ["derive", "alloc"] }
serde = { version = "1", default-features = false, optional = true, features = ["derive", "alloc"] }
derive_more = { version = "0.99.1", default-features = false, features = ["from"] }
scale = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive"] }

[features]
default = ["std"]
std = [
"serde/std",
"scale/std"
"scale/std",
]
derive = [
"scale-info-derive"
Expand Down
36 changes: 25 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,15 @@ impl<T> TypeInfo for Foo<T>
where
T: TypeInfo + 'static,
{
type Identity = Self;

fn type_info() -> Type {
Type::builder()
.path(Path::new("Foo", module_path!()))
.type_params(vec![MetaType::new::<T>()])
.composite(Fields::named()
.field_of::<T>("bar")
.field_of::<u64>("data")
.field_of::<T>("bar", "T")
.field_of::<u64>("data", "u64")
)
}
}
Expand All @@ -117,12 +119,14 @@ where
struct Foo(u32, bool);

impl TypeInfo for Foo {
type Identity = Self;

fn type_info() -> Type {
Type::builder()
.path(Path::new("Foo", module_path!()))
.composite(Fields::unnamed()
.field_of::<u32>()
.field_of::<bool>()
.field_of::<u32>("u32")
.field_of::<bool>("bool")
)
}
}
Expand All @@ -144,14 +148,16 @@ impl<T> TypeInfo for Foo<T>
where
T: TypeInfo + 'static,
{
type Identity = Self;

fn type_info() -> Type {
Type::builder()
.path(Path::new("Foo", module_path!()))
.type_params(vec![MetaType::new::<T>()])
.variant(
Variants::with_fields()
.variant("A", Fields::unnamed().field_of::<T>())
.variant("B", Fields::named().field_of::<u32>("f"))
.variant("A", Fields::unnamed().field_of::<T>("T"))
.variant("B", Fields::named().field_of::<u32>("f", "u32"))
.variant("C", Fields::unit())
)
}
Expand All @@ -169,6 +175,8 @@ enum Foo {
}

impl TypeInfo for Foo {
type Identity = Self;

fn type_info() -> Type {
Type::builder()
.path(Path::new("Foo", module_path!()))
Expand Down Expand Up @@ -201,13 +209,19 @@ After compactification all type definitions are stored in the type registry.
Note that the type registry should be serialized as part of the metadata structure where the
registered types are utilized to allow consumers to resolve the types.

## Serialization
## Encoding

The type registry can be encoded as:

- JSON (with the "serde" feature enabled).
- SCALE itself (using `parity-scale-codec`).

## Features

Currently the only supported serialization format is JSON, an example of which can be found
[here](https://github.com/paritytech/scale-info/blob/master/test_suite/tests/json.rs).
The following optional `cargo` features are available:

Future support for binary formats is planned, either SCALE itself or a more compressed format where
the monomorphization of Rust generic types could potentially result in very large files.
- **serde** includes support for json serialization/deserialization of the type registry. See example [here](https://github.com/paritytech/scale-info/blob/master/test_suite/tests/json.rs).
- **derive** reexports the [`scale-info-derive`](https://crates.io/crates/scale-info-derive) crate.

## Resources

Expand Down
10 changes: 7 additions & 3 deletions src/form.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ use crate::{
interner::UntrackedSymbol,
meta_type::MetaType,
};

#[cfg(feature = "serde")]
use serde::Serialize;

/// Trait to control the internal structures of type definitions.
Expand All @@ -51,14 +53,15 @@ pub trait Form {
/// The type representing the type.
type Type: PartialEq + Eq + PartialOrd + Ord + Clone + Debug;
/// The string type.
type String: Serialize + PartialEq + Eq + PartialOrd + Ord + Clone + Debug;
type String: PartialEq + Eq + PartialOrd + Ord + Clone + Debug;
}

/// A meta meta-type.
///
/// Allows to be converted into other forms such as compact form
/// through the registry and `IntoCompact`.
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Serialize, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
pub enum MetaForm {}

impl Form for MetaForm {
Expand All @@ -75,7 +78,8 @@ impl Form for MetaForm {
/// underlying data.
///
/// `type String` is owned in order to enable decoding
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Serialize, Debug)]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum CompactForm {}

impl Form for CompactForm {
Expand Down
22 changes: 13 additions & 9 deletions src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use crate::prelude::{
vec::Vec,
};

#[cfg(feature = "serde")]
use serde::{
Deserialize,
Serialize,
Expand All @@ -40,12 +41,13 @@ use serde::{
///
/// This can be used by self-referential types but
/// can no longer be used to resolve instances.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct UntrackedSymbol<T> {
/// The index to the symbol in the interner table.
id: NonZeroU32,
#[serde(skip)]
#[cfg_attr(feature = "serde", serde(skip))]
marker: PhantomData<fn() -> T>,
}

Expand Down Expand Up @@ -79,11 +81,12 @@ impl<T> UntrackedSymbol<T> {
/// A symbol from an interner.
///
/// Can be used to resolve to the associated instance.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
#[serde(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct Symbol<'a, T> {
id: NonZeroU32,
#[serde(skip)]
#[cfg_attr(feature = "serde", serde(skip))]
marker: PhantomData<fn() -> &'a T>,
}

Expand Down Expand Up @@ -120,15 +123,16 @@ impl<T> Symbol<'_, T> {
///
/// This is used in order to quite efficiently cache strings and type
/// definitions uniquely identified by their associated type identifiers.
#[derive(Debug, PartialEq, Eq, Serialize)]
#[serde(transparent)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct Interner<T> {
/// A mapping from the interned elements to their respective compact
/// identifiers.
///
/// The idenfitiers can be used to retrieve information about the original
/// element from the interner.
#[serde(skip)]
#[cfg_attr(feature = "serde", serde(skip))]
map: BTreeMap<T, usize>,
/// The ordered sequence of cached elements.
///
Expand Down
12 changes: 8 additions & 4 deletions src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ use scale::{
Decode,
Encode,
};
#[cfg(feature = "serde")]
use serde::{
Deserialize,
Serialize,
Expand Down Expand Up @@ -83,23 +84,25 @@ impl IntoCompact for &'static str {
///
/// A type can be a sub-type of itself. In this case the registry has a builtin
/// mechanism to stop recursion before going into an infinite loop.
#[derive(Debug, PartialEq, Eq, Serialize)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Registry {
/// The cache for already registered types.
///
/// This is just an accessor to the actual database
/// for all types found in the `types` field.
#[serde(skip)]
#[cfg_attr(feature = "serde", serde(skip))]
type_table: Interner<TypeId>,
/// The database where registered types actually reside.
///
/// This is going to be serialized upon serlialization.
#[serde(serialize_with = "serialize_registry_types")]
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_registry_types"))]
types: BTreeMap<UntrackedSymbol<core::any::TypeId>, Type<CompactForm>>,
}

/// Serializes the types of the registry by removing their unique IDs
/// and instead serialize them in order of their removed unique ID.
#[cfg(feature = "serde")]
fn serialize_registry_types<S>(
types: &BTreeMap<UntrackedSymbol<core::any::TypeId>, Type<CompactForm>>,
serializer: S,
Expand Down Expand Up @@ -200,7 +203,8 @@ impl Registry {
}

/// A read-only registry, to be used for decoding/deserializing
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Decode)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, Decode)]
pub struct RegistryReadOnly {
types: Vec<Type<CompactForm>>,
}
Expand Down
32 changes: 14 additions & 18 deletions src/ty/composite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use scale::{
Decode,
Encode,
};
#[cfg(feature = "serde")]
use serde::{
de::DeserializeOwned,
Deserialize,
Expand Down Expand Up @@ -61,27 +62,22 @@ use serde::{
/// ```
/// struct JustAMarker;
/// ```
#[derive(
PartialEq,
Eq,
PartialOrd,
Ord,
Clone,
Debug,
From,
Serialize,
Deserialize,
Encode,
Decode,
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde",
serde(bound(
serialize = "T::Type: Serialize, T::String: Serialize",
deserialize = "T::Type: DeserializeOwned, T::String: DeserializeOwned",
))
)]
#[serde(bound(
serialize = "T::Type: Serialize, T::String: Serialize",
deserialize = "T::Type: DeserializeOwned, T::String: DeserializeOwned"
))]
#[serde(rename_all = "lowercase")]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug, From, Encode, Decode)]
pub struct TypeDefComposite<T: Form = MetaForm> {
/// The fields of the composite type.
#[serde(skip_serializing_if = "Vec::is_empty", default)]
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "Vec::is_empty", default)
)]
fields: Vec<Field<T>>,
}

Expand Down
24 changes: 15 additions & 9 deletions src/ty/fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use scale::{
Decode,
Encode,
};
#[cfg(feature = "serde")]
use serde::{
de::DeserializeOwned,
Deserialize,
Expand Down Expand Up @@ -61,20 +62,25 @@ use serde::{
/// This is intended for informational and diagnostic purposes only. Although it
/// is possible to infer certain properties e.g. whether a type name is a type alias,
/// there are no guarantees provided, and the type name representation may change.
#[derive(
PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Serialize, Deserialize, Encode, Decode,
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde",
serde(bound(
serialize = "T::Type: Serialize, T::String: Serialize",
deserialize = "T::Type: DeserializeOwned, T::String: DeserializeOwned",
))
)]
#[serde(bound(
serialize = "T::Type: Serialize, T::String: Serialize",
deserialize = "T::Type: DeserializeOwned, T::String: DeserializeOwned"
))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug, Encode, Decode)]
pub struct Field<T: Form = MetaForm> {
/// The name of the field. None for unnamed fields.
#[serde(skip_serializing_if = "Option::is_none", default)]
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "Option::is_none", default)
)]
name: Option<T::String>,
/// The type of the field.
#[serde(rename = "type")]
#[cfg_attr(feature = "serde", serde(rename = "type"))]
ty: T::Type,
/// The name of the type of the field as it appears in the source code.
type_name: T::String,
Expand Down
Loading

0 comments on commit bb031f6

Please sign in to comment.