diff --git a/Cargo.lock b/Cargo.lock index c2d03fb6b56..0f114e2b518 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5251,6 +5251,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-type-name" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f94190a20388d5e7477532030a7b3d0077b473431d785b20bef0edadca55a5f" +dependencies = [ + "proc-macro2", +] + [[package]] name = "proc-macro2" version = "1.0.101" @@ -7081,6 +7090,7 @@ checksum = "9a930242493f5c875ab96903eb40fb6a90c4d3ae99597fd51da569ff22769a03" dependencies = [ "heck 0.4.1", "humantime", + "proc-macro-type-name", "proc-macro2", "quote", "spacetimedb-primitives 1.6.0", diff --git a/crates/bindings-macro/Cargo.toml b/crates/bindings-macro/Cargo.toml index 9e0094df23a..d17d1280783 100644 --- a/crates/bindings-macro/Cargo.toml +++ b/crates/bindings-macro/Cargo.toml @@ -19,6 +19,7 @@ proc-macro2.workspace = true quote.workspace = true syn.workspace = true heck.workspace = true +proc-macro-type-name = "0.1.0" [lints] workspace = true diff --git a/crates/bindings-macro/src/sats.rs b/crates/bindings-macro/src/sats.rs index 8bad39da837..8f5553eb57b 100644 --- a/crates/bindings-macro/src/sats.rs +++ b/crates/bindings-macro/src/sats.rs @@ -380,8 +380,9 @@ pub(crate) fn derive_deserialize(ty: &SatsType<'_>) -> TokenStream { let n_fields = fields.len(); - let field_names = fields.iter().map(|f| f.ident.unwrap()).collect::>(); - let field_strings = fields.iter().map(|f| f.name.as_deref().unwrap()).collect::>(); + let field_members = fields.iter().enumerate().map(|(ii, f)| f.ident.map_or_else(|| ii.into(), |v| v.clone().into())).collect::>(); + let field_names = fields.iter().enumerate().map(|(ii, f)| f.ident.map_or_else(|| syn::Ident::new(("_".to_owned() + &ii.to_string()).as_ref(), Span::call_site()), |v| v.clone())).collect::>(); + let field_strings = fields.iter().enumerate().map(|(ii, f)| f.name.clone().unwrap_or_else(|| ii.to_string())).collect::>(); let field_types = fields.iter().map(|f| &f.ty); let field_types2 = field_types.clone(); quote! { @@ -414,7 +415,7 @@ pub(crate) fn derive_deserialize(ty: &SatsType<'_>) -> TokenStream { fn visit_seq_product>(self, mut tup: A) -> Result { Ok(#name { - #(#field_names: + #(#field_members: tup.next_element::<#field_types>()? .ok_or_else(|| #spacetimedb_lib::de::Error::invalid_product_length(#iter_n, &self))?,)* }) @@ -434,7 +435,7 @@ pub(crate) fn derive_deserialize(ty: &SatsType<'_>) -> TokenStream { } } Ok(#name { - #(#field_names: + #(#field_members: #field_names.ok_or_else(|| #spacetimedb_lib::de::Error::missing_field(#iter_n3, Some(#field_strings), &self))?,)* }) } @@ -595,13 +596,13 @@ pub(crate) fn derive_serialize(ty: &SatsType) -> TokenStream { }); } - let fieldnames = fields.iter().map(|field| field.ident.unwrap()); - let tys = fields.iter().map(|f| &f.ty); - let fieldnamestrings = fields.iter().map(|field| field.name.as_ref().unwrap()); + let field_members = fields.iter().enumerate().map(|(ii, f)| f.ident.map_or_else(|| ii.into(), |v| v.clone().into())).collect::>(); + let field_strings = fields.iter().enumerate().map(|(ii, f)| f.name.clone().unwrap_or_else(|| ii.to_string())).collect::>(); + let field_types = fields.iter().map(|f| &f.ty); let nfields = fields.len(); quote! { let mut __prod = __serializer.serialize_named_product(#nfields)?; - #(#spacetimedb_lib::ser::SerializeNamedProduct::serialize_element::<#tys>(&mut __prod, Some(#fieldnamestrings), &self.#fieldnames)?;)* + #(#spacetimedb_lib::ser::SerializeNamedProduct::serialize_element::<#field_types>(&mut __prod, Some(#field_strings), &self.#field_members)?;)* #spacetimedb_lib::ser::SerializeNamedProduct::end(__prod) } } diff --git a/crates/bindings-macro/src/table.rs b/crates/bindings-macro/src/table.rs index 2748f195e64..013c6545918 100644 --- a/crates/bindings-macro/src/table.rs +++ b/crates/bindings-macro/src/table.rs @@ -3,6 +3,7 @@ use crate::sym; use crate::util::{check_duplicate, check_duplicate_msg, ident_to_litstr, match_meta}; use core::slice; use heck::ToSnakeCase; +use proc_macro_type_name::ToTypeName; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; use std::borrow::Cow; @@ -794,6 +795,9 @@ pub(crate) fn table_impl(mut args: TableArgs, item: &syn::DeriveInput) -> syn::R } }; + let pk_type = primary_key_column.clone().map(|x| x.ty); + let pk_type_ident = primary_key_column.clone().map(|x| Ident::new(&(original_struct_ident.to_string() + &(&x.ident).to_type_ident(x.ident.span()).to_string()), x.ident.span())); + let (schedule, schedule_typecheck) = args .scheduled .as_ref() @@ -900,6 +904,18 @@ pub(crate) fn table_impl(mut args: TableArgs, item: &syn::DeriveInput) -> syn::R }; // Output all macro data + + let trait_def_pk = pk_type.map(|pk_type| quote_spanned! {table_ident.span()=> + #[allow(dead_code)] + #[derive(SpacetimeType)] + struct #pk_type_ident(#pk_type); + impl std::borrow::Borrow<#pk_type> for #pk_type_ident { + fn borrow(&self) -> &#pk_type { + &self.0 + } + } + }); + let trait_def = quote_spanned! {table_ident.span()=> #[allow(non_camel_case_types, dead_code)] #vis trait #table_ident { @@ -944,6 +960,7 @@ pub(crate) fn table_impl(mut args: TableArgs, item: &syn::DeriveInput) -> syn::R #default_type_check }; + #trait_def_pk #trait_def #trait_def_view diff --git a/crates/sats/src/ser/impls.rs b/crates/sats/src/ser/impls.rs index 9baac393dff..49851e5ba31 100644 --- a/crates/sats/src/ser/impls.rs +++ b/crates/sats/src/ser/impls.rs @@ -102,8 +102,8 @@ impl Serialize for u8 { impl_serialize!([] F32, (self, ser) => f32::from(*self).serialize(ser)); impl_serialize!([] F64, (self, ser) => f64::from(*self).serialize(ser)); -impl_serialize!([T: Serialize] Vec, (self, ser) => (**self).serialize(ser)); -impl_serialize!([T: Serialize, const N: usize] SmallVec<[T; N]>, (self, ser) => (**self).serialize(ser)); +impl_serialize!([T: Serialize] Vec, (self, ser) => (**self).serialize(ser)); +impl_serialize!([T: Serialize, const N: usize] SmallVec<[T; N]>, (self, ser) => (**self).serialize(ser)); impl_serialize!([T: Serialize] [T], (self, ser) => T::__serialize_array(self, ser)); impl_serialize!([T: Serialize, const N: usize] [T; N], (self, ser) => T::__serialize_array(self, ser)); impl_serialize!([T: Serialize + ?Sized] Box, (self, ser) => (**self).serialize(ser)); diff --git a/crates/sats/src/typespace.rs b/crates/sats/src/typespace.rs index f7d0978d660..b879da60df1 100644 --- a/crates/sats/src/typespace.rs +++ b/crates/sats/src/typespace.rs @@ -399,9 +399,10 @@ impl_primitives! { String => String, } -impl_st!([](), AlgebraicType::unit()); +impl_st!([] (), AlgebraicType::unit()); impl_st!([] str, AlgebraicType::String); impl_st!([T] [T], ts => AlgebraicType::array(T::make_type(ts))); +impl_st!([T, const N: usize] [T; N], ts => AlgebraicType::array(T::make_type(ts))); impl_st!([T: ?Sized] &T, ts => T::make_type(ts)); impl_st!([T: ?Sized] Box, ts => T::make_type(ts)); impl_st!([T: ?Sized] Rc, ts => T::make_type(ts)); @@ -410,6 +411,9 @@ impl_st!([T] Vec, ts => <[T]>::make_type(ts)); impl_st!([T, const N: usize] SmallVec<[T; N]>, ts => <[T]>::make_type(ts)); impl_st!([T] Option, ts => AlgebraicType::option(T::make_type(ts))); +impl_st!([U] (U,), ts => AlgebraicType::product([U::make_type(ts)])); +impl_st!([U, V: SpacetimeType] (U, V), ts => AlgebraicType::product([U::make_type(ts), V::make_type(ts)])); + impl_st!([] spacetimedb_primitives::ArgId, AlgebraicType::U64); impl_st!([] spacetimedb_primitives::ColId, AlgebraicType::U16); impl_st!([] spacetimedb_primitives::TableId, AlgebraicType::U32);