[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