I'm working on a custom builder pattern. I'm trying to define my own const generics, and I'm trying to combine those with the syn::Generics that I get from parsing the struct. Returning the generic identifiers works, but I'm having trouble when they're constant.
At first I tried combining the generics simply by appending them after each other: quote!(#struct_generics #const_generics); or quote!(<#struct_generics #const_generics>); but that outputs <A> <const M_0: bool, const M_1: bool> or <<A> <const M_0: bool, const M_1: bool>> respectively.
After a while I tried it like the following example:
use syn::parse_quote;
use quote::format_ident;
pub struct FieldInfo {
index: usize
}
impl FieldInfo {
pub fn mandatory_index(&self) -> usize { self.index }
}
pub fn get_all_generics(fields: &Vec<FieldInfo>, target_generics: &syn::Generics) -> syn::Generics {
let mut res = target_generics.clone();
let syn::Generics {
ref mut params,
..
} = res;
fields.iter().for_each(|field| {
let ident = format_ident!("M_{}",field.mandatory_index());
params.push(parse_quote!(const #ident: bool));
});
let (impl_generics, type_generics, where_clause) = res.split_for_impl();
dbg!(impl_generics, type_generics, where_clause);
res
}
fn main() {
let generics = syn::Generics::default();
let infos = (0..2).map(|index| FieldInfo { index }).collect::<Vec<_>>();
get_all_generics(&infos, &generics);
}
Which works, impl_generics seems to be able to be used for the struct definition, and I hope the where_clause too. But now I need to set specific constants. How do I set them inside of syn::Generics?
params.push(parse_quote!(false)); doesn't seem to work.
The code below shows that I'm adding some const generics. This is all going well, until I'm trying to derive on a generic struct like pub struct Foo<A>:
#[derive(Debug, Builder)]
pub struct Foo<A> {
bar: A,
baz: A,
}
Should expand to:
impl Foo<A> {
pub fn builder() -> FooBuilder<A, false, false> {
FooBuilder::new()
}
}
#[derive(Default, Debug)]
pub struct FooBuilder<A, const M_0: bool, const M_1: bool> {
data: FooData<A>,
}
impl <A> FooBuilder<A, false, false> {
pub fn new() -> FooBuilder<A, false, false> {
Self::default()
}
}
impl<A, const M_1: bool> FooBuilder<A, false, M_1> {
pub fn bar(self, bar: String) -> FooBuilder<A, true, M_1> {
let mut data = self.data;
data.bar = Some(bar);
FooBuilder { data }
}
}
impl<A, const M_0: bool> FooBuilder<A, M_0, false> {
pub fn baz(self, baz: String) -> FooBuilder<A, M_0, true> {
let mut data = self.data;
data.baz = Some(baz);
FooBuilder { data }
}
}
impl <A> FooBuilder<A, true, true> {
pub fn build(self) -> Foo<A> {
self.data.into()
}
}
#[derive(Default, Debug)]
pub struct FooData<A> {
pub bar: Option<A>,
pub baz: Option<A>,
}
impl <A> From<FooData<A>> for Foo<A> {
fn from(data: FooData<A>) -> Foo<A> {
Foo {
bar: data.bar.unwrap(),
baz: data.baz.unwrap(),
}
}
}
I figured it out, I was getting confused over the difference between impl generics and type generics. I wrote two functions that can be used for
impl #impl_generics MyType #type_generics.As you can see, the second function doesn't return syn::Generics. I was trying to find a way to do this, but it seems impossible and this does the trick.