diff --git a/source/postcard/src/lib.rs b/source/postcard/src/lib.rs index 9fafa00..fb252b3 100644 --- a/source/postcard/src/lib.rs +++ b/source/postcard/src/lib.rs @@ -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, + }; } } diff --git a/source/postcard/src/schema.rs b/source/postcard/src/schema.rs index 4a17cc1..8203f13 100644 --- a/source/postcard/src/schema.rs +++ b/source/postcard/src/schema.rs @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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` Serde Data Model Type + Option(Box), + + /// 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), + + /// The "newtype variant" Serde Data Model Type + NewtypeVariant(Box), + + /// The "Sequence" Serde Data Model Type + Seq(Box), + + /// The "Tuple" Serde Data Model Type + Tuple(Vec), + + /// The "Tuple Struct" Serde Data Model Type + TupleStruct(Vec), + + /// The "Tuple Variant" Serde Data Model Type + TupleVariant(Vec), + + /// The "Map" Serde Data Model Type + Map { + /// The map "Key" type + key: Box, + /// The map "Value" type + val: Box, + }, + + /// The "Struct" Serde Data Model Type + Struct(Vec), + + /// The "Struct Variant" Serde Data Model Type + StructVariant(Vec), + + /// The "Enum" Serde Data Model Type (which contains any of the "Variant" types) + Enum(Vec), + } + + 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(), + } + } + } +} diff --git a/source/postcard/tests/schema.rs b/source/postcard/tests/schema.rs index 244560a..969da7b 100644 --- a/source/postcard/tests/schema.rs +++ b/source/postcard/tests/schema.rs @@ -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", @@ -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, +} + +#[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::(&ser_borrowed_schema).unwrap(); + let deser_owned_schema = postcard::from_bytes::(&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); +}