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}