Skip to content

Commit

Permalink
Implement a version of Schema types that are owned, using the heap, a…
Browse files Browse the repository at this point in the history
…nd can be

punned or converted with existing typed or serialized schema types
  • Loading branch information
jamesmunns committed Sep 6, 2024
1 parent f878536 commit 0788f30
Show file tree
Hide file tree
Showing 3 changed files with 250 additions and 7 deletions.
5 changes: 5 additions & 0 deletions source/postcard/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ pub mod experimental {
pub use crate::schema::{NamedType, NamedValue, NamedVariant, Schema, SdmTy, Varint};
// NOTE: ...and this is the derive macro
pub use postcard_derive::Schema;

#[cfg(any(feature = "use-std", feature = "alloc"))]
pub use crate::schema::owned::{
OwnedNamedType, OwnedNamedValue, OwnedNamedVariant, OwnedSdmTy,
};
}
}

Expand Down
198 changes: 192 additions & 6 deletions source/postcard/src/schema.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use serde::Serialize;
use serde::{Deserialize, Serialize};

/// A schema type representing a variably encoded integer
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Varint {
/// A variably encoded i16
I16,
Expand All @@ -26,7 +26,7 @@ pub enum Varint {
}

/// Serde Data Model Types (and friends)
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub enum SdmTy {
/// The `bool` Serde Data Model Type
Bool,
Expand Down Expand Up @@ -104,7 +104,7 @@ pub enum SdmTy {
}

/// A data type with a name - e.g. a field of a Struct
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub struct NamedValue {
/// The name of this value
pub name: &'static str,
Expand All @@ -113,7 +113,7 @@ pub struct NamedValue {
}

/// A data type - e.g. a custom `struct Foo{ ... }` type
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub struct NamedType {
/// The name of this type
pub name: &'static str,
Expand All @@ -122,7 +122,7 @@ pub struct NamedType {
}

/// An enum variant with a name, e.g. `T::Bar(...)`
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub struct NamedVariant {
/// The name of this variant
pub name: &'static str,
Expand Down Expand Up @@ -278,3 +278,189 @@ impl Schema for alloc::string::String {
ty: &SdmTy::String,
};
}

#[cfg(any(feature = "use-std", feature = "alloc"))]
pub(crate) mod owned {
use super::*;

#[cfg(feature = "use-std")]
use std::{boxed::Box, string::String, vec::Vec};

#[cfg(all(not(feature = "use-std"), feature = "alloc"))]
use alloc::{
boxed::Box,
string::{String, ToString},
vec::Vec,
};

/// Serde Data Model Types (and friends)
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum OwnedSdmTy {
/// The `bool` Serde Data Model Type
Bool,

/// The `i8` Serde Data Model Type
I8,

/// The `u8` Serde Data Model Type
U8,

/// The Serde Data Model Type for variably length encoded integers
Varint(Varint),

/// The `f32` Serde Data Model Type
F32,

/// The `f64 Serde Data Model Type
F64,

/// The `char` Serde Data Model Type
Char,

/// The `String` Serde Data Model Type
String,

/// The `[u8; N]` Serde Data Model Type
ByteArray,

/// The `Option<T>` Serde Data Model Type
Option(Box<OwnedNamedType>),

/// The `()` Serde Data Model Type
Unit,

/// The "unit struct" Serde Data Model Type
UnitStruct,

/// The "unit variant" Serde Data Model Type
UnitVariant,

/// The "newtype struct" Serde Data Model Type
NewtypeStruct(Box<OwnedNamedType>),

/// The "newtype variant" Serde Data Model Type
NewtypeVariant(Box<OwnedNamedType>),

/// The "Sequence" Serde Data Model Type
Seq(Box<OwnedNamedType>),

/// The "Tuple" Serde Data Model Type
Tuple(Vec<OwnedNamedType>),

/// The "Tuple Struct" Serde Data Model Type
TupleStruct(Vec<OwnedNamedType>),

/// The "Tuple Variant" Serde Data Model Type
TupleVariant(Vec<OwnedNamedType>),

/// The "Map" Serde Data Model Type
Map {
/// The map "Key" type
key: Box<OwnedNamedType>,
/// The map "Value" type
val: Box<OwnedNamedType>,
},

/// The "Struct" Serde Data Model Type
Struct(Vec<OwnedNamedValue>),

/// The "Struct Variant" Serde Data Model Type
StructVariant(Vec<OwnedNamedValue>),

/// The "Enum" Serde Data Model Type (which contains any of the "Variant" types)
Enum(Vec<OwnedNamedVariant>),
}

impl From<&SdmTy> for OwnedSdmTy {
fn from(other: &SdmTy) -> Self {
match other {
SdmTy::Bool => Self::Bool,
SdmTy::I8 => Self::I8,
SdmTy::U8 => Self::U8,
SdmTy::Varint(v) => Self::Varint(*v),
SdmTy::F32 => Self::F32,
SdmTy::F64 => Self::F64,
SdmTy::Char => Self::Char,
SdmTy::String => Self::String,
SdmTy::ByteArray => Self::ByteArray,
SdmTy::Option(o) => Self::Option(Box::new((*o).into())),
SdmTy::Unit => Self::Unit,
SdmTy::UnitStruct => Self::UnitStruct,
SdmTy::UnitVariant => Self::UnitVariant,
SdmTy::NewtypeStruct(nts) => Self::NewtypeStruct(Box::new((*nts).into())),
SdmTy::NewtypeVariant(ntv) => Self::NewtypeVariant(Box::new((*ntv).into())),
SdmTy::Seq(s) => Self::Seq(Box::new((*s).into())),
SdmTy::Tuple(t) => Self::Tuple(t.iter().map(|i| (*i).into()).collect()),
SdmTy::TupleStruct(ts) => {
Self::TupleStruct(ts.iter().map(|i| (*i).into()).collect())
}
SdmTy::TupleVariant(tv) => {
Self::TupleVariant(tv.iter().map(|i| (*i).into()).collect())
}
SdmTy::Map { key, val } => Self::Map {
key: Box::new((*key).into()),
val: Box::new((*val).into()),
},
SdmTy::Struct(s) => Self::Struct(s.iter().map(|i| (*i).into()).collect()),
SdmTy::StructVariant(sv) => {
Self::StructVariant(sv.iter().map(|i| (*i).into()).collect())
}
SdmTy::Enum(e) => Self::Enum(e.iter().map(|i| (*i).into()).collect()),
}
}
}

/// A data type with a name - e.g. a field of a Struct
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct OwnedNamedValue {
/// The name of this value
pub name: String,
/// The type of this value
pub ty: OwnedNamedType,
}

impl From<&NamedValue> for OwnedNamedValue {
fn from(value: &NamedValue) -> Self {
Self {
name: value.name.to_string(),
ty: value.ty.into(),
}
}
}

/// A data type - e.g. a custom `struct Foo{ ... }` type
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct OwnedNamedType {
/// The name of this type
pub name: String,
/// The type
pub ty: OwnedSdmTy,
}

impl From<&NamedType> for OwnedNamedType {
fn from(value: &NamedType) -> Self {
Self {
name: value.name.to_string(),
ty: value.ty.into(),
}
}
}

/// An enum variant with a name, e.g. `T::Bar(...)`
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct OwnedNamedVariant {
/// The name of this variant
pub name: String,
/// The type of this variant
pub ty: OwnedSdmTy,
}

impl From<&NamedVariant> for OwnedNamedVariant {
fn from(value: &NamedVariant) -> Self {
Self {
name: value.name.to_string(),
ty: value.ty.into(),
}
}
}
}
54 changes: 53 additions & 1 deletion source/postcard/tests/schema.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#![cfg(feature = "experimental-derive")]

use postcard::experimental::schema::{NamedType, NamedValue, NamedVariant, Schema, SdmTy, Varint};
use postcard::experimental::schema::{
NamedType, NamedValue, NamedVariant, OwnedNamedType, Schema, SdmTy, Varint,
};

const U8_SCHEMA: NamedType = NamedType {
name: "u8",
Expand Down Expand Up @@ -161,3 +163,53 @@ fn test_slice_serialize() {
Slice::SCHEMA
);
}

#[allow(unused)]
#[derive(Debug, Schema)]
enum TestEnum<'a> {
Alpha,
Beta(u32),
Gamma { a: bool, b: &'a [u8] },
Delta(f32, Option<&'a str>),
Epsilon(TestStruct1),
}

#[allow(unused)]
#[derive(Debug, Schema)]
struct TestStruct1 {
a: i8,
b: i16,
c: i32,
d: i64,
}

#[allow(unused)]
#[derive(Debug, Schema)]
struct TestStruct2<'a> {
x: TestEnum<'a>,
y: TestStruct1,
z: Result<TestStruct1, u32>,
}

#[test]
fn owned_punning() {
let borrowed_schema = TestStruct2::SCHEMA;
let owned_schema: OwnedNamedType = borrowed_schema.into();

// Check that they are the same on the wire when serialized
let ser_borrowed_schema = postcard::to_stdvec(borrowed_schema).unwrap();
let ser_owned_schema = postcard::to_stdvec(&owned_schema).unwrap();
assert_eq!(ser_borrowed_schema, ser_owned_schema);

// TODO: This is wildly repetitive, and likely could benefit from interning of
// repeated types, strings, etc.
assert_eq!(ser_borrowed_schema.len(), 282);

// Check that we round-trip correctly
let deser_borrowed_schema =
postcard::from_bytes::<OwnedNamedType>(&ser_borrowed_schema).unwrap();
let deser_owned_schema = postcard::from_bytes::<OwnedNamedType>(&ser_owned_schema).unwrap();
assert_eq!(deser_borrowed_schema, deser_owned_schema);
assert_eq!(deser_borrowed_schema, owned_schema);
assert_eq!(deser_owned_schema, owned_schema);
}

0 comments on commit 0788f30

Please sign in to comment.