serde_scale_wrap/
lib.rs

1// Copyright (C) 2020 Stephane Raux. Distributed under the zlib license.
2
3//! # Overview
4//! - [📦 crates.io](https://crates.io/crates/serde-scale-wrap)
5//! - [📖 Documentation](https://docs.rs/serde-scale-wrap)
6//! - [âš– zlib license](https://opensource.org/licenses/Zlib)
7//!
8//! Wrapper for types implementing [`Serialize`/`Deserialize`](https://docs.rs/serde) to implement
9//! [`Encode`/`Decode`](https://docs.rs/parity-scale-codec) automatically.
10//!
11//! # Example
12//! ```rust
13//! extern crate alloc;
14//!
15//! use alloc::string::String;
16//! use parity_scale_codec::{Decode, Encode};
17//! use serde::{Deserialize, Serialize};
18//! use serde_scale_wrap::Wrap;
19//!
20//! #[derive(Debug, Deserialize, PartialEq, Serialize)]
21//! struct Foo {
22//!     x: i32,
23//!     s: String,
24//! }
25//!
26//! let original = Foo { x: 3, s: "foo".into() };
27//! let serialized = Wrap(&original).encode();
28//! let Wrap(deserialized) = Wrap::<Foo>::decode(&mut &*serialized).unwrap();
29//! assert_eq!(original, deserialized);
30//! ```
31//!
32//! # Conformance
33//! âš  `Option<bool>` is serialized as a single byte according to the SCALE encoding, which differs
34//! from the result of `Encode::encode` -- `Encode` expects `OptionBool` to be used instead.
35//!
36//! # Features
37//! `no_std` is supported by disabling default features.
38//!
39//! - `std`: Support for `std`. It is enabled by default.
40//!
41//! 🔖 Features enabled in build dependencies and proc-macros are also enabled for normal
42//! dependencies, which may cause `serde` to have its `std` feature on when it is not desired.
43//! Nightly cargo prevents this from happening with
44//! [`-Z features=host_dep`](https://github.com/rust-lang/cargo/issues/7915#issuecomment-683294870)
45//! or the following in `.cargo/config`:
46//!
47//! ```toml
48//! [unstable]
49//! features = ["host_dep"]
50//! ```
51//!
52//! For example, this issue arises when depending on `parity-scale-codec-derive`.
53//!
54//! # Contribute
55//! All contributions shall be licensed under the [zlib license](https://opensource.org/licenses/Zlib).
56//!
57//! # Related projects
58//! - [parity-scale-codec](https://crates.io/crates/parity-scale-codec): Reference Rust implementation
59//! - [serde-scale](https://crates.io/crates/serde-scale): SCALE encoding with `serde`
60
61#![deny(warnings)]
62#![cfg_attr(not(feature = "std"), no_std)]
63
64extern crate alloc;
65
66use alloc::vec::Vec;
67use core::convert::Infallible;
68use parity_scale_codec::{Decode, Encode, EncodeLike, Error, Input, Output};
69use serde::{Deserialize, Serialize};
70use serde_scale::{Bytes, Read, Write};
71
72/// Wrapper for types serializable with `serde` to support serialization with `Encode`/`Decode`
73///
74/// This can help to pass instances of types implementing `Serialize`/`Deserialize` to `substrate`
75/// functions expecting types implementing `Encode`/`Decode`.
76///
77/// âš  The `Encode` implementation panics if the serializer returns an error (e.g. when attempting
78/// to serialize a floating point number) because `Encode` methods do not return `Result`.
79#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
80pub struct Wrap<T>(pub T);
81
82impl<T: Serialize> Encode for Wrap<T> {
83    /// # Panics
84    /// Panics if the serializer returns an error (e.g. when attempting to serialize a floating
85    /// point number).
86    fn encode_to<O: Output + ?Sized>(&self, dst: &mut O) {
87        let mut serializer = serde_scale::Serializer::new(OutputToWrite(dst));
88        self.0.serialize(&mut serializer).unwrap();
89    }
90}
91
92impl<T: Serialize> EncodeLike for Wrap<T> {}
93
94impl<'de, T: Deserialize<'de>> Decode for Wrap<T> {
95    fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
96        let mut deserializer = serde_scale::Deserializer::new(InputToRead::new(input));
97        match T::deserialize(&mut deserializer) {
98            Ok(x) => Ok(Wrap(x)),
99            Err(serde_scale::Error::Io(e)) => Err(e),
100            Err(_) => Err("Deserialization failed".into()),
101        }
102    }
103}
104
105struct OutputToWrite<'a, O: ?Sized>(&'a mut O);
106
107impl<O: Output + ?Sized> Write for OutputToWrite<'_, O> {
108    type Error = Infallible;
109
110    fn write(&mut self, bytes: &[u8]) -> Result<(), Infallible> {
111        self.0.write(bytes);
112        Ok(())
113    }
114}
115
116struct InputToRead<'a, I: ?Sized> {
117    input: &'a mut I,
118    buffer: Vec<u8>,
119}
120
121impl<'a, I: Input + ?Sized> InputToRead<'a, I> {
122    fn new(input: &'a mut I) -> Self {
123        InputToRead {
124            input,
125            buffer: Vec::new(),
126        }
127    }
128}
129
130impl<'a, 'de, I: Input + ?Sized> Read<'de> for InputToRead<'a, I> {
131    type Error = Error;
132
133    fn read_map<R, F>(&mut self, n: usize, f: F) -> Result<R, Self::Error>
134    where
135        F: FnOnce(Bytes<'de, '_>) -> R,
136    {
137        self.buffer.resize(n, 0);
138        self.input.read(&mut self.buffer)?;
139        Ok(f(Bytes::Temporary(&self.buffer)))
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use alloc::string::String;
146    use crate::Wrap;
147    use parity_scale_codec::{Decode, Encode};
148    use serde::{Deserialize, Serialize};
149
150    #[derive(Debug, Deserialize, PartialEq, Serialize)]
151    struct Foo {
152        x: i32,
153        s: String,
154    }
155
156    #[test]
157    fn foo_roundtrips() {
158        let original = Foo { x: 3, s: "foo".into() };
159        let serialized = Wrap(&original).encode();
160        let Wrap(deserialized) = Wrap::<Foo>::decode(&mut &*serialized).unwrap();
161        assert_eq!(original, deserialized);
162    }
163
164    #[test]
165    fn foo_is_correctly_serialized() {
166        let original = Foo { x: 3, s: "foo".into() };
167        let wrapped_serialized = Wrap(&original).encode();
168        let serialized = serde_scale::to_vec(&original).unwrap();
169        assert_eq!(wrapped_serialized, serialized);
170    }
171}