From 1746101b27c765cb98e29db96521d3b3ae3791d9 Mon Sep 17 00:00:00 2001 From: indirection42 Date: Thu, 27 Jun 2024 09:14:05 +0800 Subject: [PATCH 01/10] add basic types --- xcq-types/derive/Cargo.toml | 12 ++++++++++++ xcq-types/derive/src/lib.rs | 14 ++++++++++++++ xcq-types/src/lib.rs | 16 ++++------------ xcq-types/src/ty/composite.rs | 12 ++++++++++++ xcq-types/src/ty/field.rs | 6 ++++++ xcq-types/src/ty/mod.rs | 12 ++++++++++++ xcq-types/src/ty/primitive.rs | 13 +++++++++++++ xcq-types/src/ty/variant.rs | 11 +++++++++++ 8 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 xcq-types/derive/Cargo.toml create mode 100644 xcq-types/derive/src/lib.rs create mode 100644 xcq-types/src/ty/composite.rs create mode 100644 xcq-types/src/ty/field.rs create mode 100644 xcq-types/src/ty/mod.rs create mode 100644 xcq-types/src/ty/primitive.rs create mode 100644 xcq-types/src/ty/variant.rs diff --git a/xcq-types/derive/Cargo.toml b/xcq-types/derive/Cargo.toml new file mode 100644 index 0000000..018c71a --- /dev/null +++ b/xcq-types/derive/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "derive" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0" +syn = { version = "2.0", features = ["full"] } +proc-macro2 = "1.0" diff --git a/xcq-types/derive/src/lib.rs b/xcq-types/derive/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/xcq-types/derive/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/xcq-types/src/lib.rs b/xcq-types/src/lib.rs index 58456cf..1efa498 100644 --- a/xcq-types/src/lib.rs +++ b/xcq-types/src/lib.rs @@ -1,16 +1,8 @@ #![cfg_attr(not(feature = "std"), no_std)] -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; +mod ty; +pub use ty::*; - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } +pub trait TypeInfo { + fn type_info() -> Type; } diff --git a/xcq-types/src/ty/composite.rs b/xcq-types/src/ty/composite.rs new file mode 100644 index 0000000..a807838 --- /dev/null +++ b/xcq-types/src/ty/composite.rs @@ -0,0 +1,12 @@ +use super::{Field, Type}; +// A Rust struct with named fields. +pub struct StructType { + ident: Vec, + fields: Fields, +} + +pub enum Fields { + Named(Vec), + Unnamed(Vec), + Unit, +} diff --git a/xcq-types/src/ty/field.rs b/xcq-types/src/ty/field.rs new file mode 100644 index 0000000..36b7349 --- /dev/null +++ b/xcq-types/src/ty/field.rs @@ -0,0 +1,6 @@ +use super::Type; +// A field of a struct or enum variant. +pub struct Field { + ident: Vec, + ty: Type, +} diff --git a/xcq-types/src/ty/mod.rs b/xcq-types/src/ty/mod.rs new file mode 100644 index 0000000..8a13586 --- /dev/null +++ b/xcq-types/src/ty/mod.rs @@ -0,0 +1,12 @@ +mod composite; +mod field; +mod primitive; +mod variant; + +pub use self::{composite::*, field::*, primitive::*, variant::*}; +pub enum Type { + Primitive(PrimitiveType), + Struct(StructType), + Enum(EnumType), +} + diff --git a/xcq-types/src/ty/primitive.rs b/xcq-types/src/ty/primitive.rs new file mode 100644 index 0000000..f3db120 --- /dev/null +++ b/xcq-types/src/ty/primitive.rs @@ -0,0 +1,13 @@ +pub enum PrimitiveType { + /// Unsigned Integer up to 128 bits + Unsigned, + /// Signed Integer up to 128 bits + Signed, + U256, + I256, + Bool, + /// Vec + Bytes, + /// [u8; 32] + H256, +} diff --git a/xcq-types/src/ty/variant.rs b/xcq-types/src/ty/variant.rs new file mode 100644 index 0000000..738f484 --- /dev/null +++ b/xcq-types/src/ty/variant.rs @@ -0,0 +1,11 @@ +use super::Fields; + +pub struct Variant { + ident: Vec, + fields: Fields, +} + +pub struct EnumType { + ident: Vec, + variants: Vec, +} From 3d0ceb3cbd1309a54592798e14323821bd3248e6 Mon Sep 17 00:00:00 2001 From: indirection42 Date: Thu, 4 Jul 2024 22:18:50 +0800 Subject: [PATCH 02/10] add basic meta type system support --- Cargo.lock | 27 +++++ Cargo.toml | 6 + Makefile | 3 + xcq-extension/procedural/Cargo.toml | 6 +- xcq-types/Cargo.toml | 11 +- xcq-types/derive/Cargo.toml | 8 +- xcq-types/derive/src/lib.rs | 132 +++++++++++++++++++-- xcq-types/src/impls.rs | 174 ++++++++++++++++++++++++++++ xcq-types/src/lib.rs | 19 ++- xcq-types/src/prelude.rs | 53 +++++++++ xcq-types/src/tests.rs | 85 ++++++++++++++ xcq-types/src/ty/composite.rs | 12 -- xcq-types/src/ty/enum.rs | 47 ++++++++ xcq-types/src/ty/field.rs | 9 +- xcq-types/src/ty/mod.rs | 14 ++- xcq-types/src/ty/primitive.rs | 28 +++-- xcq-types/src/ty/struct.rs | 51 ++++++++ xcq-types/src/ty/variant.rs | 11 -- xcq-types/tests/Cargo.toml | 8 ++ xcq-types/tests/src/lib.rs | 25 ++++ 20 files changed, 671 insertions(+), 58 deletions(-) create mode 100644 xcq-types/src/impls.rs create mode 100644 xcq-types/src/prelude.rs create mode 100644 xcq-types/src/tests.rs delete mode 100644 xcq-types/src/ty/composite.rs create mode 100644 xcq-types/src/ty/enum.rs create mode 100644 xcq-types/src/ty/struct.rs delete mode 100644 xcq-types/src/ty/variant.rs create mode 100644 xcq-types/tests/Cargo.toml create mode 100644 xcq-types/tests/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index f9c3a97..666b599 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1263,6 +1263,17 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fortuples" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87630a8087e9cac4b7edfb6ee5e250ddca9112b57b6b17d8f5107375a3a8eace" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "frame-benchmarking" version = "33.0.0" @@ -5639,6 +5650,22 @@ dependencies = [ [[package]] name = "xcq-types" version = "0.1.0" +dependencies = [ + "cfg-if", + "fortuples", + "parity-scale-codec", + "serde", + "xcq-types-derive", +] + +[[package]] +name = "xcq-types-derive" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] [[package]] name = "yoke" diff --git a/Cargo.toml b/Cargo.toml index 387a449..a000bf0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,3 +65,9 @@ tracing = { version = "0.1.40", default-features = false } clap = { version = "4.5.4", features = ["derive"] } env_logger = { version = "0.11.3" } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } + + +# proc macros +syn = { version = "2", features = ["full", "extra-traits"] } +quote = "1" +proc-macro2 = "1" diff --git a/Makefile b/Makefile index 2e4f6a3..1f71d5f 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,9 @@ clippy: SKIP_WASM_BUILD= cargo clippy -- -D warnings cd poc/guests; cargo clippy +test: + SKIP_WASM_BUILD= cargo test + chainspec: cargo build -p poc-runtime --release mkdir -p output diff --git a/xcq-extension/procedural/Cargo.toml b/xcq-extension/procedural/Cargo.toml index a278247..822a392 100644 --- a/xcq-extension/procedural/Cargo.toml +++ b/xcq-extension/procedural/Cargo.toml @@ -10,7 +10,7 @@ version.workspace = true proc-macro = true [dependencies] -syn = { version = "2", features = ["full", "extra-traits"] } -quote = "1" -proc-macro2 = "1" +quote = { workspace = true } +syn = { workspace = true } +proc-macro2 = { workspace = true } twox-hash = "1.6.3" diff --git a/xcq-types/Cargo.toml b/xcq-types/Cargo.toml index 75b312a..db9d07c 100644 --- a/xcq-types/Cargo.toml +++ b/xcq-types/Cargo.toml @@ -8,7 +8,16 @@ repository.workspace = true version.workspace = true [dependencies] +cfg-if = "1.0" +codec = { package = "parity-scale-codec", version = "3", default-features = false, features = [ + "derive", +] } +serde = { version = "1", default-features = false, optional = true, features = [ + "derive", +] } +fortuples = "0.9" +xcq-types-derive = { path = "derive" } [features] default = ["std"] -std = [] +std = ["codec/std"] diff --git a/xcq-types/derive/Cargo.toml b/xcq-types/derive/Cargo.toml index 018c71a..3eb51d2 100644 --- a/xcq-types/derive/Cargo.toml +++ b/xcq-types/derive/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "derive" +name = "xcq-types-derive" version = "0.1.0" edition = "2021" @@ -7,6 +7,6 @@ edition = "2021" proc-macro = true [dependencies] -quote = "1.0" -syn = { version = "2.0", features = ["full"] } -proc-macro2 = "1.0" +quote = { workspace = true } +syn = { workspace = true } +proc-macro2 = { workspace = true } diff --git a/xcq-types/derive/src/lib.rs b/xcq-types/derive/src/lib.rs index 7d12d9a..a8607f2 100644 --- a/xcq-types/derive/src/lib.rs +++ b/xcq-types/derive/src/lib.rs @@ -1,14 +1,128 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use syn::{punctuated::Punctuated, token::Comma, Data, DataEnum, DataStruct, DeriveInput, Field, Fields}; + +#[proc_macro_derive(XcqTypeInfo)] +pub fn derive_xcq_type_info(input: TokenStream) -> TokenStream { + match generate(input.into()) { + Ok(output) => output.into(), + Err(err) => err.to_compile_error().into(), + } } -#[cfg(test)] -mod tests { - use super::*; +fn generate(input: TokenStream2) -> syn::Result { + let type_info_impl: XcqTypeInfoImpl = XcqTypeInfoImpl::parse(input)?; + let type_info_impl_toks = type_info_impl.expand()?; + Ok(quote! { + // A rust pattern to ensure that the type info is implemented. + #[allow(non_upper_case_globals, unused_attributes, unused_imports)] + const _: () = { + #type_info_impl_toks + }; + }) +} + +struct XcqTypeInfoImpl { + ast: DeriveInput, +} + +impl XcqTypeInfoImpl { + fn parse(input: TokenStream2) -> syn::Result { + let ast = syn::parse2::(input)?; + // Assume no attributes + if !ast.attrs.is_empty() { + return Err(syn::Error::new_spanned(ast, "unexpected attributes")); + } + Ok(Self { ast }) + } + + fn expand(&self) -> syn::Result { + let ident = &self.ast.ident; + + // Assume no generics + if self.ast.generics.type_params().next().is_some() { + return Err(syn::Error::new_spanned( + &self.ast.generics, + "generics are not supported", + )); + } + + let type_info = match &self.ast.data { + Data::Struct(ref s) => self.generate_struct_type(s), + Data::Enum(ref e) => self.generate_enum_type(e), + Data::Union(_) => { + return Err(syn::Error::new_spanned(&self.ast, "unions are not supported")); + } + }; + + // TODO: No token replacement supported yet + Ok(quote! { + impl xcq_types::XcqTypeInfo for #ident { + type Identity = Self; + fn type_info() -> xcq_types::XcqType { + #type_info + } + } + }) + } + + fn generate_struct_type(&self, data_struct: &DataStruct) -> TokenStream2 { + let ident = &self.ast.ident; + let fields = match data_struct.fields { + Fields::Named(ref fields) => self.generate_fields(&fields.named), + Fields::Unnamed(ref fields) => self.generate_fields(&fields.unnamed), + Fields::Unit => return quote! {}, + }; + + quote! { + xcq_types::StructType { + ident: stringify!(#ident).as_bytes().to_vec(), + fields: vec![#(#fields),*], + }.into() + } + } + + fn generate_enum_type(&self, data_enum: &DataEnum) -> TokenStream2 { + let ident = &self.ast.ident; + let variants = data_enum.variants.iter().map(|variant| { + let ident = &variant.ident; + let fields = match variant.fields { + Fields::Named(ref fields) => self.generate_fields(&fields.named), + Fields::Unnamed(ref fields) => self.generate_fields(&fields.unnamed), + Fields::Unit => return quote! {}, + }; + quote! { + xcq_types::Variant { + ident: stringify!(#ident).as_bytes().to_vec(), + fields: vec![#(#fields),*], + } + } + }); + quote! { + xcq_types::EnumType { + ident: stringify!(#ident).as_bytes().to_vec(), + variants: vec![#(#variants),*], + }.into() + } + } - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + fn generate_fields(&self, fields: &Punctuated) -> Vec { + fields + .iter() + .map(|f| { + let ty = &f.ty; + let ident_toks = match &f.ident { + Some(ident) => quote! { stringify!(#ident).as_bytes().to_vec()}, + None => quote! { vec![] }, + }; + quote! { + xcq_types::Field { + ident: #ident_toks, + ty: <#ty as xcq_types::XcqTypeInfo>::type_info(), + } + } + }) + .collect() } } diff --git a/xcq-types/src/impls.rs b/xcq-types/src/impls.rs new file mode 100644 index 0000000..d32a0dc --- /dev/null +++ b/xcq-types/src/impls.rs @@ -0,0 +1,174 @@ +use crate::{boxed::Box, rc::Rc, sync::Arc}; +use crate::{EnumType, Field, PrimitiveType, StructType, Variant}; +use crate::{XcqType, XcqTypeInfo}; +use fortuples::fortuples; + +macro_rules! impl_from_xcqtype_variant { + ($($from:ty => $variant: ident,)*) => { + $( + impl From<$from> for XcqType { + fn from(x: $from) -> Self { + Self::$variant(x) + } + } + )* + }; +} + +impl_from_xcqtype_variant! { + StructType => Struct, + EnumType => Enum, + PrimitiveType => Primitive, +} + +// Implement `XcqTypeInfo` for primitive types. +macro_rules! impl_metadata_for_primitives { + ( $($t:ty=> $ident_kind:expr ),*) => { + $( + impl XcqTypeInfo for $t { + type Identity = Self; + fn type_info() -> XcqType { + $ident_kind.into() + } + } + )* + } +} +impl_metadata_for_primitives! { + bool => PrimitiveType::Bool, + u8 => PrimitiveType::U8, + u16 => PrimitiveType::U16, + u32 => PrimitiveType::U32, + u64 => PrimitiveType::U64, + u128 => PrimitiveType::U128, + i8 => PrimitiveType::I8, + i16 => PrimitiveType::I16, + i32 => PrimitiveType::I32, + i64 => PrimitiveType::I64, + i128 => PrimitiveType::I128, + [u8;32] => PrimitiveType::H256 +} + +fortuples! { + impl XcqTypeInfo for #Tuple where #(#Member: XcqTypeInfo+'static),* { + type Identity = Self; + fn type_info() -> XcqType { + XcqType::Tuple(vec![ #(#Member::type_info()),* ]) + } + } +} + +impl XcqTypeInfo for Option +where + T: XcqTypeInfo + 'static, +{ + type Identity = Self; + fn type_info() -> XcqType { + EnumType { + ident: "Option".as_bytes().to_vec(), + variants: vec![ + Variant { + ident: "None".as_bytes().to_vec(), + fields: vec![], + }, + Variant { + ident: "Some".as_bytes().to_vec(), + fields: vec![Field { + ident: vec![], + ty: T::type_info(), + }], + }, + ], + } + .into() + } +} + +impl XcqTypeInfo for Result +where + T: XcqTypeInfo + 'static, + E: XcqTypeInfo + 'static, +{ + type Identity = Self; + fn type_info() -> XcqType { + EnumType { + ident: "Result".as_bytes().to_vec(), + variants: vec![ + Variant { + ident: "Ok".as_bytes().to_vec(), + fields: vec![Field { + ident: vec![], + ty: T::type_info(), + }], + }, + Variant { + ident: "Err".as_bytes().to_vec(), + fields: vec![Field { + ident: vec![], + ty: E::type_info(), + }], + }, + ], + } + .into() + } +} + +macro_rules! impl_metadata_for_smart_pointers { + ($($type:ty),*) => { + $( + impl XcqTypeInfo for $type + where + T: XcqTypeInfo + ?Sized + 'static, + { + type Identity = T; + fn type_info() -> XcqType { + Self::Identity::type_info() + } + } + )* + }; +} + +impl_metadata_for_smart_pointers! { + Box, + Arc, + Rc, + &T, + &mut T +} + +impl XcqTypeInfo for [T] +where + T: XcqTypeInfo + 'static, +{ + type Identity = Self; + fn type_info() -> XcqType { + XcqType::Sequence(Box::new(T::type_info())) + } +} + +impl XcqTypeInfo for Vec +where + T: XcqTypeInfo + 'static, +{ + type Identity = [T]; + fn type_info() -> XcqType { + Self::Identity::type_info() + } +} + +impl XcqTypeInfo for str { + type Identity = str; + fn type_info() -> XcqType { + XcqType::Sequence(Box::new(PrimitiveType::U8.into())) + } +} + +impl XcqTypeInfo for String { + type Identity = str; + fn type_info() -> XcqType { + Self::Identity::type_info() + } +} +// No impl for PhantomData, codec::Compact, Range, RangeInclusive, str, BTreeMap, BTreeSet, BinaryHeap, VecDeque yet. diff --git a/xcq-types/src/lib.rs b/xcq-types/src/lib.rs index 1efa498..efd49ee 100644 --- a/xcq-types/src/lib.rs +++ b/xcq-types/src/lib.rs @@ -2,7 +2,22 @@ mod ty; pub use ty::*; +mod impls; +mod prelude; +pub use prelude::*; +#[cfg(test)] +mod tests; -pub trait TypeInfo { - fn type_info() -> Type; +pub use xcq_types_derive::XcqTypeInfo; + +/// Implementors return the meta type information. +pub trait XcqTypeInfo { + /// This is used to uniquely identify the type via [`core::any::TypeId::of`] + /// In most case it is Self, but for reference types it is the type of the reference. + type Identity: ?Sized + 'static; + fn type_info() -> XcqType; } +/// helper trait for combining `XcqTypeInfo` and `'static +pub trait XcqStaticTypeInfo: XcqTypeInfo + 'static {} + +impl XcqStaticTypeInfo for T where T: XcqTypeInfo + 'static {} diff --git a/xcq-types/src/prelude.rs b/xcq-types/src/prelude.rs new file mode 100644 index 0000000..71fbd7f --- /dev/null +++ b/xcq-types/src/prelude.rs @@ -0,0 +1,53 @@ +#[cfg(not(feature = "std"))] +extern crate alloc; + +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(feature = "std")] { + pub use std::{ + any, + borrow, + boxed, + cmp, + collections, + fmt, + format, + hash, + marker, + mem, + num, + ops, + string, + sync, + time, + vec, + rc, + iter, + }; + } else { + pub use alloc::{ + borrow, + boxed, + collections, + format, + string, + sync, + vec, + rc + }; + + pub use core::{ + any, + cmp, + fmt, + hash, + marker, + mem, + num, + ops, + time, + iter, + }; + } +} diff --git a/xcq-types/src/tests.rs b/xcq-types/src/tests.rs new file mode 100644 index 0000000..78e31d4 --- /dev/null +++ b/xcq-types/src/tests.rs @@ -0,0 +1,85 @@ +use crate::{EnumType, Field, Variant}; +use crate::{PrimitiveType, XcqType, XcqTypeInfo}; + +fn assert_eq(expect: XcqType) { + assert_eq!(T::type_info(), expect); +} + +macro_rules! assert_type { + ($ty:ty, $expected: expr) => {{ + assert_eq::<$ty>($expected); + }}; +} + +#[test] +fn primitives() { + assert_type!(bool, XcqType::Primitive(PrimitiveType::Bool)); + assert_type!(&str, XcqType::Sequence(Box::new(PrimitiveType::U8.into()))); + assert_type!(i8, XcqType::Primitive(PrimitiveType::I8)); + assert_type!([bool], XcqType::Sequence(Box::new(PrimitiveType::Bool.into()))); +} + +#[test] +fn prelude_items() { + assert_type!(String, XcqType::Sequence(Box::new(PrimitiveType::U8.into()))); + + assert_type!( + Option, + XcqType::Enum(EnumType { + ident: "Option".as_bytes().to_vec(), + variants: vec![ + Variant { + ident: "None".as_bytes().to_vec(), + fields: vec![], + }, + Variant { + ident: "Some".as_bytes().to_vec(), + fields: vec![Field { + ident: vec![], + ty: PrimitiveType::U128.into(), + }], + }, + ], + }) + ); + + assert_type!( + Result, + XcqType::Enum(EnumType { + ident: "Result".as_bytes().to_vec(), + variants: vec![ + Variant { + ident: "Ok".as_bytes().to_vec(), + fields: vec![Field { + ident: vec![], + ty: PrimitiveType::Bool.into(), + }], + }, + Variant { + ident: "Err".as_bytes().to_vec(), + fields: vec![Field { + ident: vec![], + ty: XcqType::Sequence(Box::new(PrimitiveType::U8.into())), + }], + }, + ], + }) + ); +} + +#[test] +fn tuple_primitives() { + assert_type!((), XcqType::Tuple(vec![])); + assert_type!((bool,), XcqType::Tuple(vec![PrimitiveType::Bool.into()])); + assert_type!( + (bool, i8), + XcqType::Tuple(vec![PrimitiveType::Bool.into(), PrimitiveType::I8.into()]) + ); + assert_type!( + ((i8, i16), (u32, u64)), + XcqType::Tuple(vec![ + XcqType::Tuple(vec![PrimitiveType::I8.into(), PrimitiveType::I16.into()]), + XcqType::Tuple(vec![PrimitiveType::U32.into(), PrimitiveType::U64.into()]), + ]) + ); +} diff --git a/xcq-types/src/ty/composite.rs b/xcq-types/src/ty/composite.rs deleted file mode 100644 index a807838..0000000 --- a/xcq-types/src/ty/composite.rs +++ /dev/null @@ -1,12 +0,0 @@ -use super::{Field, Type}; -// A Rust struct with named fields. -pub struct StructType { - ident: Vec, - fields: Fields, -} - -pub enum Fields { - Named(Vec), - Unnamed(Vec), - Unit, -} diff --git a/xcq-types/src/ty/enum.rs b/xcq-types/src/ty/enum.rs new file mode 100644 index 0000000..4271aa7 --- /dev/null +++ b/xcq-types/src/ty/enum.rs @@ -0,0 +1,47 @@ +use crate::Field; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] +pub struct Variant { + pub ident: Vec, + pub fields: Vec, +} + +/// A Enum type, consisting of variants +/// +/// # Example +/// +/// ## A Rust enum with unit +/// ``` +/// enum MyEnum { +/// RustAllowsUnitVariants, +/// AndAlsoForTupleStructs(i32, bool), +/// OrNamedStructs { name: String, age: u8 }, +/// } +/// ``` +/// gives +/// ```ignore +/// EnumType { +/// ident: "MyEnum".encode(), +/// variants: vec![ +/// Variant{ident:"RustAllowsUnitVariants".encode(),vec![]}, +/// Variant{ident:"AndAlsoForTupleStructs".encode(),vec![Field { ident: vec![], ty: XcqType::Primitive(PrimitiveType::Signed) }, Field { ident: vec![], ty: XcqType::Primitive(PrimitiveType::Bool) }]} +/// Variant{ident:"OrNamedStructs".encode(),vec![Field { ident: "name".encode(), ty: XcqType::Primitive(PrimitiveType::String) }, Field { ident: "age".encode(), ty: XcqType::Primitive(PrimitiveType::Unsigned) }]} +/// ] +/// } +/// ``` +/// +/// ## A C-like enum type +/// +/// ``` +/// enum Color { +/// Red, +/// Green, +/// Blue, +/// } +/// ``` +/// +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] +pub struct EnumType { + pub ident: Vec, + pub variants: Vec, +} diff --git a/xcq-types/src/ty/field.rs b/xcq-types/src/ty/field.rs index 36b7349..e2a8158 100644 --- a/xcq-types/src/ty/field.rs +++ b/xcq-types/src/ty/field.rs @@ -1,6 +1,7 @@ -use super::Type; -// A field of a struct or enum variant. +use crate::XcqType; +// A Named or Unnamed field in a composite type +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] pub struct Field { - ident: Vec, - ty: Type, + pub ident: Vec, + pub ty: XcqType, } diff --git a/xcq-types/src/ty/mod.rs b/xcq-types/src/ty/mod.rs index 8a13586..c6bd045 100644 --- a/xcq-types/src/ty/mod.rs +++ b/xcq-types/src/ty/mod.rs @@ -1,12 +1,16 @@ -mod composite; +mod r#enum; mod field; mod primitive; -mod variant; +mod r#struct; -pub use self::{composite::*, field::*, primitive::*, variant::*}; -pub enum Type { +pub use self::{field::*, primitive::*, r#enum::*, r#struct::*}; + +/// Note: no Array Type yet +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] +pub enum XcqType { Primitive(PrimitiveType), Struct(StructType), Enum(EnumType), + Tuple(Vec), + Sequence(Box), } - diff --git a/xcq-types/src/ty/primitive.rs b/xcq-types/src/ty/primitive.rs index f3db120..5a4623d 100644 --- a/xcq-types/src/ty/primitive.rs +++ b/xcq-types/src/ty/primitive.rs @@ -1,13 +1,27 @@ +use crate::XcqType; +use codec::Encode; + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Debug)] pub enum PrimitiveType { - /// Unsigned Integer up to 128 bits - Unsigned, - /// Signed Integer up to 128 bits - Signed, + Bool, + Char, + U8, + U16, + U32, + U64, + U128, + I8, + I16, + I32, + I64, + I128, + // TODO: representation U256, + // TODO: representation I256, - Bool, - /// Vec - Bytes, /// [u8; 32] H256, + // TODO: more fixed-size arrays represented as primitives } diff --git a/xcq-types/src/ty/struct.rs b/xcq-types/src/ty/struct.rs new file mode 100644 index 0000000..d39ceca --- /dev/null +++ b/xcq-types/src/ty/struct.rs @@ -0,0 +1,51 @@ +use crate::prelude::vec::Vec; +use crate::{Field, XcqType}; + +/// A struct type, consisting of a named (struct) or unnamed (tuple struct) fields or unit struct. +/// Note: in fact, it can represent a +/// +/// # Example +/// +/// ## A Rust struct with named fields. +/// +/// ``` +/// struct Person { +/// name: String, +/// age_in_years: u8, +/// friends: Vec, +/// } +/// ``` +/// gives +/// ```ignore +/// StructType { +/// ident: "Person".encode(), +/// fields: vec![Field { ident: "name".encode(), ty: ... }, Field { ident: "age_in_years".encode(), ty: ... }, Field { ident: "friends".encode(), ty: ... }], +/// } +/// ``` +/// +/// ## A tuple struct with unnamed fields. +/// +/// ``` +/// struct Color(u8, u8, u8); +/// ``` +/// gives +/// ```ignore +/// StructType { +/// ident: "Color".encode(), +/// fields: vec![Field { ident: vec![], ty: XcqType::Primitive(PrimitiveType::Unsigned) }, Field { ident: vec![],encode(), ty: XcqType::Primitive(PrimitiveType::Unsigned) }, Field { ident: vec![], ty: XcqType::Primitive(PrimitiveType::Unsigned) }], +/// ``` +/// +/// ## A so-called unit struct +/// +/// ``` +/// struct JustAMarker; +/// ``` +/// gives +/// ```ignore +/// StructType { ident: "JustAMarker".encode(), fields: vec![] } +/// ``` +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] +pub struct StructType { + ident: Vec, + fields: Vec, +} diff --git a/xcq-types/src/ty/variant.rs b/xcq-types/src/ty/variant.rs deleted file mode 100644 index 738f484..0000000 --- a/xcq-types/src/ty/variant.rs +++ /dev/null @@ -1,11 +0,0 @@ -use super::Fields; - -pub struct Variant { - ident: Vec, - fields: Fields, -} - -pub struct EnumType { - ident: Vec, - variants: Vec, -} diff --git a/xcq-types/tests/Cargo.toml b/xcq-types/tests/Cargo.toml new file mode 100644 index 0000000..67e7a8f --- /dev/null +++ b/xcq-types/tests/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "xcq-types-derive-test" +version = "0.1.0" +edition = "2021" +authors.workspace = true + +[dependencies] +xcq-types = { path = ".." } diff --git a/xcq-types/tests/src/lib.rs b/xcq-types/tests/src/lib.rs new file mode 100644 index 0000000..0187347 --- /dev/null +++ b/xcq-types/tests/src/lib.rs @@ -0,0 +1,25 @@ +use xcq_types::XcqType; +#[derive(XcqTypeInfo)] +struct Person { + name: String, + age_in_years: u8, +} +#[test] +fn struct_types() { + assert_type!( + Person, + XcqType::Struct(StructType { + ident: "Person".as_bytes().to_vec(), + fields: vec![ + Field { + ident: "name".as_bytes().to_vec(), + ty: XcqType::Sequence(Box::new(XcqType::Primitive(Unsigned))), + }, + Field { + ident: "age_in_years".as_bytes().to_vec(), + ty: PrimitiveType::U8.into(), + }, + ], + }) + ); +} From 88dbeba4616a2a896d3f7a3cb82d92babf744631 Mon Sep 17 00:00:00 2001 From: indirection42 Date: Thu, 4 Jul 2024 22:26:07 +0800 Subject: [PATCH 03/10] clippy fix --- xcq-types/src/ty/enum.rs | 11 ----------- xcq-types/src/ty/mod.rs | 2 ++ xcq-types/src/ty/primitive.rs | 1 - xcq-types/src/ty/struct.rs | 19 +------------------ 4 files changed, 3 insertions(+), 30 deletions(-) diff --git a/xcq-types/src/ty/enum.rs b/xcq-types/src/ty/enum.rs index 4271aa7..ea32aa0 100644 --- a/xcq-types/src/ty/enum.rs +++ b/xcq-types/src/ty/enum.rs @@ -18,17 +18,6 @@ pub struct Variant { /// OrNamedStructs { name: String, age: u8 }, /// } /// ``` -/// gives -/// ```ignore -/// EnumType { -/// ident: "MyEnum".encode(), -/// variants: vec![ -/// Variant{ident:"RustAllowsUnitVariants".encode(),vec![]}, -/// Variant{ident:"AndAlsoForTupleStructs".encode(),vec![Field { ident: vec![], ty: XcqType::Primitive(PrimitiveType::Signed) }, Field { ident: vec![], ty: XcqType::Primitive(PrimitiveType::Bool) }]} -/// Variant{ident:"OrNamedStructs".encode(),vec![Field { ident: "name".encode(), ty: XcqType::Primitive(PrimitiveType::String) }, Field { ident: "age".encode(), ty: XcqType::Primitive(PrimitiveType::Unsigned) }]} -/// ] -/// } -/// ``` /// /// ## A C-like enum type /// diff --git a/xcq-types/src/ty/mod.rs b/xcq-types/src/ty/mod.rs index c6bd045..d4aed9d 100644 --- a/xcq-types/src/ty/mod.rs +++ b/xcq-types/src/ty/mod.rs @@ -11,6 +11,8 @@ pub enum XcqType { Primitive(PrimitiveType), Struct(StructType), Enum(EnumType), + // TODO: deal with self-referential types Tuple(Vec), + // TODO: deal with self-referential types Sequence(Box), } diff --git a/xcq-types/src/ty/primitive.rs b/xcq-types/src/ty/primitive.rs index 5a4623d..1a9d839 100644 --- a/xcq-types/src/ty/primitive.rs +++ b/xcq-types/src/ty/primitive.rs @@ -1,4 +1,3 @@ -use crate::XcqType; use codec::Encode; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/xcq-types/src/ty/struct.rs b/xcq-types/src/ty/struct.rs index d39ceca..e21886b 100644 --- a/xcq-types/src/ty/struct.rs +++ b/xcq-types/src/ty/struct.rs @@ -1,5 +1,5 @@ use crate::prelude::vec::Vec; -use crate::{Field, XcqType}; +use crate::Field; /// A struct type, consisting of a named (struct) or unnamed (tuple struct) fields or unit struct. /// Note: in fact, it can represent a @@ -15,35 +15,18 @@ use crate::{Field, XcqType}; /// friends: Vec, /// } /// ``` -/// gives -/// ```ignore -/// StructType { -/// ident: "Person".encode(), -/// fields: vec![Field { ident: "name".encode(), ty: ... }, Field { ident: "age_in_years".encode(), ty: ... }, Field { ident: "friends".encode(), ty: ... }], -/// } -/// ``` /// /// ## A tuple struct with unnamed fields. /// /// ``` /// struct Color(u8, u8, u8); /// ``` -/// gives -/// ```ignore -/// StructType { -/// ident: "Color".encode(), -/// fields: vec![Field { ident: vec![], ty: XcqType::Primitive(PrimitiveType::Unsigned) }, Field { ident: vec![],encode(), ty: XcqType::Primitive(PrimitiveType::Unsigned) }, Field { ident: vec![], ty: XcqType::Primitive(PrimitiveType::Unsigned) }], -/// ``` /// /// ## A so-called unit struct /// /// ``` /// struct JustAMarker; /// ``` -/// gives -/// ```ignore -/// StructType { ident: "JustAMarker".encode(), fields: vec![] } -/// ``` #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] pub struct StructType { ident: Vec, From 93d67be73d5341d2edcf6fee09ed08675095f019 Mon Sep 17 00:00:00 2001 From: indirection42 Date: Fri, 5 Jul 2024 09:58:39 +0800 Subject: [PATCH 04/10] fix no_std --- xcq-types/src/impls.rs | 2 +- xcq-types/src/lib.rs | 4 ++++ xcq-types/src/prelude.rs | 2 -- xcq-types/src/ty/enum.rs | 2 +- xcq-types/src/ty/field.rs | 1 + xcq-types/src/ty/mod.rs | 1 + xcq-types/src/ty/struct.rs | 3 +-- 7 files changed, 9 insertions(+), 6 deletions(-) diff --git a/xcq-types/src/impls.rs b/xcq-types/src/impls.rs index d32a0dc..e04cbf8 100644 --- a/xcq-types/src/impls.rs +++ b/xcq-types/src/impls.rs @@ -1,4 +1,4 @@ -use crate::{boxed::Box, rc::Rc, sync::Arc}; +use crate::{boxed::Box, rc::Rc, string::String, sync::Arc, vec::Vec}; use crate::{EnumType, Field, PrimitiveType, StructType, Variant}; use crate::{XcqType, XcqTypeInfo}; use fortuples::fortuples; diff --git a/xcq-types/src/lib.rs b/xcq-types/src/lib.rs index efd49ee..c79d395 100644 --- a/xcq-types/src/lib.rs +++ b/xcq-types/src/lib.rs @@ -1,5 +1,9 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(not(feature = "std"))] +#[macro_use] +extern crate alloc; + mod ty; pub use ty::*; mod impls; diff --git a/xcq-types/src/prelude.rs b/xcq-types/src/prelude.rs index 71fbd7f..d7702f1 100644 --- a/xcq-types/src/prelude.rs +++ b/xcq-types/src/prelude.rs @@ -1,5 +1,3 @@ -#[cfg(not(feature = "std"))] -extern crate alloc; use cfg_if::cfg_if; diff --git a/xcq-types/src/ty/enum.rs b/xcq-types/src/ty/enum.rs index ea32aa0..7c51a0b 100644 --- a/xcq-types/src/ty/enum.rs +++ b/xcq-types/src/ty/enum.rs @@ -1,4 +1,4 @@ -use crate::Field; +use crate::{vec::Vec, Field}; #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] pub struct Variant { diff --git a/xcq-types/src/ty/field.rs b/xcq-types/src/ty/field.rs index e2a8158..a280122 100644 --- a/xcq-types/src/ty/field.rs +++ b/xcq-types/src/ty/field.rs @@ -1,3 +1,4 @@ +use crate::vec::Vec; use crate::XcqType; // A Named or Unnamed field in a composite type #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] diff --git a/xcq-types/src/ty/mod.rs b/xcq-types/src/ty/mod.rs index d4aed9d..ad7aaf9 100644 --- a/xcq-types/src/ty/mod.rs +++ b/xcq-types/src/ty/mod.rs @@ -2,6 +2,7 @@ mod r#enum; mod field; mod primitive; mod r#struct; +use crate::{boxed::Box, vec::Vec}; pub use self::{field::*, primitive::*, r#enum::*, r#struct::*}; diff --git a/xcq-types/src/ty/struct.rs b/xcq-types/src/ty/struct.rs index e21886b..3fb5ead 100644 --- a/xcq-types/src/ty/struct.rs +++ b/xcq-types/src/ty/struct.rs @@ -1,5 +1,4 @@ -use crate::prelude::vec::Vec; -use crate::Field; +use crate::{vec::Vec, Field}; /// A struct type, consisting of a named (struct) or unnamed (tuple struct) fields or unit struct. /// Note: in fact, it can represent a From 2ce83e4725d8a16ad7059d7320e815fb95dc233b Mon Sep 17 00:00:00 2001 From: indirection42 Date: Fri, 5 Jul 2024 10:13:09 +0800 Subject: [PATCH 05/10] format --- xcq-types/src/prelude.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/xcq-types/src/prelude.rs b/xcq-types/src/prelude.rs index d7702f1..dcb6824 100644 --- a/xcq-types/src/prelude.rs +++ b/xcq-types/src/prelude.rs @@ -1,4 +1,3 @@ - use cfg_if::cfg_if; cfg_if! { From 8dfc8edf4c28a69c23fcfe6fbcce81eb96669b39 Mon Sep 17 00:00:00 2001 From: indirection42 Date: Fri, 5 Jul 2024 12:22:56 +0800 Subject: [PATCH 06/10] macros to be fixed --- xcq-types/src/lib.rs | 2 -- xcq-types/src/ty/struct.rs | 4 ++-- xcq-types/tests/Cargo.toml | 8 -------- xcq-types/tests/{src/lib.rs => derive.rs} | 6 ++++-- xcq-types/tests/macros.rs | 11 +++++++++++ xcq-types/tests/main.rs | 4 ++++ xcq-types/{src/tests.rs => tests/prelude.rs} | 15 +++------------ 7 files changed, 24 insertions(+), 26 deletions(-) delete mode 100644 xcq-types/tests/Cargo.toml rename xcq-types/tests/{src/lib.rs => derive.rs} (82%) create mode 100644 xcq-types/tests/macros.rs create mode 100644 xcq-types/tests/main.rs rename xcq-types/{src/tests.rs => tests/prelude.rs} (88%) diff --git a/xcq-types/src/lib.rs b/xcq-types/src/lib.rs index c79d395..e41706e 100644 --- a/xcq-types/src/lib.rs +++ b/xcq-types/src/lib.rs @@ -9,8 +9,6 @@ pub use ty::*; mod impls; mod prelude; pub use prelude::*; -#[cfg(test)] -mod tests; pub use xcq_types_derive::XcqTypeInfo; diff --git a/xcq-types/src/ty/struct.rs b/xcq-types/src/ty/struct.rs index 3fb5ead..98128a5 100644 --- a/xcq-types/src/ty/struct.rs +++ b/xcq-types/src/ty/struct.rs @@ -28,6 +28,6 @@ use crate::{vec::Vec, Field}; /// ``` #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] pub struct StructType { - ident: Vec, - fields: Vec, + pub ident: Vec, + pub fields: Vec, } diff --git a/xcq-types/tests/Cargo.toml b/xcq-types/tests/Cargo.toml deleted file mode 100644 index 67e7a8f..0000000 --- a/xcq-types/tests/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "xcq-types-derive-test" -version = "0.1.0" -edition = "2021" -authors.workspace = true - -[dependencies] -xcq-types = { path = ".." } diff --git a/xcq-types/tests/src/lib.rs b/xcq-types/tests/derive.rs similarity index 82% rename from xcq-types/tests/src/lib.rs rename to xcq-types/tests/derive.rs index 0187347..07331ee 100644 --- a/xcq-types/tests/src/lib.rs +++ b/xcq-types/tests/derive.rs @@ -1,4 +1,6 @@ -use xcq_types::XcqType; +use crate::assert_type; +use xcq_types::{Field, PrimitiveType, StructType, XcqType, XcqTypeInfo}; + #[derive(XcqTypeInfo)] struct Person { name: String, @@ -13,7 +15,7 @@ fn struct_types() { fields: vec![ Field { ident: "name".as_bytes().to_vec(), - ty: XcqType::Sequence(Box::new(XcqType::Primitive(Unsigned))), + ty: XcqType::Sequence(Box::new(XcqType::Primitive(PrimitiveType::U8.into()))), }, Field { ident: "age_in_years".as_bytes().to_vec(), diff --git a/xcq-types/tests/macros.rs b/xcq-types/tests/macros.rs new file mode 100644 index 0000000..8823986 --- /dev/null +++ b/xcq-types/tests/macros.rs @@ -0,0 +1,11 @@ +use xcq_types::{XcqType, XcqTypeInfo}; +pub fn assert_type_fn(expect: XcqType) { + assert_eq!(T::type_info(), expect); +} + +#[macro_export] +macro_rules! assert_type { + ($ty:ty, $expected: expr) => {{ + $crate::macros::assert_type_fn::<$ty>($expected); + }}; +} diff --git a/xcq-types/tests/main.rs b/xcq-types/tests/main.rs new file mode 100644 index 0000000..69a42ef --- /dev/null +++ b/xcq-types/tests/main.rs @@ -0,0 +1,4 @@ +#[macro_use] +mod macros; +mod derive; +mod prelude; diff --git a/xcq-types/src/tests.rs b/xcq-types/tests/prelude.rs similarity index 88% rename from xcq-types/src/tests.rs rename to xcq-types/tests/prelude.rs index 78e31d4..a7e2a1d 100644 --- a/xcq-types/src/tests.rs +++ b/xcq-types/tests/prelude.rs @@ -1,15 +1,6 @@ -use crate::{EnumType, Field, Variant}; -use crate::{PrimitiveType, XcqType, XcqTypeInfo}; - -fn assert_eq(expect: XcqType) { - assert_eq!(T::type_info(), expect); -} - -macro_rules! assert_type { - ($ty:ty, $expected: expr) => {{ - assert_eq::<$ty>($expected); - }}; -} +use crate::assert_type; +use xcq_types::{EnumType, Field, Variant}; +use xcq_types::{PrimitiveType, XcqType, XcqTypeInfo}; #[test] fn primitives() { From bd80e1c5232ee2e93feaca2a2cfba8102f9938e9 Mon Sep 17 00:00:00 2001 From: indirection42 Date: Fri, 5 Jul 2024 14:01:43 +0800 Subject: [PATCH 07/10] to be fixed --- xcq-types/src/lib.rs | 2 + xcq-types/{tests/prelude.rs => src/tests.rs} | 39 ++++++++++++++++++-- xcq-types/tests/derive.rs | 27 -------------- xcq-types/tests/macros.rs | 11 ------ xcq-types/tests/main.rs | 4 -- 5 files changed, 38 insertions(+), 45 deletions(-) rename xcq-types/{tests/prelude.rs => src/tests.rs} (70%) delete mode 100644 xcq-types/tests/derive.rs delete mode 100644 xcq-types/tests/macros.rs delete mode 100644 xcq-types/tests/main.rs diff --git a/xcq-types/src/lib.rs b/xcq-types/src/lib.rs index e41706e..c79d395 100644 --- a/xcq-types/src/lib.rs +++ b/xcq-types/src/lib.rs @@ -9,6 +9,8 @@ pub use ty::*; mod impls; mod prelude; pub use prelude::*; +#[cfg(test)] +mod tests; pub use xcq_types_derive::XcqTypeInfo; diff --git a/xcq-types/tests/prelude.rs b/xcq-types/src/tests.rs similarity index 70% rename from xcq-types/tests/prelude.rs rename to xcq-types/src/tests.rs index a7e2a1d..a26de4a 100644 --- a/xcq-types/tests/prelude.rs +++ b/xcq-types/src/tests.rs @@ -1,6 +1,14 @@ -use crate::assert_type; -use xcq_types::{EnumType, Field, Variant}; -use xcq_types::{PrimitiveType, XcqType, XcqTypeInfo}; +use crate::{EnumType, Field, PrimitiveType, StructType, Variant, XcqType, XcqTypeInfo}; + +fn assert_type_fn(expect: XcqType) { + assert_eq!(T::type_info(), expect); +} + +macro_rules! assert_type { + ($ty:ty, $expected: expr) => {{ + assert_type_fn::<$ty>($expected); + }}; +} #[test] fn primitives() { @@ -74,3 +82,28 @@ fn tuple_primitives() { ]) ); } + +#[derive(XcqTypeInfo)] +struct Person { + name: String, + age_in_years: u8, +} +#[test] +fn struct_types() { + assert_type!( + Person, + XcqType::Struct(StructType { + ident: "Person".as_bytes().to_vec(), + fields: vec![ + Field { + ident: "name".as_bytes().to_vec(), + ty: XcqType::Sequence(Box::new(XcqType::Primitive(PrimitiveType::U8.into()))), + }, + Field { + ident: "age_in_years".as_bytes().to_vec(), + ty: PrimitiveType::U8.into(), + }, + ], + }) + ); +} diff --git a/xcq-types/tests/derive.rs b/xcq-types/tests/derive.rs deleted file mode 100644 index 07331ee..0000000 --- a/xcq-types/tests/derive.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::assert_type; -use xcq_types::{Field, PrimitiveType, StructType, XcqType, XcqTypeInfo}; - -#[derive(XcqTypeInfo)] -struct Person { - name: String, - age_in_years: u8, -} -#[test] -fn struct_types() { - assert_type!( - Person, - XcqType::Struct(StructType { - ident: "Person".as_bytes().to_vec(), - fields: vec![ - Field { - ident: "name".as_bytes().to_vec(), - ty: XcqType::Sequence(Box::new(XcqType::Primitive(PrimitiveType::U8.into()))), - }, - Field { - ident: "age_in_years".as_bytes().to_vec(), - ty: PrimitiveType::U8.into(), - }, - ], - }) - ); -} diff --git a/xcq-types/tests/macros.rs b/xcq-types/tests/macros.rs deleted file mode 100644 index 8823986..0000000 --- a/xcq-types/tests/macros.rs +++ /dev/null @@ -1,11 +0,0 @@ -use xcq_types::{XcqType, XcqTypeInfo}; -pub fn assert_type_fn(expect: XcqType) { - assert_eq!(T::type_info(), expect); -} - -#[macro_export] -macro_rules! assert_type { - ($ty:ty, $expected: expr) => {{ - $crate::macros::assert_type_fn::<$ty>($expected); - }}; -} diff --git a/xcq-types/tests/main.rs b/xcq-types/tests/main.rs deleted file mode 100644 index 69a42ef..0000000 --- a/xcq-types/tests/main.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[macro_use] -mod macros; -mod derive; -mod prelude; From 5f735ffd7be0af331d29442b8707b481c48bb066 Mon Sep 17 00:00:00 2001 From: indirection42 Date: Fri, 5 Jul 2024 16:14:21 +0800 Subject: [PATCH 08/10] fix crate access --- Cargo.lock | 1 + Cargo.toml | 1 + xcq-types/derive/Cargo.toml | 1 + xcq-types/derive/src/lib.rs | 30 +++++++++++++++++++++++------- xcq-types/src/tests.rs | 2 ++ 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 666b599..bb8a9a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5662,6 +5662,7 @@ dependencies = [ name = "xcq-types-derive" version = "0.1.0" dependencies = [ + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.66", diff --git a/Cargo.toml b/Cargo.toml index a000bf0..3a4dfa7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,3 +71,4 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } syn = { version = "2", features = ["full", "extra-traits"] } quote = "1" proc-macro2 = "1" +proc-macro-crate = "3" diff --git a/xcq-types/derive/Cargo.toml b/xcq-types/derive/Cargo.toml index 3eb51d2..39887fd 100644 --- a/xcq-types/derive/Cargo.toml +++ b/xcq-types/derive/Cargo.toml @@ -10,3 +10,4 @@ proc-macro = true quote = { workspace = true } syn = { workspace = true } proc-macro2 = { workspace = true } +proc-macro-crate = { workspace = true } diff --git a/xcq-types/derive/src/lib.rs b/xcq-types/derive/src/lib.rs index a8607f2..5be0a5b 100644 --- a/xcq-types/derive/src/lib.rs +++ b/xcq-types/derive/src/lib.rs @@ -1,5 +1,6 @@ use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; +use proc_macro_crate::{crate_name, FoundCrate}; use quote::quote; use syn::{punctuated::Punctuated, token::Comma, Data, DataEnum, DataStruct, DeriveInput, Field, Fields}; @@ -38,6 +39,7 @@ impl XcqTypeInfoImpl { } fn expand(&self) -> syn::Result { + let xcq_types = import_xcq_types(); let ident = &self.ast.ident; // Assume no generics @@ -58,9 +60,9 @@ impl XcqTypeInfoImpl { // TODO: No token replacement supported yet Ok(quote! { - impl xcq_types::XcqTypeInfo for #ident { + impl #xcq_types::XcqTypeInfo for #ident { type Identity = Self; - fn type_info() -> xcq_types::XcqType { + fn type_info() -> #xcq_types::XcqType { #type_info } } @@ -68,6 +70,7 @@ impl XcqTypeInfoImpl { } fn generate_struct_type(&self, data_struct: &DataStruct) -> TokenStream2 { + let xcq_types = import_xcq_types(); let ident = &self.ast.ident; let fields = match data_struct.fields { Fields::Named(ref fields) => self.generate_fields(&fields.named), @@ -76,7 +79,7 @@ impl XcqTypeInfoImpl { }; quote! { - xcq_types::StructType { + #xcq_types::StructType { ident: stringify!(#ident).as_bytes().to_vec(), fields: vec![#(#fields),*], }.into() @@ -84,6 +87,7 @@ impl XcqTypeInfoImpl { } fn generate_enum_type(&self, data_enum: &DataEnum) -> TokenStream2 { + let xcq_types = import_xcq_types(); let ident = &self.ast.ident; let variants = data_enum.variants.iter().map(|variant| { let ident = &variant.ident; @@ -93,14 +97,14 @@ impl XcqTypeInfoImpl { Fields::Unit => return quote! {}, }; quote! { - xcq_types::Variant { + #xcq_types::Variant { ident: stringify!(#ident).as_bytes().to_vec(), fields: vec![#(#fields),*], } } }); quote! { - xcq_types::EnumType { + #xcq_types::EnumType { ident: stringify!(#ident).as_bytes().to_vec(), variants: vec![#(#variants),*], }.into() @@ -108,6 +112,7 @@ impl XcqTypeInfoImpl { } fn generate_fields(&self, fields: &Punctuated) -> Vec { + let xcq_types = import_xcq_types(); fields .iter() .map(|f| { @@ -117,12 +122,23 @@ impl XcqTypeInfoImpl { None => quote! { vec![] }, }; quote! { - xcq_types::Field { + #xcq_types::Field { ident: #ident_toks, - ty: <#ty as xcq_types::XcqTypeInfo>::type_info(), + ty: <#ty as #xcq_types::XcqTypeInfo>::type_info(), } } }) .collect() } } + +fn import_xcq_types() -> TokenStream2 { + let found_crate = crate_name("xcq-types").expect("xcq-types not found in Cargo.toml"); + match found_crate { + FoundCrate::Itself => quote! { crate }, + FoundCrate::Name(name) => { + let name = syn::Ident::new(&name, proc_macro2::Span::call_site()); + quote! { ::#name } + } + } +} diff --git a/xcq-types/src/tests.rs b/xcq-types/src/tests.rs index a26de4a..9f657e6 100644 --- a/xcq-types/src/tests.rs +++ b/xcq-types/src/tests.rs @@ -85,7 +85,9 @@ fn tuple_primitives() { #[derive(XcqTypeInfo)] struct Person { + #[allow(dead_code)] name: String, + #[allow(dead_code)] age_in_years: u8, } #[test] From e0aace6eda506004c4863c7aee2ceb2d5a925b27 Mon Sep 17 00:00:00 2001 From: indirection42 Date: Fri, 5 Jul 2024 16:35:29 +0800 Subject: [PATCH 09/10] add notes --- xcq-types/src/impls.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/xcq-types/src/impls.rs b/xcq-types/src/impls.rs index e04cbf8..8416d0b 100644 --- a/xcq-types/src/impls.rs +++ b/xcq-types/src/impls.rs @@ -172,3 +172,4 @@ impl XcqTypeInfo for String { } } // No impl for PhantomData, codec::Compact, Range, RangeInclusive, str, BTreeMap, BTreeSet, BinaryHeap, VecDeque yet. +// No support for self-referential types yet. From 04025f881b7c275d576c512fea3a7c1865ef50ab Mon Sep 17 00:00:00 2001 From: indirection42 Date: Mon, 8 Jul 2024 21:20:33 +0800 Subject: [PATCH 10/10] refactor raw bytes representation --- xcq-types/src/impls.rs | 12 ++++++------ xcq-types/src/tests.rs | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/xcq-types/src/impls.rs b/xcq-types/src/impls.rs index 8416d0b..a95e652 100644 --- a/xcq-types/src/impls.rs +++ b/xcq-types/src/impls.rs @@ -65,14 +65,14 @@ where type Identity = Self; fn type_info() -> XcqType { EnumType { - ident: "Option".as_bytes().to_vec(), + ident: b"Option".to_vec(), variants: vec![ Variant { - ident: "None".as_bytes().to_vec(), + ident: b"None".to_vec(), fields: vec![], }, Variant { - ident: "Some".as_bytes().to_vec(), + ident: b"Some".to_vec(), fields: vec![Field { ident: vec![], ty: T::type_info(), @@ -92,17 +92,17 @@ where type Identity = Self; fn type_info() -> XcqType { EnumType { - ident: "Result".as_bytes().to_vec(), + ident: b"Result".to_vec(), variants: vec![ Variant { - ident: "Ok".as_bytes().to_vec(), + ident: b"Ok".to_vec(), fields: vec![Field { ident: vec![], ty: T::type_info(), }], }, Variant { - ident: "Err".as_bytes().to_vec(), + ident: b"Err".to_vec(), fields: vec![Field { ident: vec![], ty: E::type_info(), diff --git a/xcq-types/src/tests.rs b/xcq-types/src/tests.rs index 9f657e6..fcbb20f 100644 --- a/xcq-types/src/tests.rs +++ b/xcq-types/src/tests.rs @@ -25,14 +25,14 @@ fn prelude_items() { assert_type!( Option, XcqType::Enum(EnumType { - ident: "Option".as_bytes().to_vec(), + ident: b"Option".to_vec(), variants: vec![ Variant { - ident: "None".as_bytes().to_vec(), + ident: b"None".to_vec(), fields: vec![], }, Variant { - ident: "Some".as_bytes().to_vec(), + ident: b"Some".to_vec(), fields: vec![Field { ident: vec![], ty: PrimitiveType::U128.into(), @@ -45,17 +45,17 @@ fn prelude_items() { assert_type!( Result, XcqType::Enum(EnumType { - ident: "Result".as_bytes().to_vec(), + ident: b"Result".to_vec(), variants: vec![ Variant { - ident: "Ok".as_bytes().to_vec(), + ident: b"Ok".to_vec(), fields: vec![Field { ident: vec![], ty: PrimitiveType::Bool.into(), }], }, Variant { - ident: "Err".as_bytes().to_vec(), + ident: b"Err".to_vec(), fields: vec![Field { ident: vec![], ty: XcqType::Sequence(Box::new(PrimitiveType::U8.into())), @@ -95,14 +95,14 @@ fn struct_types() { assert_type!( Person, XcqType::Struct(StructType { - ident: "Person".as_bytes().to_vec(), + ident: b"Person".to_vec(), fields: vec![ Field { - ident: "name".as_bytes().to_vec(), + ident: b"name".to_vec(), ty: XcqType::Sequence(Box::new(XcqType::Primitive(PrimitiveType::U8.into()))), }, Field { - ident: "age_in_years".as_bytes().to_vec(), + ident: b"age_in_years".to_vec(), ty: PrimitiveType::U8.into(), }, ],