Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 79 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[workspace]
members = ["marrow", "test_with_arrow"]
default-members = ["marrow", "test_with_arrow"]
members = ["marrow", "marrow-convert", "marrow-convert-derive", "test_with_arrow"]

resolver = "2"

Expand Down
12 changes: 12 additions & 0 deletions marrow-convert-derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "marrow-convert-derive"
version = "0.1.0"
edition = "2024"

[lib]
proc-macro = true

[dependencies]
syn = "2.0"
quote = "1.0"
proc-macro2 = "1.0"
92 changes: 92 additions & 0 deletions marrow-convert-derive/src/array_push.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
use quote::{format_ident, quote};
use syn::{
Data, DataEnum, DataStruct, DeriveInput, Fields, GenericParam, Ident, Type, spanned::Spanned,
};

pub fn derive_array_push(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
let input: DeriveInput = syn::parse2(input).unwrap();

if input
.generics
.params
.iter()
.any(|p| matches!(p, GenericParam::Type(_)))
{
panic!("Deriving TypeInfo for generics with type parameters is not supported")
}

match &input.data {
Data::Struct(data) => derive_for_struct(&input, data),
Data::Enum(data) => derive_for_enum(&input, data),
Data::Union(_) => panic!("Deriving TypeInfo for unions is not supported"),
}
}

fn derive_for_struct(input: &DeriveInput, data: &DataStruct) -> proc_macro2::TokenStream {
if data.fields.len() >= 16 {
panic!("Only structs with at most 16 fields are supported");
}

let ident = &input.ident;
let generics = &input.generics;
let generics = if generics.params.is_empty() {
quote! {}
} else {
quote! { #generics, }
};

let mut field_defs = Vec::new();
let mut field_uses = Vec::new();
let mut field_push = Vec::new();

for (idx, (name, ty)) in get_fields_and_names(&data.fields).into_iter().enumerate() {
let ident = format_ident!("T{idx}");
field_defs.push(quote! { #ident : ::marrow_convert::builder::ArrayPush<#ty> });
field_uses.push(quote! { #ident });
field_push.push(
quote! { ::marrow_convert::builder::ArrayPush::push_value(#ident, &value.#name )?; },
);
}

quote! {
const _: () = {
impl<#generics #(#field_defs),*> ::marrow_convert::builder::ArrayPush<#ident> for ::marrow_convert::builder::compound::StructBuilder<(#(#field_uses,)*)> {
fn push_value(&mut self, value: &#ident) -> ::marrow_convert::Result<()> {
self.len += 1;
let (#(#field_uses,)*) = &mut self.children;
#(#field_push)*
Ok(())
}
}
};
}
}

pub fn get_fields_and_names(fields: &Fields) -> Vec<(Ident, Type)> {
let mut result = Vec::new();
match fields {
Fields::Named(fields) => {
for field in &fields.named {
let ident = field.ident.clone().expect("Named field without ident");
let ty = field.ty.clone();
result.push((ident, ty));
}
}
Fields::Unnamed(fields) => {
for (idx, field) in fields.unnamed.iter().enumerate() {
result.push((
Ident::new(&idx.to_string(), field.ty.span()),
field.ty.clone(),
));
}
}
Fields::Unit => unimplemented!("Unit structs are currently not implemented"),
}

result
}

fn derive_for_enum(input: &DeriveInput, data: &DataEnum) -> proc_macro2::TokenStream {
let _ = (input, data);
todo!()
}
104 changes: 104 additions & 0 deletions marrow-convert-derive/src/default_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use quote::{format_ident, quote};
use syn::{Data, DataEnum, DataStruct, DeriveInput, GenericParam, LitStr};

use super::array_push::get_fields_and_names;

pub fn derive_default_builder(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
let input: DeriveInput = syn::parse2(input).unwrap();

if input
.generics
.params
.iter()
.any(|p| matches!(p, GenericParam::Type(_)))
{
panic!("Deriving TypeInfo for generics with type parameters is not supported")
}

match &input.data {
Data::Struct(data) => derive_for_struct(&input, data),
Data::Enum(data) => derive_for_enum(&input, data),
Data::Union(_) => panic!("Deriving TypeInfo for unions is not supported"),
}
}

fn derive_for_struct(input: &DeriveInput, data: &DataStruct) -> proc_macro2::TokenStream {
if data.fields.len() >= 16 {
panic!("Only structs with at most 16 fields are supported");
}

let ident = &input.ident;

let builder_ident = format_ident!("{ident}Builder");

let mut field_uses = Vec::new();
let mut field_push = Vec::new();
let mut field_builders = Vec::new();
let mut field_metas = Vec::new();
let mut field_inits = Vec::new();

for (idx, (name, ty)) in get_fields_and_names(&data.fields).into_iter().enumerate() {
let ident = format_ident!("t{idx}");
field_uses.push(quote! { #ident });
field_push.push(
quote! { ::marrow_convert::builder::ArrayPush::push_value(#ident, &value.#name )?; },
);

let field_name = LitStr::new(&name.to_string(), name.span());

field_builders
.push(quote! { <#ty as ::marrow_convert::builder::DefaultArrayBuilder>::ArrayBuilder });
field_metas.push(quote! {
::marrow::datatypes::FieldMeta {
name: String::from(#field_name),
..::std::default::Default::default()
}
});
field_inits.push(quote! {
<#ty as ::marrow_convert::builder::DefaultArrayBuilder>::default_builder()
})
}

return quote! {
const _: () = {
pub struct #builder_ident(::marrow_convert::builder::compound::StructBuilder<(#(#field_builders,)*)>);

impl ::marrow_convert::builder::DefaultArrayBuilder for #ident {
type ArrayBuilder = #builder_ident;

fn default_builder() -> Self::ArrayBuilder {
#builder_ident(::marrow_convert::builder::compound::StructBuilder {
len: 0,
meta: vec![#(#field_metas),*],
children: (#(#field_inits,)*),
})
}
}

impl ::marrow_convert::builder::ArrayBuilder for #builder_ident {
fn push_default(&mut self) -> ::marrow_convert::Result<()> {
self.0.push_default()
}

fn build_array(&mut self) -> ::marrow_convert::Result<::marrow::array::Array> {
self.0.build_array()
}
}

impl ::marrow_convert::builder::ArrayPush<#ident> for #builder_ident {
fn push_value(&mut self, value: &#ident) -> ::marrow_convert::Result<()> {
self.0.len += 1;
let (#(#field_uses,)*) = &mut self.0.children;
#(#field_push)*
Ok(())
}
}

};
};
}

fn derive_for_enum(input: &DeriveInput, data: &DataEnum) -> proc_macro2::TokenStream {
let _ = (input, data);
todo!()
}
20 changes: 20 additions & 0 deletions marrow-convert-derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use proc_macro::TokenStream;

mod array_push;
mod default_builder;
mod type_info;

#[proc_macro_derive(DefaultArrayType, attributes(marrow))]
pub fn derive_type_info(input: TokenStream) -> TokenStream {
type_info::derive_type_info(input.into()).into()
}

#[proc_macro_derive(ArrayPush, attributes(marrow))]
pub fn derive_array_push(input: TokenStream) -> TokenStream {
array_push::derive_array_push(input.into()).into()
}

#[proc_macro_derive(DefaultArrayBuilder, attributes(marrow))]
pub fn derive_default_builder(input: TokenStream) -> TokenStream {
default_builder::derive_default_builder(input.into()).into()
}
Loading