Repository: jamesmunns/postcard (language: rust)

Issue:
Restructure schema type hierarchy
Currently, the schema type hierarchy has `NamedType` at the top (i.e., `const SCHEMA: NamedType`), which has some strange consequences:
1. Schemas like `NamedType { name: "foobar", ty: U8 }` can be defined that have no meaningful interpretation: if it were the schema for `u8`, it should have `name: "u8"`, and if it were the schema for some wrapper around a `u8`, it should be a `NewtypeStruct`/`NewtypeVariant`.
2. Types that are represented purely as seqs/maps/tuples in `serde`'s model need to have a name associated with them. This requires [workarounds like ignoring struct/enum names altogether](https://github.com/jamesmunns/postcard-rpc/blob/885fae41e3763f6b8f3538fc078f3cca21831bce/source/postcard-rpc/src/hash.rs#L193) in order to treat `Vec<T>`, `[T]`, and friends as equivalent types.

Reshaping the hierarchy to have `DataModelType` at the top, and shifting struct/enum names into the relevant variants, would ensure primitives have a single canonical schema and seq/map equivalence can be handled without sacrificing nominal typing of structs/enums. I imagine this would have roughly the same shape as [`serde_content::Value`](https://docs.rs/serde-content/latest/serde_content/enum.Value.html) (which may also be useful as a `Value` type for `postcard-dyn`).


Discussion / hints:
> 2\. Types that are represented purely as seqs/maps/tuples in `serde`'s model need to have a name associated with them. This requires [workarounds like ignoring struct/enum names altogether](https://github.com/jamesmunns/postcard-rpc/blob/885fae41e3763f6b8f3538fc078f3cca21831bce/source/postcard-rpc/src/hash.rs#L193) in order to treat `Vec<T>`, `[T]`, and friends as equivalent types.

A consequence of the proposed reshaping is that less standard types like `nalgebra` matrices would also be represented as just seqs with no name attached (that is how they implement `Serialize`) and would therefore be interchangeable with other seq types; I'm not sure if this is a feature or a limitation.
I'm generally interested in this! I'm going to make a separate issue with wishlists for the next breaking change as well, as this is likely to impact a bunch of pieces.

Fix patch:
diff --git a/source/postcard-derive/src/schema.rs b/source/postcard-derive/src/schema.rs
index 70f316a..b85674f 100644
--- a/source/postcard-derive/src/schema.rs
+++ b/source/postcard-derive/src/schema.rs
@@ -27,7 +27,7 @@ pub fn do_derive_schema(input: DeriveInput) -> syn::Result<TokenStream> {
     let postcard_schema = &generator.postcard_schema;
     let expanded = quote! {
         impl #impl_generics #postcard_schema::Schema for #name #ty_generics #where_clause {
-            const SCHEMA: &'static #postcard_schema::schema::NamedType = #ty;
+            const SCHEMA: &'static #postcard_schema::schema::DataModelType = #ty;
         }
     };
 
@@ -81,35 +81,34 @@ impl Generator {
         name: String,
     ) -> Result<TokenStream, syn::Error> {
         let postcard_schema = &self.postcard_schema;
-        let ty = match data {
-            Data::Struct(data) => self.generate_struct(&data.fields),
-            Data::Enum(data) => {
-                let name = data.variants.iter().map(|v| v.ident.to_string());
-                let ty = data
-                    .variants
-                    .iter()
-                    .map(|v| self.generate_variants(&v.fields));
-
-                quote! {
-                    &#postcard_schema::schema::DataModelType::Enum(&[
-                        #( &#postcard_schema::schema::NamedVariant { name: #name, ty: #ty } ),*
-                    ])
-                }
-            }
-            Data::Union(_) => {
-                return Err(syn::Error::new(
-                    span,
-                    "unions are not supported by `postcard::experimental::schema`",
-                ))
+        match data {
+            Data::Struct(data) => {
+                let data = self.generate_struct(&data.fields);
+                Ok(quote! {
+                    &#postcard_schema::schema::DataModelType::Struct{
+                        name: #name,
+                        data: #data,
+                    }
+                })
             }
-        };
-
-        Ok(quote! {
-            &#postcard_schema::schema::NamedType {
-                name: #name,
-                ty: #ty,
+            Data::Enum(data) => {
+                let variants = data.variants.iter().map(|v| {
+                    let (name, data) = (v.ident.to_string(), self.generate_variants(&v.fields));
+                    quote! { #postcard_schema::schema::Variant { name: #name, data: #data } }
+                });
+
+                Ok(quote! {
+                    &#postcard_schema::schema::DataModelType::Enum {
+                        name: #name,
+                        variants: &[#(&#variants),*],
+                    }
+                })
             }
-        })
+            Data::Union(_) => Err(syn::Error::new(
+                span,
+                "#[derive(Schema)] does not support unions",
+            )),
+        }
     }
 
     fn generate_struct(&self, fields: &Fields) -> TokenStream {
@@ -117,11 +116,11 @@ impl Generator {
         match fields {
             syn::Fields::Named(fields) => {
                 let fields = fields.named.iter().map(|f| {
-                let ty = &f.ty;
-                let name = f.ident.as_ref().unwrap().to_string();
-                quote_spanned!(f.span() => &#postcard_schema::schema::NamedValue { name: #name, ty: <#ty as #postcard_schema::Schema>::SCHEMA })
-            });
-                quote! { &#postcard_schema::schema::DataModelType::Struct(&[
+                    let ty = &f.ty;
+                    let name = f.ident.as_ref().unwrap().to_string();
+                    quote_spanned!(f.span() => &#postcard_schema::schema::NamedField { name: #name, ty: <#ty as #postcard_schema::Schema>::SCHEMA })
+                });
+                quote! { #postcard_schema::schema::Data::Struct(&[
                     #( #fields ),*
                 ]) }
             }
@@ -131,19 +130,19 @@ impl Generator {
                     let ty = &f.ty;
                     let qs = quote_spanned!(f.span() => <#ty as #postcard_schema::Schema>::SCHEMA);
 
-                    quote! { &#postcard_schema::schema::DataModelType::NewtypeStruct(#qs) }
+                    quote! { #postcard_schema::schema::Data::Newtype(#qs) }
                 } else {
                     let fields = fields.unnamed.iter().map(|f| {
                         let ty = &f.ty;
                         quote_spanned!(f.span() => <#ty as #postcard_schema::Schema>::SCHEMA)
                     });
-                    quote! { &#postcard_schema::schema::DataModelType::TupleStruct(&[
+                    quote! { #postcard_schema::schema::Data::Tuple(&[
                         #( #fields ),*
                     ]) }
                 }
             }
             syn::Fields::Unit => {
-                quote! { &#postcard_schema::schema::DataModelType::UnitStruct }
+                quote! { #postcard_schema::schema::Data::Unit }
             }
         }
     }
@@ -153,11 +152,11 @@ impl Generator {
         match fields {
             syn::Fields::Named(fields) => {
                 let fields = fields.named.iter().map(|f| {
-                let ty = &f.ty;
-                let name = f.ident.as_ref().unwrap().to_string();
-                quote_spanned!(f.span() => &#postcard_schema::schema::NamedValue { name: #name, ty: <#ty as #postcard_schema::Schema>::SCHEMA })
-            });
-                quote! { &#postcard_schema::schema::DataModelVariant::StructVariant(&[
+                    let ty = &f.ty;
+                    let name = f.ident.as_ref().unwrap().to_string();
+                    quote_spanned!(f.span() => &#postcard_schema::schema::NamedField { name: #name, ty: <#ty as #postcard_schema::Schema>::SCHEMA })
+                });
+                quote! { #postcard_schema::schema::Data::Struct(&[
                     #( #fields ),*
                 ]) }
             }
@@ -167,19 +166,19 @@ impl Generator {
                     let ty = &f.ty;
                     let qs = quote_spanned!(f.span() => <#ty as #postcard_schema::Schema>::SCHEMA);
 
-                    quote! { &#postcard_schema::schema::DataModelVariant::NewtypeVariant(#qs) }
+                    quote! { #postcard_schema::schema::Data::Newtype(#qs) }
                 } else {
                     let fields = fields.unnamed.iter().map(|f| {
                         let ty = &f.ty;
                         quote_spanned!(f.span() => <#ty as #postcard_schema::Schema>::SCHEMA)
                     });
-                    quote! { &#postcard_schema::schema::DataModelVariant::TupleVariant(&[
+                    quote! { #postcard_schema::schema::Data::Tuple(&[
                         #( #fields ),*
                     ]) }
                 }
             }
             syn::Fields::Unit => {
-                quote! { &#postcard_schema::schema::DataModelVariant::UnitVariant }
+                quote! { #postcard_schema::schema::Data::Unit }
             }
         }
     }
diff --git a/source/postcard-dyn/src/de.rs b/source/postcard-dyn/src/de.rs
index 3e06df3..7e532b1 100644
--- a/source/postcard-dyn/src/de.rs
+++ b/source/postcard-dyn/src/de.rs
@@ -1,6 +1,6 @@
 use std::str::from_utf8;
 
-use postcard_schema::schema::owned::{OwnedDataModelType, OwnedDataModelVariant, OwnedNamedType};
+use postcard_schema::schema::owned::{OwnedData, OwnedDataModelType};
 use serde_json::{Map, Number, Value};
 
 use crate::de::varint::de_zig_zag_i16;
@@ -30,12 +30,12 @@ impl<T> GetExt for Option<T> {
     }
 }
 
-pub fn from_slice_dyn(schema: &OwnedNamedType, data: &[u8]) -> Result<Value, Error> {
-    let (val, _remain) = de_named_type(&schema.ty, data)?;
+pub fn from_slice_dyn(schema: &OwnedDataModelType, data: &[u8]) -> Result<Value, Error> {
+    let (val, _remain) = deserialize(schema, data)?;
     Ok(val)
 }
 
-fn de_named_type<'a>(ty: &OwnedDataModelType, data: &'a [u8]) -> Result<(Value, &'a [u8]), Error> {
+fn deserialize<'a>(ty: &OwnedDataModelType, data: &'a [u8]) -> Result<(Value, &'a [u8]), Error> {
     match ty {
         OwnedDataModelType::Bool => {
             let (one, rest) = data.take_one()?;
@@ -156,46 +156,57 @@ fn de_named_type<'a>(ty: &OwnedDataModelType, data: &'a [u8]) -> Result<(Value,
             let val = Value::Array(vvec);
             Ok((val, rest))
         }
-        OwnedDataModelType::Option(nt) => {
+        OwnedDataModelType::Option(inner) => {
             let (val, rest) = data.take_one()?;
             match val {
                 0 => return Ok((Value::Null, rest)),
                 1 => {}
                 _ => return Err(Error::SchemaMismatch),
             }
-            de_named_type(&nt.ty, rest)
+            deserialize(inner, rest)
         }
-        OwnedDataModelType::Unit | OwnedDataModelType::UnitStruct => {
+        OwnedDataModelType::Unit
+        | OwnedDataModelType::Struct {
+            name: _,
+            data: OwnedData::Unit,
+        } => {
             // TODO This is PROBABLY wrong, as Some(()) will be coalesced into the same
             // value as None. Fix this when we have our own Value
             Ok((Value::Null, data))
         }
-        OwnedDataModelType::NewtypeStruct(nt) => de_named_type(&nt.ty, data),
-        OwnedDataModelType::Seq(nt) => {
+        OwnedDataModelType::Struct {
+            name: _,
+            data: OwnedData::Newtype(ty),
+        } => deserialize(ty, data),
+        OwnedDataModelType::Seq(ty) => {
             let (val, mut rest) = try_take_varint_usize(data)?;
             let mut vec = vec![];
             for _ in 0..val {
-                let (v, irest) = de_named_type(&nt.ty, rest)?;
+                let (v, irest) = deserialize(ty, rest)?;
                 rest = irest;
                 vec.push(v);
             }
             Ok((Value::Array(vec), rest))
         }
-        OwnedDataModelType::Tuple(nts) | OwnedDataModelType::TupleStruct(nts) => {
-            match nts.as_slice() {
+        OwnedDataModelType::Tuple(tys)
+        | OwnedDataModelType::Struct {
+            name: _,
+            data: OwnedData::Tuple(tys),
+        } => {
+            match &tys[..] {
                 [] => {
                     // TODO: Not sure this is right...
                     Ok((Value::Null, data))
                 }
-                [nt] => {
+                [ty] => {
                     // Single item, NOT an array
-                    de_named_type(&nt.ty, data)
+                    deserialize(ty, data)
                 }
                 multi => {
                     let mut vec = vec![];
                     let mut rest = data;
-                    for nt in multi.iter() {
-                        let (val, irest) = de_named_type(&nt.ty, rest)?;
+                    for ty in multi.iter() {
+                        let (val, irest) = deserialize(ty, rest)?;
                         rest = irest;
                         vec.push(val);
                     }
@@ -208,7 +219,7 @@ fn de_named_type<'a>(ty: &OwnedDataModelType, data: &'a [u8]) -> Result<(Value,
             //
             // TODO: There's also a mismatch here because serde_json::Value requires
             // keys to be strings, when postcard doesn't.
-            if key.ty != OwnedDataModelType::String {
+            if **key != OwnedDataModelType::String {
                 return Err(Error::ShouldSupportButDont);
             }
 
@@ -220,7 +231,7 @@ fn de_named_type<'a>(ty: &OwnedDataModelType, data: &'a [u8]) -> Result<(Value,
                 let (bytes, irest) = irest.take_n(str_len)?;
                 let s = from_utf8(bytes).map_err(|_| Error::SchemaMismatch)?;
 
-                let (v, irest) = de_named_type(&val.ty, irest)?;
+                let (v, irest) = deserialize(val, irest)?;
                 rest = irest;
 
                 map.insert(s.to_string(), v);
@@ -228,43 +239,53 @@ fn de_named_type<'a>(ty: &OwnedDataModelType, data: &'a [u8]) -> Result<(Value,
 
             Ok((Value::Object(map), rest))
         }
-        OwnedDataModelType::Struct(nvs) => {
+        OwnedDataModelType::Struct {
+            name: _,
+            data: OwnedData::Struct(nvs),
+        } => {
             let mut map = Map::new();
             let mut rest = data;
             for nv in nvs.iter() {
-                let (val, irest) = de_named_type(&nv.ty.ty, rest)?;
+                let (val, irest) = deserialize(&nv.ty, rest)?;
                 rest = irest;
                 map.insert(nv.name.to_string(), val);
             }
             Ok((Value::Object(map), rest))
         }
-        OwnedDataModelType::Enum(nvars) => {
+        OwnedDataModelType::Enum {
+            name: _,
+            variants: nvars,
+        } => {
             let (variant, rest) = try_take_varint_usize(data)?;
             let schema = nvars.get(variant).right()?;
-            match &schema.ty {
-                OwnedDataModelVariant::UnitVariant => {
+            match &schema.data {
+                OwnedData::Unit => {
                     // Units become strings
                     Ok((Value::String(schema.name.to_string()), rest))
                 }
-                OwnedDataModelVariant::NewtypeVariant(owned_named_type) => {
+                OwnedData::Newtype(ty) => {
                     // everything else becomes an object with one field
-                    let (val, irest) = de_named_type(&owned_named_type.ty, rest)?;
+                    let (val, irest) = deserialize(ty, rest)?;
                     let mut map = Map::new();
                     map.insert(schema.name.to_owned().to_string(), val);
                     Ok((Value::Object(map), irest))
                 }
-                OwnedDataModelVariant::TupleVariant(vec) => {
+                OwnedData::Tuple(vec) => {
                     // everything else becomes an object with one field
-                    let (val, irest) =
-                        de_named_type(&OwnedDataModelType::Tuple(vec.clone()), rest)?;
+                    let (val, irest) = deserialize(&OwnedDataModelType::Tuple(vec.clone()), rest)?;
                     let mut map = Map::new();
                     map.insert(schema.name.to_owned().to_string(), val);
                     Ok((Value::Object(map), irest))
                 }
-                OwnedDataModelVariant::StructVariant(vec) => {
+                OwnedData::Struct(vec) => {
                     // everything else becomes an object with one field
-                    let (val, irest) =
-                        de_named_type(&OwnedDataModelType::Struct(vec.clone()), rest)?;
+                    let (val, irest) = deserialize(
+                        &OwnedDataModelType::Struct {
+                            name: schema.name.clone(),
+                            data: OwnedData::Struct(vec.clone()),
+                        },
+                        rest,
+                    )?;
                     let mut map = Map::new();
                     map.insert(schema.name.to_owned().to_string(), val);
                     Ok((Value::Object(map), irest))
diff --git a/source/postcard-dyn/src/ser.rs b/source/postcard-dyn/src/ser.rs
index dd75fcd..7cddbb3 100644
--- a/source/postcard-dyn/src/ser.rs
+++ b/source/postcard-dyn/src/ser.rs
@@ -1,6 +1,6 @@
 use std::num::TryFromIntError;
 
-use postcard_schema::schema::owned::{OwnedDataModelType, OwnedDataModelVariant, OwnedNamedType};
+use postcard_schema::schema::owned::{OwnedData, OwnedDataModelType};
 use serde_json::Value;
 use varint::{
     varint_max, varint_u128, varint_u16, varint_u32, varint_u64, varint_usize, zig_zag_i128,
@@ -15,10 +15,10 @@ pub enum Error {
     Unsupported,
 }
 
-pub fn to_stdvec_dyn(schema: &OwnedNamedType, value: &Value) -> Result<Vec<u8>, Error> {
+pub fn to_stdvec_dyn(schema: &OwnedDataModelType, value: &Value) -> Result<Vec<u8>, Error> {
     let mut out = vec![];
 
-    ser_named_type(&schema.ty, value, &mut out)?;
+    ser_named_type(schema, value, &mut out)?;
 
     Ok(out)
 }
@@ -190,20 +190,26 @@ fn ser_named_type(ty: &OwnedDataModelType, value: &Value, out: &mut Vec<u8>) ->
                 out.push(val);
             }
         }
-        OwnedDataModelType::Option(nt) => {
+        OwnedDataModelType::Option(ty) => {
             if value.is_null() {
                 out.push(0x00);
             } else {
                 out.push(0x01);
-                ser_named_type(&nt.ty, value, out)?;
+                ser_named_type(ty, value, out)?;
             }
         }
         OwnedDataModelType::Unit => {}
-        OwnedDataModelType::UnitStruct => {}
-        OwnedDataModelType::NewtypeStruct(nt) => {
-            ser_named_type(&nt.ty, value, out)?;
+        OwnedDataModelType::Struct {
+            name: _,
+            data: OwnedData::Unit,
+        } => {}
+        OwnedDataModelType::Struct {
+            name: _,
+            data: OwnedData::Newtype(ty),
+        } => {
+            ser_named_type(ty, value, out)?;
         }
-        OwnedDataModelType::Seq(nt) => {
+        OwnedDataModelType::Seq(ty) => {
             let val = value.as_array().right()?;
 
             // First add len
@@ -214,23 +220,27 @@ fn ser_named_type(ty: &OwnedDataModelType, value: &Value, out: &mut Vec<u8>) ->
 
             // Then add values
             for b in val {
-                ser_named_type(&nt.ty, b, out)?;
+                ser_named_type(ty, b, out)?;
             }
         }
-        OwnedDataModelType::Tuple(nts) | OwnedDataModelType::TupleStruct(nts) => {
+        OwnedDataModelType::Tuple(tys)
+        | OwnedDataModelType::Struct {
+            name: _,
+            data: OwnedData::Tuple(tys),
+        } => {
             // Tuples with arity of 1 are not arrays, but instead just a single object
-            if nts.len() == 1 {
-                return ser_named_type(&nts[0].ty, value, out);
+            if tys.len() == 1 {
+                return ser_named_type(&tys[0], value, out);
             }
 
             let val = value.as_array().right()?;
 
-            if val.len() != nts.len() {
+            if val.len() != tys.len() {
                 return Err(Error::SchemaMismatch);
             }
 
-            for (nt, val) in nts.iter().zip(val.iter()) {
-                ser_named_type(&nt.ty, val, out)?;
+            for (ty, val) in tys.iter().zip(val.iter()) {
+                ser_named_type(ty, val, out)?;
             }
         }
         OwnedDataModelType::Map { key, val } => {
@@ -238,7 +248,7 @@ fn ser_named_type(ty: &OwnedDataModelType, value: &Value, out: &mut Vec<u8>) ->
             //
             // TODO: There's also a mismatch here because serde_json::Value requires
             // keys to be strings, when postcard doesn't.
-            if key.ty != OwnedDataModelType::String {
+            if **key != OwnedDataModelType::String {
                 return Err(Error::ShouldSupportButDont);
             }
 
@@ -263,10 +273,13 @@ fn ser_named_type(ty: &OwnedDataModelType, value: &Value, out: &mut Vec<u8>) ->
                 out.extend_from_slice(k.as_bytes());
 
                 // VALUE
-                ser_named_type(&val.ty, v, out)?;
+                ser_named_type(val, v, out)?;
             }
         }
-        OwnedDataModelType::Struct(nvs) => {
+        OwnedDataModelType::Struct {
+            name: _,
+            data: OwnedData::Struct(nvs),
+        } => {
             let val = value.as_object().right()?;
 
             if val.len() != nvs.len() {
@@ -274,11 +287,14 @@ fn ser_named_type(ty: &OwnedDataModelType, value: &Value, out: &mut Vec<u8>) ->
             }
 
             for field in nvs.iter() {
-                let v = val.get(&field.name).right()?;
-                ser_named_type(&field.ty.ty, v, out)?;
+                let v = val.get(field.name.as_ref()).right()?;
+                ser_named_type(&field.ty, v, out)?;
             }
         }
-        OwnedDataModelType::Enum(nvars) => {
+        OwnedDataModelType::Enum {
+            name: _,
+            variants: nvars,
+        } => {
             // This is a bit serde_json::Value specific, if we make our own value
             // type we might be able to handle this "better"
 
@@ -288,9 +304,9 @@ fn ser_named_type(ty: &OwnedDataModelType, value: &Value, out: &mut Vec<u8>) ->
                 let (idx, evar) = nvars
                     .iter()
                     .enumerate()
-                    .find(|(_i, v)| v.name == s)
+                    .find(|(_i, v)| *v.name == *s)
                     .right()?;
-                if evar.ty != OwnedDataModelVariant::UnitVariant {
+                if evar.data != OwnedData::Unit {
                     return Err(Error::SchemaMismatch);
                 }
 
@@ -307,7 +323,7 @@ fn ser_named_type(ty: &OwnedDataModelType, value: &Value, out: &mut Vec<u8>) ->
                 let (idx, evar) = nvars
                     .iter()
                     .enumerate()
-                    .find(|(_i, v)| &v.name == k)
+                    .find(|(_i, v)| *v.name == *k)
                     .right()?;
 
                 // cool, we found it, serialize as a varint usize
@@ -316,30 +332,30 @@ fn ser_named_type(ty: &OwnedDataModelType, value: &Value, out: &mut Vec<u8>) ->
                 out.extend_from_slice(used);
 
                 // then serialize the value
-                match &evar.ty {
-                    OwnedDataModelVariant::UnitVariant => {
+                match &evar.data {
+                    OwnedData::Unit => {
                         // Nothing to do
                     }
-                    OwnedDataModelVariant::NewtypeVariant(owned_named_type) => {
-                        ser_named_type(&owned_named_type.ty, v, out)?;
+                    OwnedData::Newtype(ty) => {
+                        ser_named_type(ty, v, out)?;
                     }
-                    OwnedDataModelVariant::TupleVariant(nts) => {
+                    OwnedData::Tuple(tys) => {
                         // Tuples with arity of 1 are not arrays, but instead just a single object
-                        if nts.len() == 1 {
-                            return ser_named_type(&nts[0].ty, v, out);
+                        if tys.len() == 1 {
+                            return ser_named_type(&tys[0], v, out);
                         }
 
                         let val = v.as_array().right()?;
 
-                        if val.len() != nts.len() {
+                        if val.len() != tys.len() {
                             return Err(Error::SchemaMismatch);
                         }
 
-                        for (nt, val) in nts.iter().zip(val.iter()) {
-                            ser_named_type(&nt.ty, val, out)?;
+                        for (ty, val) in tys.iter().zip(val.iter()) {
+                            ser_named_type(ty, val, out)?;
                         }
                     }
-                    OwnedDataModelVariant::StructVariant(nvs) => {
+                    OwnedData::Struct(nvs) => {
                         let val = v.as_object().right()?;
 
                         if val.len() != nvs.len() {
@@ -347,8 +363,8 @@ fn ser_named_type(ty: &OwnedDataModelType, value: &Value, out: &mut Vec<u8>) ->
                         }
 
                         for field in nvs.iter() {
-                            let v = val.get(&field.name).right()?;
-                            ser_named_type(&field.ty.ty, v, out)?;
+                            let v = val.get(field.name.as_ref()).right()?;
+                            ser_named_type(&field.ty, v, out)?;
                         }
                     }
                 }
diff --git a/source/postcard-schema/src/impls/builtins_alloc.rs b/source/postcard-schema/src/impls/builtins_alloc.rs
index 6b2d0b8..6c2c898 100644
--- a/source/postcard-schema/src/impls/builtins_alloc.rs
+++ b/source/postcard-schema/src/impls/builtins_alloc.rs
@@ -1,39 +1,24 @@
 //! Implementations of the [`Schema`] trait for `alloc` types
 
-use crate::{
-    schema::{DataModelType, NamedType},
-    Schema,
-};
+use crate::{schema::DataModelType, Schema};
 
 extern crate alloc;
 
 impl<T: Schema> Schema for alloc::vec::Vec<T> {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "Vec<T>",
-        ty: &DataModelType::Seq(T::SCHEMA),
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::Seq(T::SCHEMA);
 }
 
 impl Schema for alloc::string::String {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "String",
-        ty: &DataModelType::String,
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::String;
 }
 
 impl<K: Schema, V: Schema> Schema for alloc::collections::BTreeMap<K, V> {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "BTreeMap<K, V>",
-        ty: &DataModelType::Map {
-            key: K::SCHEMA,
-            val: V::SCHEMA,
-        },
+    const SCHEMA: &'static DataModelType = &DataModelType::Map {
+        key: K::SCHEMA,
+        val: V::SCHEMA,
     };
 }
 
 impl<K: Schema> Schema for alloc::collections::BTreeSet<K> {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "BTreeSet<K>",
-        ty: &DataModelType::Seq(K::SCHEMA),
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::Seq(K::SCHEMA);
 }
diff --git a/source/postcard-schema/src/impls/builtins_nostd.rs b/source/postcard-schema/src/impls/builtins_nostd.rs
index 72debaa..fbc4f3d 100644
--- a/source/postcard-schema/src/impls/builtins_nostd.rs
+++ b/source/postcard-schema/src/impls/builtins_nostd.rs
@@ -3,7 +3,7 @@
 //! These implementations are always available
 
 use crate::{
-    schema::{DataModelType, DataModelVariant, NamedType, NamedValue, NamedVariant},
+    schema::{Data, DataModelType, NamedField, Variant},
     Schema,
 };
 use core::{
@@ -18,20 +18,14 @@ macro_rules! impl_schema {
     ($($t:ty: $sdm:expr),*) => {
         $(
             impl Schema for $t {
-                const SCHEMA: &'static NamedType = &NamedType {
-                    name: stringify!($t),
-                    ty: &$sdm,
-                };
+                const SCHEMA: &'static DataModelType = &$sdm;
             }
         )*
     };
     (tuple => [$(($($generic:ident),*)),*]) => {
         $(
             impl<$($generic: Schema),*> Schema for ($($generic,)*) {
-                const SCHEMA: &'static NamedType = &NamedType {
-                    name: stringify!(($($generic,)*)),
-                    ty: &DataModelType::Tuple(&[$($generic::SCHEMA),*]),
-                };
+                const SCHEMA: &'static DataModelType = &DataModelType::Tuple(&[$($generic::SCHEMA),*]);
             }
         )*
     };
@@ -66,53 +60,46 @@ impl_schema!(tuple => [
 ]);
 
 impl<T: Schema> Schema for Option<T> {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "Option<T>",
-        ty: &DataModelType::Option(T::SCHEMA),
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::Option(T::SCHEMA);
 }
+
 impl<T: Schema, E: Schema> Schema for Result<T, E> {
-    const SCHEMA: &'static NamedType = &NamedType {
+    const SCHEMA: &'static DataModelType = &DataModelType::Enum {
         name: "Result<T, E>",
-        ty: &DataModelType::Enum(&[
-            &NamedVariant {
+        variants: &[
+            &Variant {
                 name: "Ok",
-                ty: &DataModelVariant::TupleVariant(&[T::SCHEMA]),
+                data: Data::Newtype(T::SCHEMA),
             },
-            &NamedVariant {
+            &Variant {
                 name: "Err",
-                ty: &DataModelVariant::TupleVariant(&[E::SCHEMA]),
+                data: Data::Newtype(E::SCHEMA),
             },
-        ]),
+        ],
     };
 }
 
 impl<T: Schema + ?Sized> Schema for &'_ T {
-    const SCHEMA: &'static NamedType = T::SCHEMA;
+    const SCHEMA: &'static DataModelType = T::SCHEMA;
 }
 
 impl<T: Schema> Schema for [T] {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "[T]",
-        ty: &DataModelType::Seq(T::SCHEMA),
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::Seq(T::SCHEMA);
 }
+
 impl<T: Schema, const N: usize> Schema for [T; N] {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "[T; N]",
-        ty: &DataModelType::Tuple(&[T::SCHEMA; N]),
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::Tuple(&[T::SCHEMA; N]);
 }
 
 impl<T: Schema> Schema for Range<T> {
-    const SCHEMA: &'static crate::schema::NamedType = &NamedType {
+    const SCHEMA: &'static DataModelType = &DataModelType::Struct {
         name: "Range<T>",
-        ty: &DataModelType::Struct(&[
-            &NamedValue {
+        data: Data::Struct(&[
+            &NamedField {
                 name: "start",
                 ty: T::SCHEMA,
             },
-            &NamedValue {
+            &NamedField {
                 name: "end",
                 ty: T::SCHEMA,
             },
@@ -121,14 +108,14 @@ impl<T: Schema> Schema for Range<T> {
 }
 
 impl<T: Schema> Schema for RangeInclusive<T> {
-    const SCHEMA: &'static crate::schema::NamedType = &NamedType {
+    const SCHEMA: &'static DataModelType = &DataModelType::Struct {
         name: "RangeInclusive<T>",
-        ty: &DataModelType::Struct(&[
-            &NamedValue {
+        data: Data::Struct(&[
+            &NamedField {
                 name: "start",
                 ty: T::SCHEMA,
             },
-            &NamedValue {
+            &NamedField {
                 name: "end",
                 ty: T::SCHEMA,
             },
@@ -137,9 +124,9 @@ impl<T: Schema> Schema for RangeInclusive<T> {
 }
 
 impl<T: Schema> Schema for RangeFrom<T> {
-    const SCHEMA: &'static crate::schema::NamedType = &NamedType {
+    const SCHEMA: &'static DataModelType = &DataModelType::Struct {
         name: "RangeFrom<T>",
-        ty: &DataModelType::Struct(&[&NamedValue {
+        data: Data::Struct(&[&NamedField {
             name: "start",
             ty: T::SCHEMA,
         }]),
@@ -147,9 +134,9 @@ impl<T: Schema> Schema for RangeFrom<T> {
 }
 
 impl<T: Schema> Schema for RangeTo<T> {
-    const SCHEMA: &'static crate::schema::NamedType = &NamedType {
+    const SCHEMA: &'static DataModelType = &DataModelType::Struct {
         name: "RangeTo<T>",
-        ty: &DataModelType::Struct(&[&NamedValue {
+        data: Data::Struct(&[&NamedField {
             name: "end",
             ty: T::SCHEMA,
         }]),
diff --git a/source/postcard-schema/src/impls/builtins_std.rs b/source/postcard-schema/src/impls/builtins_std.rs
index 6b62abd..44cc026 100644
--- a/source/postcard-schema/src/impls/builtins_std.rs
+++ b/source/postcard-schema/src/impls/builtins_std.rs
@@ -1,68 +1,44 @@
 //! Implementations of the [`Schema`] trait for `std` types
 
-use crate::{
-    schema::{DataModelType, NamedType},
-    Schema,
-};
+use crate::{schema::DataModelType, Schema};
 
 #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "use-std"))))]
 impl<T: Schema> Schema for std::vec::Vec<T> {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "Vec<T>",
-        ty: &DataModelType::Seq(T::SCHEMA),
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::Seq(T::SCHEMA);
 }
 
 #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "use-std"))))]
 impl Schema for std::string::String {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "String",
-        ty: &DataModelType::String,
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::String;
 }
 
 #[cfg_attr(docsrs, doc(cfg(feature = "use-std")))]
 impl Schema for std::path::PathBuf {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "PathBuf",
-        ty: &DataModelType::String,
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::String;
 }
 
 #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "use-std"))))]
 impl<K: Schema, V: Schema> Schema for std::collections::HashMap<K, V> {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "HashMap<K, V>",
-        ty: &DataModelType::Map {
-            key: K::SCHEMA,
-            val: V::SCHEMA,
-        },
+    const SCHEMA: &'static DataModelType = &DataModelType::Map {
+        key: K::SCHEMA,
+        val: V::SCHEMA,
     };
 }
 
 #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "use-std"))))]
 impl<K: Schema, V: Schema> Schema for std::collections::BTreeMap<K, V> {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "BTreeMap<K, V>",
-        ty: &DataModelType::Map {
-            key: K::SCHEMA,
-            val: V::SCHEMA,
-        },
+    const SCHEMA: &'static DataModelType = &DataModelType::Map {
+        key: K::SCHEMA,
+        val: V::SCHEMA,
     };
 }
 
 #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "use-std"))))]
 impl<K: Schema> Schema for std::collections::HashSet<K> {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "HashSet<K>",
-        ty: &DataModelType::Seq(K::SCHEMA),
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::Seq(K::SCHEMA);
 }
 
 #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "use-std"))))]
 impl<K: Schema> Schema for std::collections::BTreeSet<K> {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "BTreeSet<K>",
-        ty: &DataModelType::Seq(K::SCHEMA),
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::Seq(K::SCHEMA);
 }
diff --git a/source/postcard-schema/src/impls/chrono_v0_4.rs b/source/postcard-schema/src/impls/chrono_v0_4.rs
index 8c439e1..1e206a7 100644
--- a/source/postcard-schema/src/impls/chrono_v0_4.rs
+++ b/source/postcard-schema/src/impls/chrono_v0_4.rs
@@ -1,11 +1,8 @@
 //! Implementations of the [`Schema`] trait for the `chrono` crate v0.4
 
-use crate::{schema::NamedType, Schema};
+use crate::{schema::DataModelType, Schema};
 
 #[cfg_attr(docsrs, doc(cfg(feature = "chrono-v0_4")))]
 impl<Tz: chrono_v0_4::TimeZone> Schema for chrono_v0_4::DateTime<Tz> {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "DateTime",
-        ty: <&str>::SCHEMA.ty,
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::String;
 }
diff --git a/source/postcard-schema/src/impls/heapless_v0_7.rs b/source/postcard-schema/src/impls/heapless_v0_7.rs
index e215189..d08a491 100644
--- a/source/postcard-schema/src/impls/heapless_v0_7.rs
+++ b/source/postcard-schema/src/impls/heapless_v0_7.rs
@@ -1,21 +1,13 @@
 //! Implementations of the [`Schema`] trait for the `heapless` crate v0.7
 
-use crate::{
-    schema::{DataModelType, NamedType},
-    Schema,
-};
+use crate::{schema::DataModelType, Schema};
 
 #[cfg_attr(docsrs, doc(cfg(feature = "heapless-v0_7")))]
 impl<T: Schema, const N: usize> Schema for heapless_v0_7::Vec<T, N> {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "heapless::Vec<T, N>",
-        ty: &DataModelType::Seq(T::SCHEMA),
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::Seq(T::SCHEMA);
 }
+
 #[cfg_attr(docsrs, doc(cfg(feature = "heapless-v0_7")))]
 impl<const N: usize> Schema for heapless_v0_7::String<N> {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "heapless::String<N>",
-        ty: &DataModelType::String,
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::String;
 }
diff --git a/source/postcard-schema/src/impls/heapless_v0_8.rs b/source/postcard-schema/src/impls/heapless_v0_8.rs
index 09f9cfa..85ed3d4 100644
--- a/source/postcard-schema/src/impls/heapless_v0_8.rs
+++ b/source/postcard-schema/src/impls/heapless_v0_8.rs
@@ -1,21 +1,13 @@
 //! Implementations of the [`Schema`] trait for the `heapless` crate v0.8
 
-use crate::{
-    schema::{DataModelType, NamedType},
-    Schema,
-};
+use crate::{schema::DataModelType, Schema};
 
 #[cfg_attr(docsrs, doc(cfg(feature = "heapless-v0_8")))]
 impl<T: Schema, const N: usize> Schema for heapless_v0_8::Vec<T, N> {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "heapless::Vec<T, N>",
-        ty: &DataModelType::Seq(T::SCHEMA),
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::Seq(T::SCHEMA);
 }
+
 #[cfg_attr(docsrs, doc(cfg(feature = "heapless-v0_8")))]
 impl<const N: usize> Schema for heapless_v0_8::String<N> {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "heapless::String<N>",
-        ty: &DataModelType::String,
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::String;
 }
diff --git a/source/postcard-schema/src/impls/mod.rs b/source/postcard-schema/src/impls/mod.rs
index 975afcd..c541c8d 100644
--- a/source/postcard-schema/src/impls/mod.rs
+++ b/source/postcard-schema/src/impls/mod.rs
@@ -2,10 +2,7 @@
 //!
 //! Each module requires the matching feature flag to be enabled.
 
-use crate::{
-    schema::{DataModelType, NamedType},
-    Schema,
-};
+use crate::{schema::DataModelType, Schema};
 
 pub mod builtins_nostd;
 
@@ -37,9 +34,6 @@ pub mod heapless_v0_8;
 #[cfg_attr(docsrs, doc(cfg(feature = "nalgebra-v0_33")))]
 pub mod nalgebra_v0_33;
 
-impl Schema for NamedType {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "NamedType",
-        ty: &DataModelType::Schema,
-    };
+impl Schema for DataModelType {
+    const SCHEMA: &'static DataModelType = &DataModelType::Schema;
 }
diff --git a/source/postcard-schema/src/impls/nalgebra_v0_33.rs b/source/postcard-schema/src/impls/nalgebra_v0_33.rs
index 3f9ceb7..a7a6fcf 100644
--- a/source/postcard-schema/src/impls/nalgebra_v0_33.rs
+++ b/source/postcard-schema/src/impls/nalgebra_v0_33.rs
@@ -1,9 +1,6 @@
 //! Implementations of the [`Schema`] trait for the `nalgebra` crate v0.33
 
-use crate::{
-    schema::{DataModelType, NamedType},
-    Schema,
-};
+use crate::{schema::DataModelType, Schema};
 
 #[cfg_attr(docsrs, doc(cfg(feature = "nalgebra-v0_33")))]
 impl<T, const R: usize, const C: usize> Schema
@@ -16,10 +13,7 @@ impl<T, const R: usize, const C: usize> Schema
 where
     T: Schema + nalgebra_v0_33::Scalar,
 {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "nalgebra::Matrix<T, R, C, ArrayStorage<T, R, C>>",
-        ty: &DataModelType::Tuple(flatten(&[[T::SCHEMA; R]; C])),
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::Tuple(flatten(&[[T::SCHEMA; R]; C]));
 }
 
 /// Const version of the const-unstable [`<[[T; N]]>::as_flattened()`]
@@ -37,7 +31,7 @@ const fn flatten<T, const N: usize>(slice: &[[T; N]]) -> &[T] {
 #[test]
 fn flattened() {
     type T = nalgebra_v0_33::SMatrix<u8, 3, 3>;
-    assert_eq!(T::SCHEMA.ty, <[u8; 9]>::SCHEMA.ty);
+    assert_eq!(T::SCHEMA, <[u8; 9]>::SCHEMA);
 }
 
 #[test]
diff --git a/source/postcard-schema/src/impls/uuid_v1_0.rs b/source/postcard-schema/src/impls/uuid_v1_0.rs
index 805557b..f8d3baa 100644
--- a/source/postcard-schema/src/impls/uuid_v1_0.rs
+++ b/source/postcard-schema/src/impls/uuid_v1_0.rs
@@ -1,13 +1,7 @@
 //! Implementations of the [`Schema`] trait for the `uuid` crate v1.0
 
-use crate::{
-    schema::{DataModelType, NamedType},
-    Schema,
-};
+use crate::{schema::DataModelType, Schema};
 
 impl Schema for uuid_v1_0::Uuid {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "Uuid",
-        ty: &DataModelType::ByteArray,
-    };
+    const SCHEMA: &'static DataModelType = &DataModelType::ByteArray;
 }
diff --git a/source/postcard-schema/src/key/hash.rs b/source/postcard-schema/src/key/hash.rs
index e9bfd8b..9e6045d 100644
--- a/source/postcard-schema/src/key/hash.rs
+++ b/source/postcard-schema/src/key/hash.rs
@@ -8,10 +8,7 @@
 //!
 //! [was noted]: https://doc.rust-lang.org/stable/std/hash/trait.Hash.html#portability
 
-use crate::{
-    schema::{DataModelType, NamedType, NamedValue, NamedVariant},
-    Schema,
-};
+use crate::{schema::DataModelType, Schema};
 
 /// A const compatible Fnv1a64 hasher
 pub struct Fnv1a64Hasher {
@@ -56,7 +53,8 @@ impl Default for Fnv1a64Hasher {
 
 pub mod fnv1a64 {
     //! Const and no-std helper methods and types for perfoming hash calculation
-    use crate::schema::DataModelVariant;
+
+    use crate::schema::{Data, NamedField, Variant};
 
     use super::*;
 
@@ -64,7 +62,7 @@ pub mod fnv1a64 {
     pub const fn hash_ty_path<T: Schema + ?Sized>(path: &str) -> [u8; 8] {
         let schema = T::SCHEMA;
         let state = hash_update_str(Fnv1a64Hasher::BASIS, path);
-        hash_named_type(state, schema).to_le_bytes()
+        hash_sdm_type(state, schema).to_le_bytes()
     }
 
     pub(crate) const fn hash_update(mut state: u64, bytes: &[u8]) -> u64 {
@@ -131,97 +129,99 @@ pub mod fnv1a64 {
             DataModelType::Char => hash_update(state, &[0xC1]),
             DataModelType::String => hash_update(state, &[0x25]),
             DataModelType::ByteArray => hash_update(state, &[0x65]),
-            DataModelType::Option(nt) => {
+            DataModelType::Option(t) => {
                 let state = hash_update(state, &[0x6D]);
-                hash_named_type(state, nt)
+                hash_sdm_type(state, t)
             }
             DataModelType::Unit => hash_update(state, &[0x47]),
-            DataModelType::UnitStruct => hash_update(state, &[0xBF]),
-            DataModelType::NewtypeStruct(nt) => {
-                let state = hash_update(state, &[0x9D]);
-                hash_named_type(state, nt)
-            }
-            DataModelType::Seq(nt) => {
+            DataModelType::Seq(t) => {
                 let state = hash_update(state, &[0x03]);
-                hash_named_type(state, nt)
+                hash_sdm_type(state, t)
             }
-            DataModelType::Tuple(nts) => {
+            DataModelType::Tuple(ts) => {
                 let mut state = hash_update(state, &[0xA7]);
                 let mut idx = 0;
-                while idx < nts.len() {
-                    state = hash_named_type(state, nts[idx]);
-                    idx += 1;
-                }
-                state
-            }
-            DataModelType::TupleStruct(nts) => {
-                let mut state = hash_update(state, &[0x05]);
-                let mut idx = 0;
-                while idx < nts.len() {
-                    state = hash_named_type(state, nts[idx]);
+                while idx < ts.len() {
+                    state = hash_sdm_type(state, ts[idx]);
                     idx += 1;
                 }
                 state
             }
             DataModelType::Map { key, val } => {
                 let state = hash_update(state, &[0x4F]);
-                let state = hash_named_type(state, key);
-                hash_named_type(state, val)
+                let state = hash_sdm_type(state, key);
+                hash_sdm_type(state, val)
             }
-            DataModelType::Struct(nvs) => {
-                let mut state = hash_update(state, &[0x7F]);
-                let mut idx = 0;
-                while idx < nvs.len() {
-                    state = hash_named_value(state, nvs[idx]);
-                    idx += 1;
-                }
-                state
-            }
-            DataModelType::Enum(nvs) => {
+            DataModelType::Struct { name, data } => hash_struct(state, name, data),
+            DataModelType::Enum { name: _, variants } => {
                 let mut state = hash_update(state, &[0xE9]);
                 let mut idx = 0;
-                while idx < nvs.len() {
-                    state = hash_named_variant(state, nvs[idx]);
+                while idx < variants.len() {
+                    state = hash_variant(state, variants[idx]);
                     idx += 1;
                 }
                 state
             }
-            DataModelType::Schema => hash_update(state, &[0xB3]),
+            DataModelType::Schema => hash_update(state, &[0xE5]),
         }
     }
 
-    const fn hash_named_type(state: u64, nt: &NamedType) -> u64 {
+    const fn hash_struct(state: u64, _name: &str, data: &Data) -> u64 {
         // NOTE: We do *not* hash the name of the type in hashv2. This
         // is to allow "safe" type punning, e.g. treating `Vec<u8>` and
         // `&[u8]` as compatible types, when talking between std and no-std
         // targets
         //
-        // let state = hash_update(state, nt.name.as_bytes());
-        hash_sdm_type(state, nt.ty)
+        // let state = hash_update(state, name.as_bytes());
+        match data {
+            Data::Unit => hash_update(state, &[0xBF]),
+            Data::Newtype(dmt) => {
+                let state = hash_update(state, &[0x9D]);
+                hash_sdm_type(state, dmt)
+            }
+            Data::Tuple(dmts) => {
+                let mut state = hash_update(state, &[0x05]);
+                let mut idx = 0;
+                while idx < dmts.len() {
+                    state = hash_sdm_type(state, dmts[idx]);
+                    idx += 1;
+                }
+                state
+            }
+            Data::Struct(nfs) => {
+                let mut state = hash_update(state, &[0x7F]);
+                let mut idx = 0;
+                while idx < nfs.len() {
+                    state = hash_named_field(state, nfs[idx]);
+                    idx += 1;
+                }
+                state
+            }
+        }
     }
 
-    const fn hash_named_variant(state: u64, nt: &NamedVariant) -> u64 {
+    const fn hash_variant(state: u64, nt: &Variant) -> u64 {
         let state = hash_update(state, nt.name.as_bytes());
-        match nt.ty {
-            DataModelVariant::UnitVariant => hash_update(state, &[0xB5]),
-            DataModelVariant::NewtypeVariant(nt) => {
+        match nt.data {
+            Data::Unit => hash_update(state, &[0xB5]),
+            Data::Newtype(t) => {
                 let state = hash_update(state, &[0xDF]);
-                hash_named_type(state, nt)
+                hash_sdm_type(state, t)
             }
-            DataModelVariant::TupleVariant(nts) => {
+            Data::Tuple(ts) => {
                 let mut state = hash_update(state, &[0xC7]);
                 let mut idx = 0;
-                while idx < nts.len() {
-                    state = hash_named_type(state, nts[idx]);
+                while idx < ts.len() {
+                    state = hash_sdm_type(state, ts[idx]);
                     idx += 1;
                 }
                 state
             }
-            DataModelVariant::StructVariant(nvs) => {
+            Data::Struct(fields) => {
                 let mut state = hash_update(state, &[0x67]);
                 let mut idx = 0;
-                while idx < nvs.len() {
-                    state = hash_named_value(state, nvs[idx]);
+                while idx < fields.len() {
+                    state = hash_named_field(state, fields[idx]);
                     idx += 1;
                 }
                 state
@@ -229,9 +229,9 @@ pub mod fnv1a64 {
         }
     }
 
-    const fn hash_named_value(state: u64, nt: &NamedValue) -> u64 {
+    const fn hash_named_field(state: u64, nt: &NamedField) -> u64 {
         let state = hash_update(state, nt.name.as_bytes());
-        hash_named_type(state, nt.ty)
+        hash_sdm_type(state, nt.ty)
     }
 }
 
@@ -239,21 +239,18 @@ pub mod fnv1a64 {
 pub mod fnv1a64_owned {
     //! Heapful helpers and versions of hashing for use on `std` targets
 
-    use crate::schema::owned::{
-        OwnedDataModelType, OwnedDataModelVariant, OwnedNamedType, OwnedNamedValue,
-        OwnedNamedVariant,
-    };
+    use crate::schema::owned::{OwnedData, OwnedDataModelType, OwnedNamedField, OwnedVariant};
 
     use super::fnv1a64::*;
     use super::*;
 
-    /// Calculate the Key hash for the given path and OwnedNamedType
-    pub fn hash_ty_path_owned(path: &str, nt: &OwnedNamedType) -> [u8; 8] {
+    /// Calculate the Key hash for the given path and [`OwnedDataModelType`]
+    pub fn hash_ty_path_owned(path: &str, ty: &OwnedDataModelType) -> [u8; 8] {
         let state = hash_update_str(Fnv1a64Hasher::BASIS, path);
-        hash_named_type_owned(state, nt).to_le_bytes()
+        hash_sdm_type_owned(state, ty).to_le_bytes()
     }
 
-    fn hash_sdm_type_owned(state: u64, sdmty: &OwnedDataModelType) -> u64 {
+    const fn hash_sdm_type_owned(state: u64, sdmty: &OwnedDataModelType) -> u64 {
         // The actual values we use here don't matter that much (as far as I know),
         // as long as the values for each variant are unique. I am unsure of the
         // implications of doing a TON of single byte calls to `update`, it may be
@@ -302,97 +299,99 @@ pub mod fnv1a64_owned {
             OwnedDataModelType::Char => hash_update(state, &[0xC1]),
             OwnedDataModelType::String => hash_update(state, &[0x25]),
             OwnedDataModelType::ByteArray => hash_update(state, &[0x65]),
-            OwnedDataModelType::Option(nt) => {
+            OwnedDataModelType::Option(t) => {
                 let state = hash_update(state, &[0x6D]);
-                hash_named_type_owned(state, nt)
+                hash_sdm_type_owned(state, t)
             }
             OwnedDataModelType::Unit => hash_update(state, &[0x47]),
-            OwnedDataModelType::UnitStruct => hash_update(state, &[0xBF]),
-            OwnedDataModelType::NewtypeStruct(nt) => {
-                let state = hash_update(state, &[0x9D]);
-                hash_named_type_owned(state, nt)
-            }
-            OwnedDataModelType::Seq(nt) => {
+            OwnedDataModelType::Seq(t) => {
                 let state = hash_update(state, &[0x03]);
-                hash_named_type_owned(state, nt)
+                hash_sdm_type_owned(state, t)
             }
-            OwnedDataModelType::Tuple(nts) => {
+            OwnedDataModelType::Tuple(ts) => {
                 let mut state = hash_update(state, &[0xA7]);
                 let mut idx = 0;
-                while idx < nts.len() {
-                    state = hash_named_type_owned(state, &nts[idx]);
-                    idx += 1;
-                }
-                state
-            }
-            OwnedDataModelType::TupleStruct(nts) => {
-                let mut state = hash_update(state, &[0x05]);
-                let mut idx = 0;
-                while idx < nts.len() {
-                    state = hash_named_type_owned(state, &nts[idx]);
+                while idx < ts.len() {
+                    state = hash_sdm_type_owned(state, &ts[idx]);
                     idx += 1;
                 }
                 state
             }
             OwnedDataModelType::Map { key, val } => {
                 let state = hash_update(state, &[0x4F]);
-                let state = hash_named_type_owned(state, key);
-                hash_named_type_owned(state, val)
-            }
-            OwnedDataModelType::Struct(nvs) => {
-                let mut state = hash_update(state, &[0x7F]);
-                let mut idx = 0;
-                while idx < nvs.len() {
-                    state = hash_named_value_owned(state, &nvs[idx]);
-                    idx += 1;
-                }
-                state
+                let state = hash_sdm_type_owned(state, key);
+                hash_sdm_type_owned(state, val)
             }
-            OwnedDataModelType::Enum(nvs) => {
+            OwnedDataModelType::Struct { name, data } => hash_struct(state, name, data),
+            OwnedDataModelType::Enum { name: _, variants } => {
                 let mut state = hash_update(state, &[0xE9]);
                 let mut idx = 0;
-                while idx < nvs.len() {
-                    state = hash_named_variant_owned(state, &nvs[idx]);
+                while idx < variants.len() {
+                    state = hash_variant(state, &variants[idx]);
                     idx += 1;
                 }
                 state
             }
-            OwnedDataModelType::Schema => hash_update(state, &[0xB3]),
+            OwnedDataModelType::Schema => hash_update(state, &[0xE5]),
         }
     }
 
-    fn hash_named_type_owned(state: u64, nt: &OwnedNamedType) -> u64 {
+    const fn hash_struct(state: u64, _name: &str, data: &OwnedData) -> u64 {
         // NOTE: We do *not* hash the name of the type in hashv2. This
         // is to allow "safe" type punning, e.g. treating `Vec<u8>` and
         // `&[u8]` as compatible types, when talking between std and no-std
         // targets
         //
-        // let state = hash_update(state, nt.name.as_bytes());
-        hash_sdm_type_owned(state, &nt.ty)
+        // let state = hash_update(state, name.as_bytes());
+        match data {
+            OwnedData::Unit => hash_update(state, &[0xBF]),
+            OwnedData::Newtype(dmt) => {
+                let state = hash_update(state, &[0x9D]);
+                hash_sdm_type_owned(state, dmt)
+            }
+            OwnedData::Tuple(dmts) => {
+                let mut state = hash_update(state, &[0x05]);
+                let mut idx = 0;
+                while idx < dmts.len() {
+                    state = hash_sdm_type_owned(state, &dmts[idx]);
+                    idx += 1;
+                }
+                state
+            }
+            OwnedData::Struct(nfs) => {
+                let mut state = hash_update(state, &[0x7F]);
+                let mut idx = 0;
+                while idx < nfs.len() {
+                    state = hash_named_field(state, &nfs[idx]);
+                    idx += 1;
+                }
+                state
+            }
+        }
     }
 
-    fn hash_named_variant_owned(state: u64, nt: &OwnedNamedVariant) -> u64 {
+    const fn hash_variant(state: u64, nt: &OwnedVariant) -> u64 {
         let state = hash_update(state, nt.name.as_bytes());
-        match &nt.ty {
-            OwnedDataModelVariant::UnitVariant => hash_update(state, &[0xB5]),
-            OwnedDataModelVariant::NewtypeVariant(nt) => {
+        match &nt.data {
+            OwnedData::Unit => hash_update(state, &[0xB5]),
+            OwnedData::Newtype(t) => {
                 let state = hash_update(state, &[0xDF]);
-                hash_named_type_owned(state, nt)
+                hash_sdm_type_owned(state, t)
             }
-            OwnedDataModelVariant::TupleVariant(nts) => {
+            OwnedData::Tuple(ts) => {
                 let mut state = hash_update(state, &[0xC7]);
                 let mut idx = 0;
-                while idx < nts.len() {
-                    state = hash_named_type_owned(state, &nts[idx]);
+                while idx < ts.len() {
+                    state = hash_sdm_type_owned(state, &ts[idx]);
                     idx += 1;
                 }
                 state
             }
-            OwnedDataModelVariant::StructVariant(nvs) => {
+            OwnedData::Struct(fields) => {
                 let mut state = hash_update(state, &[0x67]);
                 let mut idx = 0;
-                while idx < nvs.len() {
-                    state = hash_named_value_owned(state, &nvs[idx]);
+                while idx < fields.len() {
+                    state = hash_named_field(state, &fields[idx]);
                     idx += 1;
                 }
                 state
@@ -400,16 +399,42 @@ pub mod fnv1a64_owned {
         }
     }
 
-    fn hash_named_value_owned(state: u64, nt: &OwnedNamedValue) -> u64 {
+    const fn hash_named_field(state: u64, nt: &OwnedNamedField) -> u64 {
         let state = hash_update(state, nt.name.as_bytes());
-        hash_named_type_owned(state, &nt.ty)
+        hash_sdm_type_owned(state, &nt.ty)
     }
 }
 
 #[cfg(test)]
 mod test {
+    use postcard_derive::Schema;
+
     use super::fnv1a64::hash_ty_path;
 
+    #[test]
+    fn hash_stability() {
+        #![allow(dead_code)]
+
+        #[derive(Schema)]
+        #[postcard(crate = crate)]
+        struct Foo {
+            a: u32,
+            b: String,
+        }
+
+        #[derive(Schema)]
+        #[postcard(crate = crate)]
+        enum Bar {
+            A,
+            B(Foo),
+        }
+
+        assert_eq!(
+            hash_ty_path::<Bar>("test_path"),
+            [139, 128, 52, 27, 107, 8, 218, 98]
+        );
+    }
+
     #[test]
     fn type_punning_good() {
         let hash_1 = hash_ty_path::<Vec<u8>>("test_path");
diff --git a/source/postcard-schema/src/key/mod.rs b/source/postcard-schema/src/key/mod.rs
index 23f80e9..277218b 100644
--- a/source/postcard-schema/src/key/mod.rs
+++ b/source/postcard-schema/src/key/mod.rs
@@ -11,10 +11,7 @@
 
 use serde::{Deserialize, Serialize};
 
-use crate::{
-    schema::{DataModelType, NamedType},
-    Schema,
-};
+use crate::{schema::DataModelType, Schema};
 
 pub mod hash;
 
@@ -43,9 +40,9 @@ use defmt_v0_3 as defmt;
 pub struct Key([u8; 8]);
 
 impl Schema for Key {
-    const SCHEMA: &'static crate::schema::NamedType = &NamedType {
+    const SCHEMA: &'static crate::schema::DataModelType = &DataModelType::Struct {
         name: "Key",
-        ty: &DataModelType::NewtypeStruct(<[u8; 8] as Schema>::SCHEMA),
+        data: crate::schema::Data::Newtype(<[u8; 8] as Schema>::SCHEMA),
     };
 }
 
@@ -102,10 +99,10 @@ impl Key {
 #[cfg(feature = "use-std")]
 mod key_owned {
     use super::*;
-    use crate::schema::owned::OwnedNamedType;
+    use crate::schema::owned::OwnedDataModelType;
     impl Key {
-        /// Calculate the Key for the given path and [`OwnedNamedType`]
-        pub fn for_owned_schema_path(path: &str, nt: &OwnedNamedType) -> Key {
+        /// Calculate the Key for the given path and [`OwnedDataModelType`]
+        pub fn for_owned_schema_path(path: &str, nt: &OwnedDataModelType) -> Key {
             Key(hash::fnv1a64_owned::hash_ty_path_owned(path, nt))
         }
     }
@@ -113,53 +110,22 @@ mod key_owned {
 
 #[cfg(test)]
 mod test {
-    use crate::{
-        key::Key,
-        schema::{DataModelType, NamedType},
-        Schema,
-    };
+    use crate::{key::Key, schema::DataModelType, Schema};
 
     #[test]
     fn matches_old_postcard_rpc_defn() {
-        let old = &NamedType {
+        let old = &DataModelType::Struct {
             name: "Key",
-            ty: &DataModelType::NewtypeStruct(&NamedType {
-                name: "[T; N]",
-                ty: &DataModelType::Tuple(&[
-                    &NamedType {
-                        name: "u8",
-                        ty: &DataModelType::U8,
-                    },
-                    &NamedType {
-                        name: "u8",
-                        ty: &DataModelType::U8,
-                    },
-                    &NamedType {
-                        name: "u8",
-                        ty: &DataModelType::U8,
-                    },
-                    &NamedType {
-                        name: "u8",
-                        ty: &DataModelType::U8,
-                    },
-                    &NamedType {
-                        name: "u8",
-                        ty: &DataModelType::U8,
-                    },
-                    &NamedType {
-                        name: "u8",
-                        ty: &DataModelType::U8,
-                    },
-                    &NamedType {
-                        name: "u8",
-                        ty: &DataModelType::U8,
-                    },
-                    &NamedType {
-                        name: "u8",
-                        ty: &DataModelType::U8,
-                    },
-                ]),
-            }),
+            data: crate::schema::Data::Newtype(&DataModelType::Tuple(&[
+                &DataModelType::U8,
+                &DataModelType::U8,
+                &DataModelType::U8,
+                &DataModelType::U8,
+                &DataModelType::U8,
+                &DataModelType::U8,
+                &DataModelType::U8,
+                &DataModelType::U8,
+            ])),
         };
 
         let new = <Key as Schema>::SCHEMA;
diff --git a/source/postcard-schema/src/lib.rs b/source/postcard-schema/src/lib.rs
index e6531cf..0928b32 100644
--- a/source/postcard-schema/src/lib.rs
+++ b/source/postcard-schema/src/lib.rs
@@ -70,7 +70,7 @@ pub use postcard_derive::Schema;
 pub trait Schema {
     /// A recursive data structure that describes the schema of the given
     /// type.
-    const SCHEMA: &'static schema::NamedType;
+    const SCHEMA: &'static schema::DataModelType;
 }
 
 #[cfg(test)]
@@ -89,14 +89,14 @@ mod tests {
 
         assert_eq!(
             Point::SCHEMA,
-            &schema::NamedType {
+            &schema::DataModelType::Struct {
                 name: "Point",
-                ty: &schema::DataModelType::Struct(&[
-                    &schema::NamedValue {
+                data: schema::Data::Struct(&[
+                    &schema::NamedField {
                         name: "x",
                         ty: i32::SCHEMA
                     },
-                    &schema::NamedValue {
+                    &schema::NamedField {
                         name: "y",
                         ty: i32::SCHEMA
                     },
diff --git a/source/postcard-schema/src/schema/fmt.rs b/source/postcard-schema/src/schema/fmt.rs
index c06319a..c247046 100644
--- a/source/postcard-schema/src/schema/fmt.rs
+++ b/source/postcard-schema/src/schema/fmt.rs
@@ -6,7 +6,7 @@
 #[cfg(all(not(feature = "use-std"), feature = "alloc"))]
 extern crate alloc;
 
-use super::owned::{OwnedDataModelType, OwnedDataModelVariant, OwnedNamedType};
+use super::owned::{OwnedData, OwnedDataModelType};
 
 #[cfg(feature = "use-std")]
 use std::{string::String, vec::Vec};
@@ -35,26 +35,60 @@ pub fn is_prim(osdmty: &OwnedDataModelType) -> bool {
         OwnedDataModelType::Char => true,
         OwnedDataModelType::String => true,
         OwnedDataModelType::ByteArray => true,
-        OwnedDataModelType::Option(owned_named_type) => is_prim(&owned_named_type.ty),
+        OwnedDataModelType::Option(ty) => is_prim(ty),
         OwnedDataModelType::Unit => true,
-        OwnedDataModelType::UnitStruct => true,
-        OwnedDataModelType::NewtypeStruct(owned_named_type) => is_prim(&owned_named_type.ty),
         OwnedDataModelType::Seq(_) => false,
         OwnedDataModelType::Tuple(_) => false,
-        OwnedDataModelType::TupleStruct(vec) => vec.iter().all(|e| is_prim(&e.ty)),
-        OwnedDataModelType::Map { key, val } => is_prim(&key.ty) && is_prim(&val.ty),
-        OwnedDataModelType::Struct(_) => false,
-        OwnedDataModelType::Enum(_) => false,
+        OwnedDataModelType::Map { key, val } => is_prim(key) && is_prim(val),
+        OwnedDataModelType::Struct { .. } => false,
+        OwnedDataModelType::Enum { .. } => false,
         OwnedDataModelType::Schema => true,
     }
 }
 
-/// Format an [`OwnedNamedType`] to the given string.
+/// Format an [`OwnedDataModelType`] to the given string.
 ///
 /// Use `top_level = true` when this is a standalone type, and `top_level = false`
 /// when this type is contained within another type
-pub fn fmt_owned_nt_to_buf(ont: &OwnedNamedType, buf: &mut String, top_level: bool) {
-    match &ont.ty {
+pub fn fmt_owned_dmt_to_buf(dmt: &OwnedDataModelType, buf: &mut String, top_level: bool) {
+    let fmt_data = |data: &OwnedData, buf: &mut String| match data {
+        OwnedData::Unit => {}
+        OwnedData::Newtype(inner) => {
+            *buf += "(";
+            fmt_owned_dmt_to_buf(inner, buf, false);
+            *buf += ")";
+        }
+        OwnedData::Tuple(fields) => {
+            *buf += "(";
+            let mut fields = fields.iter();
+            if let Some(first) = fields.next() {
+                fmt_owned_dmt_to_buf(first, buf, false);
+            }
+            for field in fields {
+                *buf += ", ";
+                fmt_owned_dmt_to_buf(field, buf, false);
+            }
+            *buf += ")";
+        }
+        OwnedData::Struct(fields) => {
+            *buf += " { ";
+            let mut fields = fields.iter();
+            if let Some(first) = fields.next() {
+                *buf += &first.name;
+                *buf += ": ";
+                fmt_owned_dmt_to_buf(&first.ty, buf, false);
+            }
+            for field in fields {
+                *buf += ", ";
+                *buf += &field.name;
+                *buf += ": ";
+                fmt_owned_dmt_to_buf(&field.ty, buf, false);
+            }
+            *buf += " }";
+        }
+    };
+
+    match dmt {
         OwnedDataModelType::Bool => *buf += "bool",
         OwnedDataModelType::I8 => *buf += "i8",
         OwnedDataModelType::U8 => *buf += "u8",
@@ -73,32 +107,15 @@ pub fn fmt_owned_nt_to_buf(ont: &OwnedNamedType, buf: &mut String, top_level: bo
         OwnedDataModelType::Char => *buf += "char",
         OwnedDataModelType::String => *buf += "String",
         OwnedDataModelType::ByteArray => *buf += "[u8]",
-        OwnedDataModelType::Option(owned_named_type) => {
+        OwnedDataModelType::Option(ty) => {
             *buf += "Option<";
-            fmt_owned_nt_to_buf(owned_named_type, buf, false);
+            fmt_owned_dmt_to_buf(ty, buf, false);
             *buf += ">";
         }
         OwnedDataModelType::Unit => *buf += "()",
-        OwnedDataModelType::UnitStruct => {
-            if top_level {
-                *buf += "struct ";
-            }
-            *buf += &ont.name;
-        }
-        OwnedDataModelType::NewtypeStruct(owned_named_type) => {
-            if top_level {
-                *buf += "struct ";
-            }
-            *buf += &ont.name;
-            if top_level {
-                *buf += "(";
-                fmt_owned_nt_to_buf(owned_named_type, buf, false);
-                *buf += ")";
-            }
-        }
-        OwnedDataModelType::Seq(owned_named_type) => {
+        OwnedDataModelType::Seq(ty) => {
             *buf += "[";
-            *buf += &owned_named_type.name;
+            fmt_owned_dmt_to_buf(ty, buf, false);
             *buf += "]";
         }
         OwnedDataModelType::Tuple(vec) => {
@@ -107,7 +124,7 @@ pub fn fmt_owned_nt_to_buf(ont: &OwnedNamedType, buf: &mut String, top_level: bo
                 if vec.iter().all(|v| first == v) {
                     // This is a fixed size array
                     *buf += "[";
-                    *buf += &first.name;
+                    fmt_owned_dmt_to_buf(first, buf, false);
                     *buf += "; ";
                     *buf += &format!("{}", vec.len());
                     *buf += "]";
@@ -117,7 +134,7 @@ pub fn fmt_owned_nt_to_buf(ont: &OwnedNamedType, buf: &mut String, top_level: bo
                         .iter()
                         .map(|v| {
                             let mut buf = String::new();
-                            fmt_owned_nt_to_buf(v, &mut buf, false);
+                            fmt_owned_dmt_to_buf(v, &mut buf, false);
                             buf
                         })
                         .collect::<Vec<_>>()
@@ -129,104 +146,34 @@ pub fn fmt_owned_nt_to_buf(ont: &OwnedNamedType, buf: &mut String, top_level: bo
                 *buf += "()";
             }
         }
-        OwnedDataModelType::TupleStruct(vec) => {
-            if top_level {
-                *buf += "struct ";
-                *buf += &ont.name;
-                *buf += "(";
-                let fields = vec
-                    .iter()
-                    .map(|v| {
-                        let mut buf = String::new();
-                        fmt_owned_nt_to_buf(v, &mut buf, false);
-                        buf
-                    })
-                    .collect::<Vec<_>>()
-                    .join(", ");
-                *buf += &fields;
-                *buf += ")";
-            } else {
-                *buf += &ont.name;
-            }
-        }
         OwnedDataModelType::Map { key, val } => {
             *buf += "Map<";
-            *buf += &key.name;
+            fmt_owned_dmt_to_buf(key, buf, false);
             *buf += ", ";
-            *buf += &val.name;
+            fmt_owned_dmt_to_buf(val, buf, false);
             *buf += ">";
         }
-        OwnedDataModelType::Struct(vec) => {
+        OwnedDataModelType::Struct { name, data } => {
             if top_level {
                 *buf += "struct ";
-                *buf += &ont.name;
-                *buf += " { ";
-                let fields = vec
-                    .iter()
-                    .map(|v| {
-                        let mut buf = String::new();
-                        buf += &v.name;
-                        buf += ": ";
-                        fmt_owned_nt_to_buf(&v.ty, &mut buf, false);
-                        buf
-                    })
-                    .collect::<Vec<_>>()
-                    .join(", ");
-                *buf += &fields;
-                *buf += " }";
+                *buf += name;
+                fmt_data(data, buf);
             } else {
-                *buf += &ont.name;
+                *buf += name;
             }
         }
-        OwnedDataModelType::Enum(vec) => {
+        OwnedDataModelType::Enum { name, variants } => {
             if top_level {
                 *buf += "enum ";
-                *buf += &ont.name;
+                *buf += name;
                 *buf += " { ";
 
-                let fields = vec
+                let fields = variants
                     .iter()
                     .map(|v| {
                         let mut buf = String::new();
                         buf += &v.name;
-                        match &v.ty {
-                            OwnedDataModelVariant::UnitVariant => {}
-                            OwnedDataModelVariant::NewtypeVariant(owned_named_type) => {
-                                buf += "(";
-                                fmt_owned_nt_to_buf(owned_named_type, &mut buf, false);
-                                buf += ")";
-                            }
-                            OwnedDataModelVariant::TupleVariant(vec) => {
-                                buf += "(";
-                                let fields = vec
-                                    .iter()
-                                    .map(|ont| {
-                                        let mut buf = String::new();
-                                        fmt_owned_nt_to_buf(ont, &mut buf, false);
-                                        buf
-                                    })
-                                    .collect::<Vec<_>>()
-                                    .join(", ");
-                                buf += &fields;
-                                buf += ")";
-                            }
-                            OwnedDataModelVariant::StructVariant(vec) => {
-                                buf += "{ ";
-                                let fields = vec
-                                    .iter()
-                                    .map(|nv| {
-                                        let mut buf = String::new();
-                                        buf += &nv.name;
-                                        buf += ": ";
-                                        fmt_owned_nt_to_buf(&nv.ty, &mut buf, false);
-                                        buf
-                                    })
-                                    .collect::<Vec<_>>()
-                                    .join(", ");
-                                buf += &fields;
-                                buf += "}";
-                            }
-                        }
+                        fmt_data(&v.data, &mut buf);
                         buf
                     })
                     .collect::<Vec<_>>()
@@ -234,100 +181,80 @@ pub fn fmt_owned_nt_to_buf(ont: &OwnedNamedType, buf: &mut String, top_level: bo
                 *buf += &fields;
                 *buf += " }";
             } else {
-                *buf += &ont.name;
+                *buf += name;
             }
         }
         OwnedDataModelType::Schema => *buf += "Schema",
     }
 }
 
-/// Collect unique types mentioned by this [`OwnedNamedType`]
-#[cfg(feature = "use-std")]
-pub fn discover_tys(ont: &OwnedNamedType, set: &mut std::collections::HashSet<OwnedNamedType>) {
-    set.insert(ont.clone());
-    discover_tys_sdm(&ont.ty, set);
-}
-
 /// Collect unique types mentioned by this [`OwnedDataModelType`]
 #[cfg(feature = "use-std")]
-pub fn discover_tys_sdm(
-    sdm: &OwnedDataModelType,
-    set: &mut std::collections::HashSet<OwnedNamedType>,
+pub fn discover_tys(
+    ty: &OwnedDataModelType,
+    set: &mut std::collections::HashSet<OwnedDataModelType>,
 ) {
-    use crate::Schema;
-    match sdm {
-        OwnedDataModelType::Bool => set.insert(bool::SCHEMA.into()),
-        OwnedDataModelType::I8 => set.insert(i8::SCHEMA.into()),
-        OwnedDataModelType::U8 => set.insert(u8::SCHEMA.into()),
-        OwnedDataModelType::I16 => set.insert(i16::SCHEMA.into()),
-        OwnedDataModelType::I32 => set.insert(i32::SCHEMA.into()),
-        OwnedDataModelType::I64 => set.insert(i64::SCHEMA.into()),
-        OwnedDataModelType::I128 => set.insert(i128::SCHEMA.into()),
-        OwnedDataModelType::U16 => set.insert(u16::SCHEMA.into()),
-        OwnedDataModelType::U32 => set.insert(u32::SCHEMA.into()),
-        OwnedDataModelType::U64 => set.insert(u64::SCHEMA.into()),
-        OwnedDataModelType::U128 => set.insert(u128::SCHEMA.into()),
+    let discover_tys_data = |data: &OwnedData, set: &mut _| match data {
+        OwnedData::Unit => {}
+        OwnedData::Newtype(inner) => discover_tys(inner, set),
+        OwnedData::Tuple(elements) => {
+            for element in elements {
+                discover_tys(element, set)
+            }
+        }
+        OwnedData::Struct(fields) => {
+            for field in fields {
+                discover_tys(&field.ty, set)
+            }
+        }
+    };
+
+    set.insert(ty.clone());
+    match ty {
+        OwnedDataModelType::Bool => {}
+        OwnedDataModelType::I8 => {}
+        OwnedDataModelType::U8 => {}
+        OwnedDataModelType::I16 => {}
+        OwnedDataModelType::I32 => {}
+        OwnedDataModelType::I64 => {}
+        OwnedDataModelType::I128 => {}
+        OwnedDataModelType::U16 => {}
+        OwnedDataModelType::U32 => {}
+        OwnedDataModelType::U64 => {}
+        OwnedDataModelType::U128 => {}
 
         // TODO: usize and isize don't impl Schema, which, fair.
         OwnedDataModelType::Usize => unreachable!(),
         OwnedDataModelType::Isize => unreachable!(),
         //
-        OwnedDataModelType::F32 => set.insert(f32::SCHEMA.into()),
-        OwnedDataModelType::F64 => set.insert(f64::SCHEMA.into()),
-        OwnedDataModelType::Char => set.insert(char::SCHEMA.into()),
-        OwnedDataModelType::String => set.insert(String::SCHEMA.into()),
-        OwnedDataModelType::ByteArray => set.insert(<[u8]>::SCHEMA.into()),
-        OwnedDataModelType::Option(owned_named_type) => {
-            discover_tys(owned_named_type, set);
-            false
-        }
-        OwnedDataModelType::Unit => set.insert(<()>::SCHEMA.into()),
-        OwnedDataModelType::UnitStruct => false,
-        OwnedDataModelType::NewtypeStruct(owned_named_type) => {
-            discover_tys(owned_named_type, set);
-            false
+        OwnedDataModelType::F32 => {}
+        OwnedDataModelType::F64 => {}
+        OwnedDataModelType::Char => {}
+        OwnedDataModelType::String => {}
+        OwnedDataModelType::ByteArray => {}
+        OwnedDataModelType::Option(inner) => {
+            discover_tys(inner, set);
         }
-        OwnedDataModelType::Seq(owned_named_type) => {
-            discover_tys(owned_named_type, set);
-            false
+        OwnedDataModelType::Unit => {}
+        OwnedDataModelType::Seq(elements) => {
+            discover_tys(elements, set);
         }
-        OwnedDataModelType::Tuple(vec) | OwnedDataModelType::TupleStruct(vec) => {
+        OwnedDataModelType::Tuple(vec) => {
             for v in vec.iter() {
-                discover_tys_sdm(&v.ty, set);
+                discover_tys(v, set);
             }
-            false
         }
         OwnedDataModelType::Map { key, val } => {
             discover_tys(key, set);
             discover_tys(val, set);
-            false
         }
-        OwnedDataModelType::Struct(vec) => {
-            for v in vec.iter() {
-                discover_tys(&v.ty, set);
-            }
-            false
+        OwnedDataModelType::Struct { name: _, data } => {
+            discover_tys_data(data, set);
         }
-        OwnedDataModelType::Enum(vec) => {
-            for v in vec.iter() {
-                match &v.ty {
-                    OwnedDataModelVariant::UnitVariant => {}
-                    OwnedDataModelVariant::NewtypeVariant(owned_named_type) => {
-                        discover_tys(owned_named_type, set);
-                    }
-                    OwnedDataModelVariant::TupleVariant(vec) => {
-                        for v in vec.iter() {
-                            discover_tys(v, set);
-                        }
-                    }
-                    OwnedDataModelVariant::StructVariant(vec) => {
-                        for v in vec.iter() {
-                            discover_tys(&v.ty, set);
-                        }
-                    }
-                }
+        OwnedDataModelType::Enum { name: _, variants } => {
+            for variant in variants {
+                discover_tys_data(&variant.data, set);
             }
-            false
         }
         OwnedDataModelType::Schema => todo!(),
     };
diff --git a/source/postcard-schema/src/schema/mod.rs b/source/postcard-schema/src/schema/mod.rs
index 11cce83..15d983d 100644
--- a/source/postcard-schema/src/schema/mod.rs
+++ b/source/postcard-schema/src/schema/mod.rs
@@ -3,7 +3,7 @@
 //! The types in this module are used to define the schema of a given data type.
 //!
 //! The **Postcard Data Model** is nearly identical to the **Serde Data Model**, however Postcard also
-//! allows for one additional type, `Schema`, which maps to the [`NamedValue`] type, allowing
+//! allows for one additional type, `Schema`, which maps to the [`DataModelType`] type, allowing
 //! the schema of types to also be sent over the wire and implement the `Schema` trait.
 //!
 //! ## Borrowed vs Owned
@@ -28,25 +28,11 @@ pub mod fmt;
 
 use serde::Serialize;
 
-/// A "NamedType" is used to describe the schema of a given type.
-///
-/// It contains two pieces of information:
-///
-/// * A `name`, which is the name of the type, e.g. "u8" for [`u8`].
-/// * A `ty`, which is one of the possible [`DataModelType`]s any given type can be represented as.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
-pub struct NamedType {
-    /// The name of this type
-    pub name: &'static str,
-    /// The type
-    pub ty: &'static DataModelType,
-}
-
 /// This enum lists which of the Data Model Types apply to a given type. This describes how the
 /// type is encoded on the wire.
 ///
-/// This enum contains all Serde Data Model types other than enum variants which exist in
-/// [`DataModelVariant`], as well as a "Schema" Model Type, which maps to [`NamedType`].
+/// This enum contains all Serde Data Model types as well as a "Schema" Type,
+/// which corresponds to [`DataModelType`] itself.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
 pub enum DataModelType {
     /// The `bool` Serde Data Model Type
@@ -91,7 +77,7 @@ pub enum DataModelType {
     /// The `f32` Serde Data Model Type
     F32,
 
-    /// The `f64 Serde Data Model Type
+    /// The `f64` Serde Data Model Type
     F64,
 
     /// The `char` Serde Data Model Type
@@ -104,75 +90,78 @@ pub enum DataModelType {
     ByteArray,
 
     /// The `Option<T>` Serde Data Model Type
-    Option(&'static NamedType),
+    Option(&'static Self),
 
     /// The `()` Serde Data Model Type
     Unit,
 
-    /// The "unit struct" Serde Data Model Type
-    UnitStruct,
-
-    /// The "newtype struct" Serde Data Model Type
-    NewtypeStruct(&'static NamedType),
-
     /// The "Sequence" Serde Data Model Type
-    Seq(&'static NamedType),
+    Seq(&'static Self),
 
     /// The "Tuple" Serde Data Model Type
-    Tuple(&'static [&'static NamedType]),
-
-    /// The "Tuple Struct" Serde Data Model Type
-    TupleStruct(&'static [&'static NamedType]),
+    Tuple(&'static [&'static Self]),
 
     /// The "Map" Serde Data Model Type
     Map {
         /// The map "Key" type
-        key: &'static NamedType,
+        key: &'static Self,
         /// The map "Value" type
-        val: &'static NamedType,
+        val: &'static Self,
     },
 
-    /// The "Struct" Serde Data Model Type
-    Struct(&'static [&'static NamedValue]),
+    /// One of the struct Serde Data Model types
+    Struct {
+        /// The name of this struct
+        name: &'static str,
+        /// The data contained in this struct
+        data: Data,
+    },
 
     /// The "Enum" Serde Data Model Type (which contains any of the "Variant" types)
-    Enum(&'static [&'static NamedVariant]),
+    Enum {
+        /// The name of this struct
+        name: &'static str,
+        /// The variants contained in this enum
+        variants: &'static [&'static Variant],
+    },
 
-    /// A NamedType/OwnedNamedType
+    /// A [`DataModelType`]/[`OwnedDataModelType`](owned::OwnedDataModelType)
     Schema,
 }
 
-/// This is similar to [`DataModelType`], however it only contains the potential Data Model Types
-/// used as variants of an `enum`.
+/// The contents of a struct or enum variant.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
-pub enum DataModelVariant {
-    /// The "unit variant" Serde Data Model Type
-    UnitVariant,
-    /// The "newtype variant" Serde Data Model Type
-    NewtypeVariant(&'static NamedType),
-    /// The "Tuple Variant" Serde Data Model Type
-    TupleVariant(&'static [&'static NamedType]),
-    /// The "Struct Variant" Serde Data Model Type
-    StructVariant(&'static [&'static NamedValue]),
+pub enum Data {
+    /// The "Unit Struct" or "Unit Variant" Serde Data Model Type
+    Unit,
+
+    /// The "Newtype Struct" or "Newtype Variant" Serde Data Model Type
+    Newtype(&'static DataModelType),
+
+    /// The "Tuple Struct" or "Tuple Variant" Serde Data Model Type
+    Tuple(&'static [&'static DataModelType]),
+
+    /// The "Struct" or "Struct Variant" Serde Data Model Type
+    Struct(&'static [&'static NamedField]),
 }
 
 /// This represents a named struct field.
 ///
 /// For example, in `struct Ex { a: u32 }` the field `a` would be reflected as
-/// `NamedValue { name: "a", ty: DataModelType::U32 }`.
+/// `NamedField { name: "a", ty: DataModelType::U32 }`.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
-pub struct NamedValue {
-    /// The name of this value
+pub struct NamedField {
+    /// The name of this field
     pub name: &'static str,
-    /// The type of this value
-    pub ty: &'static NamedType,
+    /// The type of this field
+    pub ty: &'static DataModelType,
 }
 
-/// An enum variant with a name, e.g. `T::Bar(...)`
+/// An enum variant e.g. `T::Bar(...)`
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
-pub struct NamedVariant {
+pub struct Variant {
     /// The name of this variant
     pub name: &'static str,
-    /// The type of this variant
-    pub ty: &'static DataModelVariant,
+    /// The data contained in this variant
+    pub data: Data,
 }
diff --git a/source/postcard-schema/src/schema/owned.rs b/source/postcard-schema/src/schema/owned.rs
index b25ff1e..978ff94 100644
--- a/source/postcard-schema/src/schema/owned.rs
+++ b/source/postcard-schema/src/schema/owned.rs
@@ -1,70 +1,45 @@
 //! Owned Schema version
 
-use super::{DataModelType, DataModelVariant, NamedType, NamedValue, NamedVariant};
+use super::{Data, DataModelType, NamedField, Variant};
 use serde::{Deserialize, Serialize};
 
 #[cfg(all(not(feature = "use-std"), feature = "alloc"))]
 extern crate alloc;
 
 #[cfg(feature = "use-std")]
-use std::{boxed::Box, collections::HashSet, string::String, vec::Vec};
+use std::{boxed::Box, collections::HashSet, string::String};
 
 #[cfg(all(not(feature = "use-std"), feature = "alloc"))]
-use alloc::{
-    boxed::Box,
-    string::{String, ToString},
-    vec::Vec,
-};
+use alloc::{boxed::Box, string::String};
 
 // ---
 
-/// The owned version of [`NamedType`]
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-pub struct OwnedNamedType {
-    /// The name of this type
-    pub name: String,
-    /// The type
-    pub ty: OwnedDataModelType,
-}
-
-impl OwnedNamedType {
-    /// Convert an [OwnedNamedType] to a pseudo-Rust type format
+impl OwnedDataModelType {
+    /// Convert an `[OwnedDataModelType]` to a pseudo-Rust type format
     pub fn to_pseudocode(&self) -> String {
         let mut buf = String::new();
-        super::fmt::fmt_owned_nt_to_buf(self, &mut buf, true);
+        super::fmt::fmt_owned_dmt_to_buf(self, &mut buf, true);
         buf
     }
 
     /// Collect all types used recursively by this type
     #[cfg(feature = "use-std")]
-    pub fn all_used_types(&self) -> HashSet<OwnedNamedType> {
+    pub fn all_used_types(&self) -> HashSet<Self> {
         let mut buf = HashSet::new();
         super::fmt::discover_tys(self, &mut buf);
         buf
     }
 }
 
-impl core::fmt::Display for OwnedNamedType {
+impl core::fmt::Display for OwnedDataModelType {
     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
         let pc = self.to_pseudocode();
         f.write_str(&pc)
     }
 }
 
-impl From<&NamedType> for OwnedNamedType {
-    fn from(value: &NamedType) -> Self {
-        Self {
-            name: value.name.to_string(),
-            ty: value.ty.into(),
-        }
-    }
-}
-
-impl crate::Schema for OwnedNamedType {
-    const SCHEMA: &'static NamedType = &NamedType {
-        name: "OwnedNamedType",
-        ty: &DataModelType::Schema,
-    };
+impl crate::Schema for OwnedDataModelType {
+    const SCHEMA: &'static DataModelType = &DataModelType::Schema;
 }
 
 // ---
@@ -127,41 +102,42 @@ pub enum OwnedDataModelType {
     ByteArray,
 
     /// The `Option<T>` Serde Data Model Type
-    Option(Box<OwnedNamedType>),
+    Option(Box<Self>),
 
     /// The `()` Serde Data Model Type
     Unit,
 
-    /// The "unit struct" Serde Data Model Type
-    UnitStruct,
-
-    /// The "newtype struct" Serde Data Model Type
-    NewtypeStruct(Box<OwnedNamedType>),
-
     /// The "Sequence" Serde Data Model Type
-    Seq(Box<OwnedNamedType>),
+    Seq(Box<Self>),
 
     /// The "Tuple" Serde Data Model Type
-    Tuple(Vec<OwnedNamedType>),
-
-    /// The "Tuple Struct" Serde Data Model Type
-    TupleStruct(Vec<OwnedNamedType>),
+    Tuple(Box<[Self]>),
 
     /// The "Map" Serde Data Model Type
     Map {
         /// The map "Key" type
-        key: Box<OwnedNamedType>,
+        key: Box<Self>,
         /// The map "Value" type
-        val: Box<OwnedNamedType>,
+        val: Box<Self>,
     },
 
-    /// The "Struct" Serde Data Model Type
-    Struct(Vec<OwnedNamedValue>),
+    /// One of the struct Serde Data Model types
+    Struct {
+        /// The name of this struct
+        name: Box<str>,
+        /// The data contained in this struct
+        data: OwnedData,
+    },
 
     /// The "Enum" Serde Data Model Type (which contains any of the "Variant" types)
-    Enum(Vec<OwnedNamedVariant>),
+    Enum {
+        /// The name of this struct
+        name: Box<str>,
+        /// The variants contained in this enum
+        variants: Box<[OwnedVariant]>,
+    },
 
-    /// A NamedType/OwnedNamedType
+    /// A [`DataModelType`]/[`OwnedDataModelType`]
     Schema,
 }
 
@@ -188,19 +164,20 @@ impl From<&DataModelType> for OwnedDataModelType {
             DataModelType::ByteArray => Self::ByteArray,
             DataModelType::Option(o) => Self::Option(Box::new((*o).into())),
             DataModelType::Unit => Self::Unit,
-            DataModelType::UnitStruct => Self::UnitStruct,
-            DataModelType::NewtypeStruct(nts) => Self::NewtypeStruct(Box::new((*nts).into())),
             DataModelType::Seq(s) => Self::Seq(Box::new((*s).into())),
             DataModelType::Tuple(t) => Self::Tuple(t.iter().map(|i| (*i).into()).collect()),
-            DataModelType::TupleStruct(ts) => {
-                Self::TupleStruct(ts.iter().map(|i| (*i).into()).collect())
-            }
             DataModelType::Map { key, val } => Self::Map {
                 key: Box::new((*key).into()),
                 val: Box::new((*val).into()),
             },
-            DataModelType::Struct(s) => Self::Struct(s.iter().map(|i| (*i).into()).collect()),
-            DataModelType::Enum(e) => Self::Enum(e.iter().map(|i| (*i).into()).collect()),
+            DataModelType::Struct { name, data } => Self::Struct {
+                name: (*name).into(),
+                data: data.into(),
+            },
+            DataModelType::Enum { name, variants } => Self::Enum {
+                name: (*name).into(),
+                variants: variants.iter().map(|i| (*i).into()).collect(),
+            },
             DataModelType::Schema => Self::Schema,
         }
     }
@@ -208,49 +185,48 @@ impl From<&DataModelType> for OwnedDataModelType {
 
 // ---
 
-/// The owned version of [`DataModelVariant`]
+/// The owned version of [`Data`].
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-pub enum OwnedDataModelVariant {
-    /// The "unit variant" Serde Data Model Type
-    UnitVariant,
-    /// The "newtype variant" Serde Data Model Type
-    NewtypeVariant(Box<OwnedNamedType>),
-    /// The "Tuple Variant" Serde Data Model Type
-    TupleVariant(Vec<OwnedNamedType>),
-    /// The "Struct Variant" Serde Data Model Type
-    StructVariant(Vec<OwnedNamedValue>),
+pub enum OwnedData {
+    /// The "Unit Struct" or "Unit Variant" Serde Data Model Type
+    Unit,
+
+    /// The "Newtype Struct" or "Newtype Variant" Serde Data Model Type
+    Newtype(Box<OwnedDataModelType>),
+
+    /// The "Tuple Struct" or "Tuple Variant" Serde Data Model Type
+    Tuple(Box<[OwnedDataModelType]>),
+
+    /// The "Struct" or "Struct Variant" Serde Data Model Type
+    Struct(Box<[OwnedNamedField]>),
 }
 
-impl From<&DataModelVariant> for OwnedDataModelVariant {
-    fn from(value: &DataModelVariant) -> Self {
-        match value {
-            DataModelVariant::UnitVariant => Self::UnitVariant,
-            DataModelVariant::NewtypeVariant(d) => Self::NewtypeVariant(Box::new((*d).into())),
-            DataModelVariant::TupleVariant(d) => {
-                Self::TupleVariant(d.iter().map(|i| (*i).into()).collect())
-            }
-            DataModelVariant::StructVariant(d) => {
-                Self::StructVariant(d.iter().map(|i| (*i).into()).collect())
-            }
+impl From<&Data> for OwnedData {
+    fn from(data: &Data) -> Self {
+        match data {
+            Data::Unit => Self::Unit,
+            Data::Newtype(d) => Self::Newtype(Box::new((*d).into())),
+            Data::Tuple(d) => Self::Tuple(d.iter().map(|i| (*i).into()).collect()),
+            Data::Struct(d) => Self::Struct(d.iter().map(|i| (*i).into()).collect()),
         }
     }
 }
 
 // ---
 
-/// The owned version of [`NamedValue`]
+/// The owned version of [`NamedField`]
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-pub struct OwnedNamedValue {
+pub struct OwnedNamedField {
     /// The name of this value
-    pub name: String,
+    pub name: Box<str>,
     /// The type of this value
-    pub ty: OwnedNamedType,
+    pub ty: OwnedDataModelType,
 }
 
-impl From<&NamedValue> for OwnedNamedValue {
-    fn from(value: &NamedValue) -> Self {
+impl From<&NamedField> for OwnedNamedField {
+    fn from(value: &NamedField) -> Self {
         Self {
-            name: value.name.to_string(),
+            name: value.name.into(),
             ty: value.ty.into(),
         }
     }
@@ -258,20 +234,20 @@ impl From<&NamedValue> for OwnedNamedValue {
 
 // ---
 
-/// The owned version of [`NamedVariant`]
+/// The owned version of [`Variant`]
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-pub struct OwnedNamedVariant {
+pub struct OwnedVariant {
     /// The name of this variant
-    pub name: String,
-    /// The type of this variant
-    pub ty: OwnedDataModelVariant,
+    pub name: Box<str>,
+    /// The data contained in this variant
+    pub data: OwnedData,
 }
 
-impl From<&NamedVariant> for OwnedNamedVariant {
-    fn from(value: &NamedVariant) -> Self {
+impl From<&Variant> for OwnedVariant {
+    fn from(value: &Variant) -> Self {
         Self {
-            name: value.name.to_string(),
-            ty: value.ty.into(),
+            name: value.name.into(),
+            data: (&value.data).into(),
         }
     }
 }


Test patch:
diff --git a/source/postcard-schema/tests/schema.rs b/source/postcard-schema/tests/schema.rs
index c300bb0..ddf42e3 100644
--- a/source/postcard-schema/tests/schema.rs
+++ b/source/postcard-schema/tests/schema.rs
@@ -1,33 +1,9 @@
 use postcard_schema::{
-    schema::{
-        owned::OwnedNamedType, DataModelType, DataModelVariant, NamedType, NamedValue, NamedVariant,
-    },
+    schema::{owned::OwnedDataModelType, Data, DataModelType, NamedField, Variant},
     Schema,
 };
 use std::path::PathBuf;
 
-const U8_SCHEMA: NamedType = NamedType {
-    name: "u8",
-    ty: &DataModelType::U8,
-};
-const U32_SCHEMA: NamedType = NamedType {
-    name: "u32",
-    ty: &DataModelType::U32,
-};
-const U64_SCHEMA: NamedType = NamedType {
-    name: "u64",
-    ty: &DataModelType::U64,
-};
-
-const I16_SCHEMA: NamedType = NamedType {
-    name: "i16",
-    ty: &DataModelType::I16,
-};
-const I32_SCHEMA: NamedType = NamedType {
-    name: "i32",
-    ty: &DataModelType::I32,
-};
-
 #[allow(unused)]
 #[derive(Schema)]
 enum Inner {
@@ -78,45 +54,39 @@ mod bound {
 #[test]
 fn test_enum_serialize() {
     assert_eq!(
-        &NamedType {
+        &DataModelType::Enum {
             name: "Inner",
-            ty: &DataModelType::Enum(&[
-                &NamedVariant {
+            variants: &[
+                &Variant {
                     name: "Alpha",
-                    ty: &DataModelVariant::UnitVariant
+                    data: Data::Unit
                 },
-                &NamedVariant {
+                &Variant {
                     name: "Beta",
-                    ty: &DataModelVariant::UnitVariant
+                    data: Data::Unit
                 },
-                &NamedVariant {
+                &Variant {
                     name: "Gamma",
-                    ty: &DataModelVariant::UnitVariant
+                    data: Data::Unit
                 },
-                &NamedVariant {
+                &Variant {
                     name: "Delta",
-                    ty: &DataModelVariant::TupleVariant(&[&I32_SCHEMA, &I16_SCHEMA,])
+                    data: Data::Tuple(&[i32::SCHEMA, i16::SCHEMA,])
                 },
-                &NamedVariant {
+                &Variant {
                     name: "Epsilon",
-                    ty: &DataModelVariant::StructVariant(&[
-                        &NamedValue {
+                    data: Data::Struct(&[
+                        &NamedField {
                             name: "zeta",
-                            ty: &NamedType {
-                                name: "f32",
-                                ty: &DataModelType::F32
-                            },
+                            ty: f32::SCHEMA,
                         },
-                        &NamedValue {
+                        &NamedField {
                             name: "eta",
-                            ty: &NamedType {
-                                name: "bool",
-                                ty: &DataModelType::Bool
-                            },
+                            ty: bool::SCHEMA,
                         }
                     ]),
                 }
-            ]),
+            ],
         },
         Inner::SCHEMA
     );
@@ -124,45 +94,34 @@ fn test_enum_serialize() {
 
 #[test]
 fn test_struct_serialize() {
-    const TEN_BYTES_SCHEMA: &[&NamedType] = &[&U8_SCHEMA; 10];
-
     assert_eq!(
         Outer::SCHEMA,
-        &NamedType {
+        &DataModelType::Struct {
             name: "Outer",
-            ty: &DataModelType::Struct(&[
-                &NamedValue {
+            data: Data::Struct(&[
+                &NamedField {
                     name: "a",
-                    ty: &U32_SCHEMA
+                    ty: u32::SCHEMA
                 },
-                &NamedValue {
+                &NamedField {
                     name: "b",
-                    ty: &U64_SCHEMA
+                    ty: u64::SCHEMA
                 },
-                &NamedValue {
+                &NamedField {
                     name: "c",
-                    ty: &U8_SCHEMA
+                    ty: u8::SCHEMA
                 },
-                &NamedValue {
+                &NamedField {
                     name: "d",
                     ty: Inner::SCHEMA
                 },
-                &NamedValue {
+                &NamedField {
                     name: "e",
-                    ty: &NamedType {
-                        name: "[T; N]",
-                        ty: &DataModelType::Tuple(TEN_BYTES_SCHEMA),
-                    }
+                    ty: &DataModelType::Tuple(&[u8::SCHEMA; 10]),
                 },
-                &NamedValue {
+                &NamedField {
                     name: "f",
-                    ty: &NamedType {
-                        name: "[T]",
-                        ty: &DataModelType::Seq(&NamedType {
-                            name: "u8",
-                            ty: &DataModelType::U8
-                        })
-                    }
+                    ty: &DataModelType::Seq(u8::SCHEMA),
                 },
             ]),
         }
@@ -172,15 +131,12 @@ fn test_struct_serialize() {
 #[test]
 fn test_slice_serialize() {
     assert_eq!(
-        &NamedType {
+        &DataModelType::Struct {
             name: "Slice",
-            ty: &DataModelType::Struct(&[&NamedValue {
+            data: Data::Struct(&[&NamedField {
                 name: "x",
-                ty: &NamedType {
-                    name: "[T]",
-                    ty: &DataModelType::Seq(&U8_SCHEMA)
-                }
-            },]),
+                ty: &DataModelType::Seq(u8::SCHEMA),
+            }]),
         },
         Slice::SCHEMA
     );
@@ -189,11 +145,11 @@ fn test_slice_serialize() {
 #[test]
 fn test_bound_serialize() {
     assert_eq!(
-        &NamedType {
+        &DataModelType::Struct {
             name: "Bound",
-            ty: &DataModelType::Struct(&[&NamedValue {
+            data: Data::Struct(&[&NamedField {
                 name: "x",
-                ty: &U8_SCHEMA
+                ty: u8::SCHEMA
             }]),
         },
         Bound::<bound::Id>::SCHEMA,
@@ -230,7 +186,7 @@ struct TestStruct2<'a> {
 #[test]
 fn owned_punning() {
     let borrowed_schema = TestStruct2::SCHEMA;
-    let owned_schema: OwnedNamedType = borrowed_schema.into();
+    let owned_schema: OwnedDataModelType = borrowed_schema.into();
 
     // Check that they are the same on the wire when serialized
     let ser_borrowed_schema = postcard::to_stdvec(borrowed_schema).unwrap();
@@ -239,12 +195,12 @@ fn owned_punning() {
 
     // TODO: This is wildly repetitive, and likely could benefit from interning of
     // repeated types, strings, etc.
-    assert_eq!(ser_borrowed_schema.len(), 268);
+    assert_eq!(ser_borrowed_schema.len(), 187);
 
     // 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();
+        postcard::from_bytes::<OwnedDataModelType>(&ser_borrowed_schema).unwrap();
+    let deser_owned_schema = postcard::from_bytes::<OwnedDataModelType>(&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);
@@ -269,34 +225,34 @@ enum TestEnum2 {
 fn newtype_vs_tuple() {
     assert_eq!(
         TestStruct3::SCHEMA,
-        &NamedType {
+        &DataModelType::Struct {
             name: "TestStruct3",
-            ty: &DataModelType::NewtypeStruct(u64::SCHEMA)
+            data: Data::Newtype(u64::SCHEMA)
         }
     );
 
     assert_eq!(
         TestStruct4::SCHEMA,
-        &NamedType {
+        &DataModelType::Struct {
             name: "TestStruct4",
-            ty: &DataModelType::TupleStruct(&[u64::SCHEMA, bool::SCHEMA]),
+            data: Data::Tuple(&[u64::SCHEMA, bool::SCHEMA]),
         }
     );
 
     assert_eq!(
         TestEnum2::SCHEMA,
-        &NamedType {
+        &DataModelType::Enum {
             name: "TestEnum2",
-            ty: &DataModelType::Enum(&[
-                &NamedVariant {
+            variants: &[
+                &Variant {
                     name: "Nt",
-                    ty: &DataModelVariant::NewtypeVariant(u64::SCHEMA)
+                    data: Data::Newtype(u64::SCHEMA)
                 },
-                &NamedVariant {
+                &Variant {
                     name: "Tup",
-                    ty: &DataModelVariant::TupleVariant(&[u64::SCHEMA, bool::SCHEMA])
+                    data: Data::Tuple(&[u64::SCHEMA, bool::SCHEMA])
                 },
-            ]),
+            ],
         }
     );
 }
@@ -304,7 +260,7 @@ fn newtype_vs_tuple() {
 // Formatting
 
 fn dewit<T: Schema>() -> String {
-    let schema: OwnedNamedType = T::SCHEMA.into();
+    let schema: OwnedDataModelType = T::SCHEMA.into();
     schema.to_pseudocode()
 }