[init] Initial commit

This commit is contained in:
Uttarayan Mondal
2023-08-11 11:42:42 +05:30
commit 143b9c84de
7 changed files with 316 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
/target
/Cargo.lock
+24
View File
@@ -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
+15
View File
@@ -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
+149
View File
@@ -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(&gtype, 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![<](Span::call_site()).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![>](Span::call_site()).into(),
where_clause: None,
};
let builder_struct = syn::ItemStruct {
attrs: vec![],
vis: Visibility::Inherited,
struct_token: Token![struct](Span::call_site()),
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![PathSegment {
ident: builder_struct_name.clone(),
arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
colon2_token: None,
lt_token: Token![<](Span::call_site()),
args: core::iter::repeat::<GenericArgument>(parse_quote!(
#crate_name::Empty
))
.take(state as usize - 1)
.collect(),
gt_token: Token![>](Span::call_site()),
}),
}]),
},
});
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![impl](Span::call_site()),
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
})
}
}
+101
View File
@@ -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);
}
+12
View File
@@ -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
View File
@@ -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;
}