[init] Initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
/Cargo.lock
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/uttarayan21/comptime-builder"
|
||||
documentation = "https://docs.rs/comptime-builder"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
".",
|
||||
"comptime-builder-macros"
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
comptime-builder-macros = { version = "0.1.0", path = "comptime-builder-macros" }
|
||||
|
||||
[package]
|
||||
name = "comptime-builder"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
documentation.workspace = true
|
||||
|
||||
[dependencies]
|
||||
comptime-builder-macros.workspace = true
|
||||
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "comptime-builder-macros"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
documentation.workspace = true
|
||||
|
||||
[dependencies]
|
||||
convert_case = "0.6.0"
|
||||
proc-macro2 = "1.0.66"
|
||||
quote = "1.0.32"
|
||||
syn = { version = "2.0.27", features = ["extra-traits", "full"] }
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
@@ -0,0 +1,149 @@
|
||||
use syn::*;
|
||||
|
||||
#[proc_macro_derive(Builder)]
|
||||
pub fn builder_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
dbg!(derive_builder::derive(input)
|
||||
.unwrap_or_else(|err| err.into_compile_error())
|
||||
.into())
|
||||
}
|
||||
|
||||
mod derive_builder {
|
||||
use proc_macro2::*;
|
||||
use quote::quote;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::token::Comma;
|
||||
use syn::*;
|
||||
|
||||
pub fn derive(input: DeriveInput) -> Result<TokenStream> {
|
||||
let crate_name: syn::Path = parse_quote!(::comptime_builder);
|
||||
let fields = match input.data {
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Named(FieldsNamed { named, .. }),
|
||||
..
|
||||
}) => named,
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
input,
|
||||
"expected a struct with named fields",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let struct_name = &input.ident;
|
||||
let builder_struct_name = Ident::new(&format!("{}Builder", struct_name), Span::call_site());
|
||||
|
||||
// Use Generic type T{0..} to represent the fields
|
||||
let mut state = 1u32;
|
||||
let generic_fields = fields.into_iter().scan(&mut state, |state, mut field| {
|
||||
let gtype = format!("T{}", state);
|
||||
**state += 1;
|
||||
field.ty = syn::Type::Path(TypePath {
|
||||
qself: None,
|
||||
path: syn::Path {
|
||||
leading_colon: None,
|
||||
segments: Punctuated::from_iter(vec![PathSegment {
|
||||
ident: syn::Ident::new(>ype, Span::call_site()),
|
||||
arguments: PathArguments::None,
|
||||
}]),
|
||||
},
|
||||
});
|
||||
Some(field)
|
||||
});
|
||||
let generic_fields: Punctuated<Field, Comma> = generic_fields.collect();
|
||||
let builder_generics = syn::Generics {
|
||||
lt_token: Token).into(),
|
||||
params: (1..state)
|
||||
.map(|f| {
|
||||
GenericParam::Type(TypeParam {
|
||||
attrs: vec![],
|
||||
ident: syn::Ident::new(&format!("T{}", f), Span::call_site()),
|
||||
colon_token: None,
|
||||
bounds: Punctuated::new(),
|
||||
eq_token: None,
|
||||
default: None,
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
gt_token: Token).into(),
|
||||
where_clause: None,
|
||||
};
|
||||
|
||||
let builder_struct = syn::ItemStruct {
|
||||
attrs: vec![],
|
||||
vis: Visibility::Inherited,
|
||||
struct_token: Token),
|
||||
ident: builder_struct_name.clone(),
|
||||
fields: Fields::Named(FieldsNamed {
|
||||
brace_token: token::Brace::default(),
|
||||
named: generic_fields,
|
||||
}),
|
||||
generics: builder_generics,
|
||||
semi_token: None,
|
||||
};
|
||||
|
||||
let empty_builder_type: syn::Type = syn::Type::Path(TypePath {
|
||||
qself: None,
|
||||
path: syn::Path {
|
||||
leading_colon: None,
|
||||
segments: Punctuated::from_iter(vec),
|
||||
args: core::iter::repeat::<GenericArgument>(parse_quote!(
|
||||
#crate_name::Empty
|
||||
))
|
||||
.take(state as usize - 1)
|
||||
.collect(),
|
||||
gt_token: Token),
|
||||
}),
|
||||
}]),
|
||||
},
|
||||
});
|
||||
|
||||
let empty_builder = syn::ExprStruct {
|
||||
attrs: vec![],
|
||||
qself: None,
|
||||
path: parse_quote!(#builder_struct_name),
|
||||
brace_token: token::Brace::default(),
|
||||
fields: core::iter::repeat::<FieldValue>(parse_quote!(
|
||||
_field: #crate_name::Empty
|
||||
))
|
||||
.take(state as usize - 1)
|
||||
.collect(),
|
||||
dot2_token: None,
|
||||
rest: None,
|
||||
};
|
||||
|
||||
let empty_builder_fn = syn::ImplItemFn {
|
||||
attrs: vec![],
|
||||
vis: Visibility::Inherited,
|
||||
defaultness: None,
|
||||
sig: parse_quote!(fn builder() -> #empty_builder_type),
|
||||
block: parse_quote! {{
|
||||
#empty_builder
|
||||
}},
|
||||
};
|
||||
|
||||
let ge = input.generics.params.clone();
|
||||
let self_ty = parse_quote!(#struct_name<#ge>);
|
||||
let impl_empty_builder_fn = syn::ItemImpl {
|
||||
attrs: vec![],
|
||||
defaultness: None,
|
||||
unsafety: None,
|
||||
impl_token: Token),
|
||||
generics: input.generics.clone(),
|
||||
trait_: None,
|
||||
self_ty: Box::new(self_ty),
|
||||
brace_token: token::Brace::default(),
|
||||
items: vec![ImplItem::Fn(empty_builder_fn)],
|
||||
};
|
||||
|
||||
Ok(quote! {
|
||||
#impl_empty_builder_fn
|
||||
#builder_struct
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
use comptime_builder::*;
|
||||
|
||||
impl<Y, M, D> WithField<1, u16, Date> for DateBuilder<Y, M, D> {
|
||||
type Output = DateBuilder<Field<1, u16>, M, D>;
|
||||
fn with_field(self, value: u16) -> Self::Output {
|
||||
DateBuilder {
|
||||
year: Field(value),
|
||||
month: self.month,
|
||||
day: self.day,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Y, M, D> WithField<2, u8, Date> for DateBuilder<Y, M, D> {
|
||||
type Output = DateBuilder<Y, Field<2, u8>, D>;
|
||||
fn with_field(self, value: u8) -> Self::Output {
|
||||
DateBuilder {
|
||||
year: self.year,
|
||||
month: Field(value),
|
||||
day: self.day,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Y, M, D> WithField<3, u8, Date> for DateBuilder<Y, M, D> {
|
||||
type Output = DateBuilder<Y, M, Field<3, u8>>;
|
||||
fn with_field(self, value: u8) -> Self::Output {
|
||||
DateBuilder {
|
||||
year: self.year,
|
||||
month: self.month,
|
||||
day: Field(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WithYear: WithField<1, u16, Date> {
|
||||
fn with_year(self, value: u16) -> Self::Output {
|
||||
self.with_field(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> WithYear for T where T: WithField<1, u16, Date> {}
|
||||
|
||||
pub trait WithMonth: WithField<2, u8, Date> {
|
||||
fn with_month(self, value: u8) -> Self::Output {
|
||||
self.with_field(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> WithMonth for T where T: WithField<2, u8, Date> {}
|
||||
|
||||
pub trait WithDay: WithField<3, u8, Date> {
|
||||
fn with_day(self, value: u8) -> Self::Output {
|
||||
self.with_field(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> WithDay for T where T: WithField<3, u8, Date> {}
|
||||
|
||||
pub struct DateBuilder<Year, Month, Day> {
|
||||
year: Year,
|
||||
month: Month,
|
||||
day: Day,
|
||||
}
|
||||
|
||||
impl DateBuilder<Field<1, u16>, Field<2, u8>, Field<3, u8>> {
|
||||
fn build(self) -> Date {
|
||||
Date {
|
||||
year: self.year.0,
|
||||
month: self.month.0,
|
||||
day: self.day.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Date {
|
||||
year: u16,
|
||||
month: u8,
|
||||
day: u8,
|
||||
}
|
||||
pub struct Empty;
|
||||
|
||||
impl Date {
|
||||
pub fn builder() -> DateBuilder<Empty, Empty, Empty> {
|
||||
DateBuilder {
|
||||
year: Empty,
|
||||
month: Empty,
|
||||
day: Empty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let date = Date::builder()
|
||||
.with_year(2022)
|
||||
.with_month(5)
|
||||
.with_day(1)
|
||||
.build();
|
||||
dbg!(date);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
use comptime_builder::Builder;
|
||||
|
||||
#[derive(Builder)]
|
||||
pub struct MyStruct<T1, T2> {
|
||||
pub my_field: T1,
|
||||
pub my_other_field: T2,
|
||||
pub our_field: String,
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
pub use comptime_builder_macros::Builder;
|
||||
|
||||
pub struct Empty;
|
||||
|
||||
|
||||
pub trait HasField<const N: usize, T> {}
|
||||
impl<const N: usize, T> HasField<N, T> for Field<N, T> {}
|
||||
pub struct Field<const N: usize, T>(pub T);
|
||||
|
||||
pub trait WithField<const N: usize, Field, Struct>: Sized {
|
||||
type Output;
|
||||
fn with_field(self, value: Field) -> Self::Output;
|
||||
}
|
||||
Reference in New Issue
Block a user