From eab842c950d0b6d673faf891c971d4bf9c3cb573 Mon Sep 17 00:00:00 2001 From: Ben PHL Date: Wed, 30 Aug 2023 22:31:02 +0200 Subject: [PATCH 01/11] Add list-bulid work based on https://github.com/Ben-PH/hlist_builder/tree/778b03c9783e8880fcb45445f7452d6fafdac54d --- proc-macros/Cargo.toml | 3 +- proc-macros/src/lib.rs | 6 + proc-macros/src/list_builder.rs | 323 ++++++++++++++++++++++++++++++++ 3 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 proc-macros/src/list_builder.rs diff --git a/proc-macros/Cargo.toml b/proc-macros/Cargo.toml index b190ef279..0c2518ad0 100644 --- a/proc-macros/Cargo.toml +++ b/proc-macros/Cargo.toml @@ -13,8 +13,9 @@ keywords = ["Frunk", "macros"] travis-ci = { repository = "lloydmeta/frunk" } [dependencies] -syn = "2" +syn = {version = "2", features = ["full"] } quote = "1" +proc-macro2 = "1.0.66" [lib] proc-macro = true diff --git a/proc-macros/src/lib.rs b/proc-macros/src/lib.rs index 8a5d9ab62..f6ee49a8a 100644 --- a/proc-macros/src/lib.rs +++ b/proc-macros/src/lib.rs @@ -19,6 +19,8 @@ use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, Expr}; +mod list_builder; + /// Build a generic path that can be used for traversals #[proc_macro] pub fn path(input: TokenStream) -> TokenStream { @@ -46,3 +48,7 @@ pub fn Path(input: TokenStream) -> TokenStream { // println!("ast: [{}]", ast); TokenStream::from(ast) } +#[proc_macro_derive(ListBuild)] +pub fn list_build(item: TokenStream) -> TokenStream { + list_builder::list_build_inner(item) +} diff --git a/proc-macros/src/list_builder.rs b/proc-macros/src/list_builder.rs new file mode 100644 index 000000000..2fbcfa11a --- /dev/null +++ b/proc-macros/src/list_builder.rs @@ -0,0 +1,323 @@ +extern crate proc_macro; +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::quote; +use syn::{ + parse_macro_input, punctuated::Punctuated, token::Comma, Block, DeriveInput, FnArg, + GenericParam, Generics, Ident, Stmt, WherePredicate, TypeParamBound +}; + +// struct field or function argument +struct ArgPair { + ident: syn::Ident, + tp: syn::Type +} + +impl ArgPair { + /// ``` + /// #[derive(ListBuild] + /// struct Foo { + /// foo: u8, + /// #[list_build_ignore] + /// bar: u16 + /// } + /// // -> fn(l0: HList!(u8), bar: u16) + fn make_args(fields: Vec) -> impl Iterator { + std::iter::once(syn::parse2(quote!{l0: L0}).unwrap()).chain(fields.into_iter().map(FnArg::from)) + } +} +impl From<(syn::Ident, syn::Type)> for ArgPair { + fn from(value: (syn::Ident, syn::Type)) -> Self { + Self{ ident: value.0, tp: value.1 } + } +} +impl From for syn::FnArg { + fn from(value: ArgPair) -> Self { + syn::FnArg::Typed(syn::PatType { + attrs: Vec::new(), + pat: Box::new(syn::Pat::Ident(syn::PatIdent { + attrs: Vec::new(), + by_ref: None, + mutability: None, + ident: value.ident.clone(), + subpat: None, + })), + colon_token: syn::token::Colon { spans: [proc_macro2::Span::call_site()] }, + ty: Box::new(value.tp.clone()) + }) + } +} + +/// each line in the where predicate is the type-binding, and the trait it must impl +#[derive(Clone)] +struct WhereLine { + tp: syn::Type, + pred: PluckParam +} + +impl WhereLine { + /// entry into the recursive build-up of the where-lines + /// + /// generates the base-line (L0: Plucker) then goes into the recursive call + fn gen_lines_top(types: &[syn::Type]) -> Vec { + if types.len() == 0 { + panic!("no pluckings happening"); + } + let base = Self::gen_base(&types[0]); + if types.len() == 1 { + return vec![base]; + } + Self::gen_lines_recur(vec![base], &types[1..]) + } + + /// Beginning with the base line, it generates a new where-line for each type. + /// Each line is an "absorbed" version of the previous line, with `Plucker` as + /// the new trait-impl requirement. + /// + /// ```ignore + /// LN: Plucker // is saved, the next being... + /// >::Remainder: Plucker + /// ``` + fn gen_lines_recur(mut acc: Vec, types: &[syn::Type]) -> Vec { + // use the previous predicate to make the new type + let tp = acc.last().cloned().expect("should never recurse without the base...").absorb(); + + let pred = PluckParam::from((types[0].clone(), acc.len() as u8 + 1)); + acc.push(Self{tp, pred}); + if types.len() == 1 { + return acc; + } + Self::gen_lines_recur(acc, &types[1..]) + } + + /// L0: Plucker + fn gen_base(tp: &syn::Type) -> Self { + let pred = syn::parse2( quote!{frunk::hlist::Plucker<#tp, L1>} ).expect("quote the base plucker"); + // Create the WhereLine + WhereLine { + tp: syn::parse2( quote!{L0} ).expect("quote the L0"), + pred: PluckParam(pred), + } + } + + /// Does the "absorption" needed for the next line for each gen_lines_recur + /// Tn: Pn -> ::Remainder + fn absorb(self) -> syn::Type { + let WhereLine { tp, pred } = self; + let pred = pred.0; + + let res = quote!{<#tp as #pred>::Remainder}; + syn::parse2(res).expect("absorbing") + } +} + +impl From for WherePredicate { + fn from(line: WhereLine) -> Self { + let WhereLine { tp, pred } = line; + let bound: TypeParamBound = pred.0.into(); + let predicate = quote! { #tp: #bound }; + syn::parse2(predicate).expect("whereline to pred") + } +} + +/// shim allowing PredicateVec::from(line_vec).into() where a `syn::WhereClause` is needed +struct PredicateVec { + preds: Vec +} + +impl From> for PredicateVec { + fn from(value: Vec) -> Self { + Self{ preds: value.into_iter().map(WherePredicate::from).collect() } + } +} +impl From for syn::WhereClause { + fn from(value: PredicateVec) -> Self { + Self { + where_token: syn::Token![where](proc_macro2::Span::call_site()), + predicates: syn::punctuated::Punctuated::from_iter(value.preds), + } + } +} + +#[derive(Clone)] +struct PluckParam(syn::TypeParamBound); +impl From<(syn::Type, u8)> for PluckParam { + fn from((ty, n): (syn::Type, u8)) -> Self { + + + // Create an ident for "LN" where N is the u8 value + let l_ident = syn::Ident::new(&format!("L{}", n), Span::call_site()); + return Self(syn::parse2(quote!{frunk::hlist::Plucker<#ty, #l_ident>}).unwrap()); + } +} + + + +pub fn list_build_inner(item: TokenStream) -> TokenStream { + let input = parse_macro_input!(item as DeriveInput); + // get the fields: the list-built fields, and the manually-built fields + let (input, annotated_fields, non_annotated_fields) = parse_fields(input); + + + let block = gen_stmts( + &annotated_fields.iter().map(|ArgPair{ident, ..}| ident.clone()).collect(), + &non_annotated_fields + .iter() + .map(|ArgPair{ident, ..}| ident.clone()) + .collect::>()[..], + ); + + // hl_new args include the injected list, and values for the non-list built args + let args = ArgPair::make_args(non_annotated_fields); + + if annotated_fields.len() == 0 { + panic!("redundant builder annotations"); + } + let types = annotated_fields.iter().map(|ArgPair{ tp, .. }| tp.clone()).collect::>(); + + // make all where-clauses + let lines: Vec = WhereLine::gen_lines_top(&types[..]); + + // Take the last clause and absorb that to make the ret-val + let ret = WhereLine::absorb(lines.last().expect("last line").clone()); + + + + let fn_ident = syn::Ident::new("hl_new", proc_macro2::Span::call_site()); + let output: syn::ReturnType = syn::ReturnType::Type( + syn::token::RArrow::default(), + Box::new(syn::Type::Tuple(syn::TypeTuple { + paren_token: syn::token::Paren::default(), + elems: { + let mut punctuated = Punctuated::new(); + punctuated.push(syn::Type::Path(syn::TypePath { + qself: None, + path: syn::parse_str("Self").expect("parseing Self"), + })); + punctuated.push(ret); + punctuated + }, + })), + ); + // tie all the signature details together + let sig = syn::Signature { + ident: fn_ident, + generics: Generics { + where_clause: Some(PredicateVec::from(lines).into()), + params: make_generic_params(annotated_fields.len()), + ..Default::default() + }, + inputs: Punctuated::from_iter(args), + + output, + constness: None, + asyncness: None, + unsafety: None, + abi: None, + variadic: None, + fn_token: Default::default(), + paren_token: Default::default(), + }; + let struct_ident = input.clone().ident; + let fun = syn::ItemFn { + attrs: vec![], + vis: syn::Visibility::Inherited, + sig, + block: Box::new(block), + }; + + // et, voile! en a des code magnifique! + quote! { + impl #struct_ident { + #fun + } + }.into() +} +/// collects the field name/type pairs, splitting them according to fields being built by the list +/// or as args passed into the constructor +fn parse_fields( + mut input: DeriveInput, +) -> ( + DeriveInput, + Vec, + Vec, +) { + let mut list_built = Vec::new(); + let mut ignored_fields = Vec::new(); + + + if let syn::Data::Struct(syn::DataStruct { + fields: syn::Fields::Named(named), + .. + }) = &mut input.data + { + for field in &mut named.named { + let ignored = field + .attrs + .iter() + .any(|attr| attr.path().is_ident("list_build_ignore")); + + let field_ident = field.ident.clone().expect("field ident"); // Assuming named fields + let field_type = field.ty.clone(); // Type + + if ignored { + ignored_fields.push((field_ident, field_type).into()); + } else { + list_built.push((field_ident, field_type).into()); + // Remove the hl_field attribute + field.attrs.retain(|attr| !attr.path().is_ident("list_build_ignore")); + } + } + } + (input, list_built, ignored_fields) +} + + +// `` for the `fn hl_new` +fn make_generic_params(count: usize) -> Punctuated { + let gens: String = (0..count + 1) + .map(|i| format!("L{}", i)) + .collect::>() + .join(", "); + let gens = format!("<{}>", gens); + syn::parse_str::(&gens).expect("parsing the make_generic_params").params +} + + +// generates a line of code for each field that needs a value plucked out of the constructor list. +// ```ignore +// let (list_built0, l1) = frunk::hlist::Plucker::pluck(l0); +// ``` +// ...and for the fileds ignored, just moves from the function argument to the rusulting structs +// field +fn gen_stmts(fields: &Vec, args: &[Ident]) -> Block { + let mut list_n = 0; + let mut stmts: Vec = vec![]; + // Generate the "let (field, lX) = lY.pluck();" statements + for id in fields { + let next_list = list_n + 1; + let next_list = syn::Ident::new(&format!("l{}", next_list), Span::call_site()); + let list_n_tok = syn::Ident::new(&format!("l{}", list_n), Span::call_site()); + let stmt: Stmt = syn::parse2(quote! { + let (#id, #next_list) = frunk::hlist::Plucker::pluck(#list_n_tok); + }) + .expect(""); + stmts.push(stmt); + list_n += 1; + } + + // Generate the "Self { fields... }" part of the block + let all_fields = [&fields[..], args].concat(); + let list_n_ident = syn::Ident::new(&format!("l{}", list_n), proc_macro2::Span::call_site()); + let self_stmt: Stmt = syn::parse2(quote! { + return (Self { #(#all_fields,)* }, #list_n_ident); + }) + .expect(""); + stmts.push(self_stmt); + + Block { + stmts, + brace_token: Default::default(), + } +} + From 503954878f2b15f80c855e0d1444ca8b73c36e97 Mon Sep 17 00:00:00 2001 From: Ben PHL Date: Wed, 30 Aug 2023 22:45:29 +0200 Subject: [PATCH 02/11] doc-comment the macro --- proc-macros/src/lib.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/proc-macros/src/lib.rs b/proc-macros/src/lib.rs index f6ee49a8a..2a90f34ad 100644 --- a/proc-macros/src/lib.rs +++ b/proc-macros/src/lib.rs @@ -48,6 +48,41 @@ pub fn Path(input: TokenStream) -> TokenStream { // println!("ast: [{}]", ast); TokenStream::from(ast) } + + +/// Constructs a struct using an `HList`. +/// +/// This trait allows you to create an instance of a struct from an HList. You keep the +/// remaining items in the `HList` after `DerivedListBuild::hl_new(...)` +/// +/// # Examples +/// +/// ``` +/// use frunk::hlist::HList; +/// use frunk::hlist; +/// +/// #[derive(Debug, Eq, PartialEq, frunk::hlist::ListBuild)] +/// struct ListConstructed { +/// field0: bool, +/// field1: u8, +/// #[list_build_ignore] +/// fn_built: &'static str, +/// } +/// +/// // Create a struct manually for comparison +/// let manually_made = ListConstructed { +/// field0: true, +/// field1: 3, +/// fn_built: "passed_in", +/// }; +/// +/// // Use `hl_new` to construct a struct and remaining HList +/// let (built, list): (ListConstructed, HList!(u32)) = +/// ListConstructed::hl_new(hlist![true, 3u8, 42u32], "passed_in"); +/// +/// assert_eq!(built, manually_made); +/// assert_eq!(list, hlist![42u32]); +/// ``` #[proc_macro_derive(ListBuild)] pub fn list_build(item: TokenStream) -> TokenStream { list_builder::list_build_inner(item) From c2f337861209c259506a7dc68efbe83f3ce6759b Mon Sep 17 00:00:00 2001 From: Ben PHL Date: Wed, 30 Aug 2023 23:38:04 +0200 Subject: [PATCH 03/11] Move to derives crate --- derives/Cargo.toml | 3 +- derives/src/lib.rs | 40 ++++++++++++++++++++ {proc-macros => derives}/src/list_builder.rs | 4 +- proc-macros/Cargo.toml | 3 +- proc-macros/src/lib.rs | 40 -------------------- 5 files changed, 45 insertions(+), 45 deletions(-) rename {proc-macros => derives}/src/list_builder.rs (99%) diff --git a/derives/Cargo.toml b/derives/Cargo.toml index fdac7867e..14c30588b 100644 --- a/derives/Cargo.toml +++ b/derives/Cargo.toml @@ -16,8 +16,9 @@ travis-ci = { repository = "lloydmeta/frunk" } proc-macro = true [dependencies] -syn = "2" +syn = { version = "2", features = ["full"] } quote = "1" +proc-macro2 = "1.0.66" [dependencies.frunk_proc_macro_helpers] path = "../proc-macro-helpers" diff --git a/derives/src/lib.rs b/derives/src/lib.rs index 02e78c2fd..dcdb71506 100644 --- a/derives/src/lib.rs +++ b/derives/src/lib.rs @@ -23,6 +23,8 @@ use crate::derive_generic::impl_generic; mod derive_labelled_generic; use crate::derive_labelled_generic::impl_labelled_generic; +mod list_builder; + use quote::ToTokens; /// Derives a Generic instance based on HList for @@ -51,3 +53,41 @@ pub fn labelled_generic(input: TokenStream) -> TokenStream { // Return the generated impl gen.into_token_stream().into() } + +/// Constructs a struct using an `HList`. +/// +/// This trait allows you to create an instance of a struct from an HList. You keep the +/// remaining items in the `HList` after `DerivedListBuild::hl_new(...)` +/// +/// # Examples +/// +/// ```ignore +/// use frunk::hlist::HList; +/// use frunk::hlist; +/// +/// #[derive(Debug, Eq, PartialEq, frunk::hlist::ListBuild)] +/// struct ListConstructed { +/// field0: bool, +/// field1: u8, +/// #[list_build_ignore] +/// fn_built: &'static str, +/// } +/// +/// // Create a struct manually for comparison +/// let manually_made = ListConstructed { +/// field0: true, +/// field1: 3, +/// fn_built: "passed_in", +/// }; +/// +/// // Use `hl_new` to construct a struct and remaining HList +/// let (built, list): (ListConstructed, HList!(u32)) = +/// ListConstructed::hl_new(hlist![true, 3u8, 42u32], "passed_in"); +/// +/// assert_eq!(built, manually_made); +/// assert_eq!(list, hlist![42u32]); +/// ``` +#[proc_macro_derive(ListBuild, attributes(list_build_ignore))] +pub fn list_build(item: TokenStream) -> TokenStream { + list_builder::list_build_inner(item) +} diff --git a/proc-macros/src/list_builder.rs b/derives/src/list_builder.rs similarity index 99% rename from proc-macros/src/list_builder.rs rename to derives/src/list_builder.rs index 2fbcfa11a..2dfebaab1 100644 --- a/proc-macros/src/list_builder.rs +++ b/derives/src/list_builder.rs @@ -14,8 +14,8 @@ struct ArgPair { } impl ArgPair { - /// ``` - /// #[derive(ListBuild] + /// ```ignore + /// #[derive(ListBuild)] /// struct Foo { /// foo: u8, /// #[list_build_ignore] diff --git a/proc-macros/Cargo.toml b/proc-macros/Cargo.toml index 0c2518ad0..b190ef279 100644 --- a/proc-macros/Cargo.toml +++ b/proc-macros/Cargo.toml @@ -13,9 +13,8 @@ keywords = ["Frunk", "macros"] travis-ci = { repository = "lloydmeta/frunk" } [dependencies] -syn = {version = "2", features = ["full"] } +syn = "2" quote = "1" -proc-macro2 = "1.0.66" [lib] proc-macro = true diff --git a/proc-macros/src/lib.rs b/proc-macros/src/lib.rs index 2a90f34ad..5b718a935 100644 --- a/proc-macros/src/lib.rs +++ b/proc-macros/src/lib.rs @@ -19,8 +19,6 @@ use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, Expr}; -mod list_builder; - /// Build a generic path that can be used for traversals #[proc_macro] pub fn path(input: TokenStream) -> TokenStream { @@ -49,41 +47,3 @@ pub fn Path(input: TokenStream) -> TokenStream { TokenStream::from(ast) } - -/// Constructs a struct using an `HList`. -/// -/// This trait allows you to create an instance of a struct from an HList. You keep the -/// remaining items in the `HList` after `DerivedListBuild::hl_new(...)` -/// -/// # Examples -/// -/// ``` -/// use frunk::hlist::HList; -/// use frunk::hlist; -/// -/// #[derive(Debug, Eq, PartialEq, frunk::hlist::ListBuild)] -/// struct ListConstructed { -/// field0: bool, -/// field1: u8, -/// #[list_build_ignore] -/// fn_built: &'static str, -/// } -/// -/// // Create a struct manually for comparison -/// let manually_made = ListConstructed { -/// field0: true, -/// field1: 3, -/// fn_built: "passed_in", -/// }; -/// -/// // Use `hl_new` to construct a struct and remaining HList -/// let (built, list): (ListConstructed, HList!(u32)) = -/// ListConstructed::hl_new(hlist![true, 3u8, 42u32], "passed_in"); -/// -/// assert_eq!(built, manually_made); -/// assert_eq!(list, hlist![42u32]); -/// ``` -#[proc_macro_derive(ListBuild)] -pub fn list_build(item: TokenStream) -> TokenStream { - list_builder::list_build_inner(item) -} From 4875a5111d6637ac7c323b4800ce8cc6e10fa904 Mon Sep 17 00:00:00 2001 From: Ben PHL Date: Fri, 1 Sep 2023 13:11:16 +0200 Subject: [PATCH 04/11] Move type-helprs into dedicated modules --- derives/src/list_builder.rs | 151 +----------------- derives/src/list_builder/type_helpers.rs | 6 + .../src/list_builder/type_helpers/arg_pair.rs | 42 +++++ .../list_builder/type_helpers/pluck_param.rs | 12 ++ .../list_builder/type_helpers/where_line.rs | 93 +++++++++++ 5 files changed, 157 insertions(+), 147 deletions(-) create mode 100644 derives/src/list_builder/type_helpers.rs create mode 100644 derives/src/list_builder/type_helpers/arg_pair.rs create mode 100644 derives/src/list_builder/type_helpers/pluck_param.rs create mode 100644 derives/src/list_builder/type_helpers/where_line.rs diff --git a/derives/src/list_builder.rs b/derives/src/list_builder.rs index 2dfebaab1..7918e338d 100644 --- a/derives/src/list_builder.rs +++ b/derives/src/list_builder.rs @@ -3,155 +3,12 @@ use proc_macro::TokenStream; use proc_macro2::Span; use quote::quote; use syn::{ - parse_macro_input, punctuated::Punctuated, token::Comma, Block, DeriveInput, FnArg, - GenericParam, Generics, Ident, Stmt, WherePredicate, TypeParamBound + parse_macro_input, punctuated::Punctuated, token::Comma, Block, DeriveInput, + GenericParam, Generics, Ident, Stmt, }; -// struct field or function argument -struct ArgPair { - ident: syn::Ident, - tp: syn::Type -} - -impl ArgPair { - /// ```ignore - /// #[derive(ListBuild)] - /// struct Foo { - /// foo: u8, - /// #[list_build_ignore] - /// bar: u16 - /// } - /// // -> fn(l0: HList!(u8), bar: u16) - fn make_args(fields: Vec) -> impl Iterator { - std::iter::once(syn::parse2(quote!{l0: L0}).unwrap()).chain(fields.into_iter().map(FnArg::from)) - } -} -impl From<(syn::Ident, syn::Type)> for ArgPair { - fn from(value: (syn::Ident, syn::Type)) -> Self { - Self{ ident: value.0, tp: value.1 } - } -} -impl From for syn::FnArg { - fn from(value: ArgPair) -> Self { - syn::FnArg::Typed(syn::PatType { - attrs: Vec::new(), - pat: Box::new(syn::Pat::Ident(syn::PatIdent { - attrs: Vec::new(), - by_ref: None, - mutability: None, - ident: value.ident.clone(), - subpat: None, - })), - colon_token: syn::token::Colon { spans: [proc_macro2::Span::call_site()] }, - ty: Box::new(value.tp.clone()) - }) - } -} - -/// each line in the where predicate is the type-binding, and the trait it must impl -#[derive(Clone)] -struct WhereLine { - tp: syn::Type, - pred: PluckParam -} - -impl WhereLine { - /// entry into the recursive build-up of the where-lines - /// - /// generates the base-line (L0: Plucker) then goes into the recursive call - fn gen_lines_top(types: &[syn::Type]) -> Vec { - if types.len() == 0 { - panic!("no pluckings happening"); - } - let base = Self::gen_base(&types[0]); - if types.len() == 1 { - return vec![base]; - } - Self::gen_lines_recur(vec![base], &types[1..]) - } - - /// Beginning with the base line, it generates a new where-line for each type. - /// Each line is an "absorbed" version of the previous line, with `Plucker` as - /// the new trait-impl requirement. - /// - /// ```ignore - /// LN: Plucker // is saved, the next being... - /// >::Remainder: Plucker - /// ``` - fn gen_lines_recur(mut acc: Vec, types: &[syn::Type]) -> Vec { - // use the previous predicate to make the new type - let tp = acc.last().cloned().expect("should never recurse without the base...").absorb(); - - let pred = PluckParam::from((types[0].clone(), acc.len() as u8 + 1)); - acc.push(Self{tp, pred}); - if types.len() == 1 { - return acc; - } - Self::gen_lines_recur(acc, &types[1..]) - } - - /// L0: Plucker - fn gen_base(tp: &syn::Type) -> Self { - let pred = syn::parse2( quote!{frunk::hlist::Plucker<#tp, L1>} ).expect("quote the base plucker"); - // Create the WhereLine - WhereLine { - tp: syn::parse2( quote!{L0} ).expect("quote the L0"), - pred: PluckParam(pred), - } - } - - /// Does the "absorption" needed for the next line for each gen_lines_recur - /// Tn: Pn -> ::Remainder - fn absorb(self) -> syn::Type { - let WhereLine { tp, pred } = self; - let pred = pred.0; - - let res = quote!{<#tp as #pred>::Remainder}; - syn::parse2(res).expect("absorbing") - } -} - -impl From for WherePredicate { - fn from(line: WhereLine) -> Self { - let WhereLine { tp, pred } = line; - let bound: TypeParamBound = pred.0.into(); - let predicate = quote! { #tp: #bound }; - syn::parse2(predicate).expect("whereline to pred") - } -} - -/// shim allowing PredicateVec::from(line_vec).into() where a `syn::WhereClause` is needed -struct PredicateVec { - preds: Vec -} - -impl From> for PredicateVec { - fn from(value: Vec) -> Self { - Self{ preds: value.into_iter().map(WherePredicate::from).collect() } - } -} -impl From for syn::WhereClause { - fn from(value: PredicateVec) -> Self { - Self { - where_token: syn::Token![where](proc_macro2::Span::call_site()), - predicates: syn::punctuated::Punctuated::from_iter(value.preds), - } - } -} - -#[derive(Clone)] -struct PluckParam(syn::TypeParamBound); -impl From<(syn::Type, u8)> for PluckParam { - fn from((ty, n): (syn::Type, u8)) -> Self { - - - // Create an ident for "LN" where N is the u8 value - let l_ident = syn::Ident::new(&format!("L{}", n), Span::call_site()); - return Self(syn::parse2(quote!{frunk::hlist::Plucker<#ty, #l_ident>}).unwrap()); - } -} - - +mod type_helpers; +use type_helpers::{ArgPair, PredicateVec, WhereLine}; pub fn list_build_inner(item: TokenStream) -> TokenStream { let input = parse_macro_input!(item as DeriveInput); diff --git a/derives/src/list_builder/type_helpers.rs b/derives/src/list_builder/type_helpers.rs new file mode 100644 index 000000000..8de6388a2 --- /dev/null +++ b/derives/src/list_builder/type_helpers.rs @@ -0,0 +1,6 @@ +mod arg_pair; +pub(crate) use arg_pair::*; +mod where_line; +pub(crate) use where_line::*; +mod pluck_param; +pub(crate) use pluck_param::*; diff --git a/derives/src/list_builder/type_helpers/arg_pair.rs b/derives/src/list_builder/type_helpers/arg_pair.rs new file mode 100644 index 000000000..83354557a --- /dev/null +++ b/derives/src/list_builder/type_helpers/arg_pair.rs @@ -0,0 +1,42 @@ + + +// struct field or function argument +pub(crate) struct ArgPair { + ident: syn::Ident, + tp: syn::Type +} + +impl ArgPair { + /// ```ignore + /// #[derive(ListBuild)] + /// struct Foo { + /// foo: u8, + /// #[list_build_ignore] + /// bar: u16 + /// } + /// // -> fn(l0: HList!(u8), bar: u16) + pub(crate) fn make_args(fields: Vec) -> impl Iterator { + std::iter::once(syn::parse2(quote!{l0: L0}).unwrap()).chain(fields.into_iter().map(syn::FnArg::from)) + } +} +impl From<(syn::Ident, syn::Type)> for ArgPair { + fn from(value: (syn::Ident, syn::Type)) -> Self { + Self{ ident: value.0, tp: value.1 } + } +} +impl From for syn::FnArg { + fn from(value: ArgPair) -> Self { + syn::FnArg::Typed(syn::PatType { + attrs: Vec::new(), + pat: Box::new(syn::Pat::Ident(syn::PatIdent { + attrs: Vec::new(), + by_ref: None, + mutability: None, + ident: value.ident.clone(), + subpat: None, + })), + colon_token: syn::token::Colon { spans: [proc_macro2::Span::call_site()] }, + ty: Box::new(value.tp.clone()) + }) + } +} diff --git a/derives/src/list_builder/type_helpers/pluck_param.rs b/derives/src/list_builder/type_helpers/pluck_param.rs new file mode 100644 index 000000000..0af8f9150 --- /dev/null +++ b/derives/src/list_builder/type_helpers/pluck_param.rs @@ -0,0 +1,12 @@ + +#[derive(Clone)] +pub(crate) struct PluckParam(pub(crate) syn::TypeParamBound); +impl From<(syn::Type, u8)> for PluckParam { + fn from((ty, n): (syn::Type, u8)) -> Self { + + + // Create an ident for "LN" where N is the u8 value + let l_ident = syn::Ident::new(&format!("L{}", n), Span::call_site()); + return Self(syn::parse2(quote!{frunk::hlist::Plucker<#ty, #l_ident>}).unwrap()); + } +} diff --git a/derives/src/list_builder/type_helpers/where_line.rs b/derives/src/list_builder/type_helpers/where_line.rs new file mode 100644 index 000000000..47730afcf --- /dev/null +++ b/derives/src/list_builder/type_helpers/where_line.rs @@ -0,0 +1,93 @@ +use super::PluckParam; + + +#[derive(Clone)] +pub(crate) struct WhereLine { + tp: syn::Type, + pred: PluckParam +} + +impl WhereLine { + /// entry into the recursive build-up of the where-lines + /// + /// generates the base-line (L0: Plucker) then goes into the recursive call + pub(crate) fn gen_lines_top(types: &[syn::Type]) -> Vec { + if types.len() == 0 { + panic!("no pluckings happening"); + } + let base = Self::gen_base(&types[0]); + if types.len() == 1 { + return vec![base]; + } + Self::gen_lines_recur(vec![base], &types[1..]) + } + + /// Beginning with the base line, it generates a new where-line for each type. + /// Each line is an "absorbed" version of the previous line, with `Plucker` as + /// the new trait-impl requirement. + /// + /// ```ignore + /// LN: Plucker // is saved, the next being... + /// >::Remainder: Plucker + /// ``` + fn gen_lines_recur(mut acc: Vec, types: &[syn::Type]) -> Vec { + // use the previous predicate to make the new type + let tp = acc.last().cloned().expect("should never recurse without the base...").absorb(); + + let pred = PluckParam::from((types[0].clone(), acc.len() as u8 + 1)); + acc.push(Self{tp, pred}); + if types.len() == 1 { + return acc; + } + Self::gen_lines_recur(acc, &types[1..]) + } + + /// L0: Plucker + fn gen_base(tp: &syn::Type) -> Self { + let pred = syn::parse2( quote!{frunk::hlist::Plucker<#tp, L1>} ).expect("quote the base plucker"); + // Create the WhereLine + WhereLine { + tp: syn::parse2( quote!{L0} ).expect("quote the L0"), + pred: PluckParam(pred), + } + } + + /// Does the "absorption" needed for the next line for each gen_lines_recur + /// Tn: Pn -> ::Remainder + pub(crate) fn absorb(self) -> syn::Type { + let WhereLine { tp, pred } = self; + let pred = pred.0; + + let res = quote!{<#tp as #pred>::Remainder}; + syn::parse2(res).expect("absorbing") + } +} + +impl From for syn::WherePredicate { + fn from(line: WhereLine) -> Self { + let WhereLine { tp, pred } = line; + let bound: syn::TypeParamBound = pred.0.into(); + let predicate = quote! { #tp: #bound }; + syn::parse2(predicate).expect("whereline to pred") + } +} +/// each line in the where predicate is the type-binding, and the trait it must impl + +/// shim allowing PredicateVec::from(line_vec).into() where a `syn::WhereClause` is needed +pub(crate) struct PredicateVec { + pub(crate) preds: Vec +} + +impl From> for PredicateVec { + fn from(value: Vec) -> Self { + Self{ preds: value.into_iter().map(syn::WherePredicate::from).collect() } + } +} +impl From for syn::WhereClause { + fn from(value: PredicateVec) -> Self { + Self { + where_token: syn::Token![where](proc_macro2::Span::call_site()), + predicates: syn::punctuated::Punctuated::from_iter(value.preds), + } + } +} From bf9421eeed8af0c123bd8be62bfcd78cf00cf0d5 Mon Sep 17 00:00:00 2001 From: Ben PHL Date: Fri, 1 Sep 2023 13:13:58 +0200 Subject: [PATCH 05/11] clear doc-check warning --- derives/src/list_builder/type_helpers/where_line.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/derives/src/list_builder/type_helpers/where_line.rs b/derives/src/list_builder/type_helpers/where_line.rs index 47730afcf..92f6aa82d 100644 --- a/derives/src/list_builder/type_helpers/where_line.rs +++ b/derives/src/list_builder/type_helpers/where_line.rs @@ -53,7 +53,7 @@ impl WhereLine { } /// Does the "absorption" needed for the next line for each gen_lines_recur - /// Tn: Pn -> ::Remainder + /// `Tn: Pn` -> `::Remainder` pub(crate) fn absorb(self) -> syn::Type { let WhereLine { tp, pred } = self; let pred = pred.0; From 10100d929031170ae6cd23f70673a7a2b30a06dd Mon Sep 17 00:00:00 2001 From: Ben PHL Date: Fri, 1 Sep 2023 13:14:32 +0200 Subject: [PATCH 06/11] Cargo fmt --- derives/src/lib.rs | 2 +- derives/src/list_builder.rs | 44 +++++++++---------- .../src/list_builder/type_helpers/arg_pair.rs | 20 +++++---- .../list_builder/type_helpers/pluck_param.rs | 5 +-- .../list_builder/type_helpers/where_line.rs | 30 ++++++++----- proc-macros/src/lib.rs | 1 - 6 files changed, 53 insertions(+), 49 deletions(-) diff --git a/derives/src/lib.rs b/derives/src/lib.rs index dcdb71506..4a476f01d 100644 --- a/derives/src/lib.rs +++ b/derives/src/lib.rs @@ -81,7 +81,7 @@ pub fn labelled_generic(input: TokenStream) -> TokenStream { /// }; /// /// // Use `hl_new` to construct a struct and remaining HList -/// let (built, list): (ListConstructed, HList!(u32)) = +/// let (built, list): (ListConstructed, HList!(u32)) = /// ListConstructed::hl_new(hlist![true, 3u8, 42u32], "passed_in"); /// /// assert_eq!(built, manually_made); diff --git a/derives/src/list_builder.rs b/derives/src/list_builder.rs index 7918e338d..efed4e8f0 100644 --- a/derives/src/list_builder.rs +++ b/derives/src/list_builder.rs @@ -3,8 +3,8 @@ use proc_macro::TokenStream; use proc_macro2::Span; use quote::quote; use syn::{ - parse_macro_input, punctuated::Punctuated, token::Comma, Block, DeriveInput, - GenericParam, Generics, Ident, Stmt, + parse_macro_input, punctuated::Punctuated, token::Comma, Block, DeriveInput, GenericParam, + Generics, Ident, Stmt, }; mod type_helpers; @@ -15,12 +15,14 @@ pub fn list_build_inner(item: TokenStream) -> TokenStream { // get the fields: the list-built fields, and the manually-built fields let (input, annotated_fields, non_annotated_fields) = parse_fields(input); - let block = gen_stmts( - &annotated_fields.iter().map(|ArgPair{ident, ..}| ident.clone()).collect(), + &annotated_fields + .iter() + .map(|ArgPair { ident, .. }| ident.clone()) + .collect(), &non_annotated_fields .iter() - .map(|ArgPair{ident, ..}| ident.clone()) + .map(|ArgPair { ident, .. }| ident.clone()) .collect::>()[..], ); @@ -30,7 +32,10 @@ pub fn list_build_inner(item: TokenStream) -> TokenStream { if annotated_fields.len() == 0 { panic!("redundant builder annotations"); } - let types = annotated_fields.iter().map(|ArgPair{ tp, .. }| tp.clone()).collect::>(); + let types = annotated_fields + .iter() + .map(|ArgPair { tp, .. }| tp.clone()) + .collect::>(); // make all where-clauses let lines: Vec = WhereLine::gen_lines_top(&types[..]); @@ -38,8 +43,6 @@ pub fn list_build_inner(item: TokenStream) -> TokenStream { // Take the last clause and absorb that to make the ret-val let ret = WhereLine::absorb(lines.last().expect("last line").clone()); - - let fn_ident = syn::Ident::new("hl_new", proc_macro2::Span::call_site()); let output: syn::ReturnType = syn::ReturnType::Type( syn::token::RArrow::default(), @@ -88,21 +91,15 @@ pub fn list_build_inner(item: TokenStream) -> TokenStream { impl #struct_ident { #fun } - }.into() + } + .into() } /// collects the field name/type pairs, splitting them according to fields being built by the list /// or as args passed into the constructor -fn parse_fields( - mut input: DeriveInput, -) -> ( - DeriveInput, - Vec, - Vec, -) { +fn parse_fields(mut input: DeriveInput) -> (DeriveInput, Vec, Vec) { let mut list_built = Vec::new(); let mut ignored_fields = Vec::new(); - if let syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Named(named), .. @@ -122,14 +119,15 @@ fn parse_fields( } else { list_built.push((field_ident, field_type).into()); // Remove the hl_field attribute - field.attrs.retain(|attr| !attr.path().is_ident("list_build_ignore")); + field + .attrs + .retain(|attr| !attr.path().is_ident("list_build_ignore")); } } } (input, list_built, ignored_fields) } - // `` for the `fn hl_new` fn make_generic_params(count: usize) -> Punctuated { let gens: String = (0..count + 1) @@ -137,11 +135,12 @@ fn make_generic_params(count: usize) -> Punctuated { .collect::>() .join(", "); let gens = format!("<{}>", gens); - syn::parse_str::(&gens).expect("parsing the make_generic_params").params + syn::parse_str::(&gens) + .expect("parsing the make_generic_params") + .params } - -// generates a line of code for each field that needs a value plucked out of the constructor list. +// generates a line of code for each field that needs a value plucked out of the constructor list. // ```ignore // let (list_built0, l1) = frunk::hlist::Plucker::pluck(l0); // ``` @@ -177,4 +176,3 @@ fn gen_stmts(fields: &Vec, args: &[Ident]) -> Block { brace_token: Default::default(), } } - diff --git a/derives/src/list_builder/type_helpers/arg_pair.rs b/derives/src/list_builder/type_helpers/arg_pair.rs index 83354557a..e59957ca9 100644 --- a/derives/src/list_builder/type_helpers/arg_pair.rs +++ b/derives/src/list_builder/type_helpers/arg_pair.rs @@ -1,9 +1,7 @@ - - // struct field or function argument pub(crate) struct ArgPair { ident: syn::Ident, - tp: syn::Type + tp: syn::Type, } impl ArgPair { @@ -14,14 +12,18 @@ impl ArgPair { /// #[list_build_ignore] /// bar: u16 /// } - /// // -> fn(l0: HList!(u8), bar: u16) + /// // -> fn(l0: HList!(u8), bar: u16) pub(crate) fn make_args(fields: Vec) -> impl Iterator { - std::iter::once(syn::parse2(quote!{l0: L0}).unwrap()).chain(fields.into_iter().map(syn::FnArg::from)) + std::iter::once(syn::parse2(quote! {l0: L0}).unwrap()) + .chain(fields.into_iter().map(syn::FnArg::from)) } } impl From<(syn::Ident, syn::Type)> for ArgPair { fn from(value: (syn::Ident, syn::Type)) -> Self { - Self{ ident: value.0, tp: value.1 } + Self { + ident: value.0, + tp: value.1, + } } } impl From for syn::FnArg { @@ -35,8 +37,10 @@ impl From for syn::FnArg { ident: value.ident.clone(), subpat: None, })), - colon_token: syn::token::Colon { spans: [proc_macro2::Span::call_site()] }, - ty: Box::new(value.tp.clone()) + colon_token: syn::token::Colon { + spans: [proc_macro2::Span::call_site()], + }, + ty: Box::new(value.tp.clone()), }) } } diff --git a/derives/src/list_builder/type_helpers/pluck_param.rs b/derives/src/list_builder/type_helpers/pluck_param.rs index 0af8f9150..caa41c896 100644 --- a/derives/src/list_builder/type_helpers/pluck_param.rs +++ b/derives/src/list_builder/type_helpers/pluck_param.rs @@ -1,12 +1,9 @@ - #[derive(Clone)] pub(crate) struct PluckParam(pub(crate) syn::TypeParamBound); impl From<(syn::Type, u8)> for PluckParam { fn from((ty, n): (syn::Type, u8)) -> Self { - - // Create an ident for "LN" where N is the u8 value let l_ident = syn::Ident::new(&format!("L{}", n), Span::call_site()); - return Self(syn::parse2(quote!{frunk::hlist::Plucker<#ty, #l_ident>}).unwrap()); + return Self(syn::parse2(quote! {frunk::hlist::Plucker<#ty, #l_ident>}).unwrap()); } } diff --git a/derives/src/list_builder/type_helpers/where_line.rs b/derives/src/list_builder/type_helpers/where_line.rs index 92f6aa82d..95df70b3b 100644 --- a/derives/src/list_builder/type_helpers/where_line.rs +++ b/derives/src/list_builder/type_helpers/where_line.rs @@ -1,10 +1,9 @@ use super::PluckParam; - #[derive(Clone)] pub(crate) struct WhereLine { tp: syn::Type, - pred: PluckParam + pred: PluckParam, } impl WhereLine { @@ -19,7 +18,7 @@ impl WhereLine { if types.len() == 1 { return vec![base]; } - Self::gen_lines_recur(vec![base], &types[1..]) + Self::gen_lines_recur(vec![base], &types[1..]) } /// Beginning with the base line, it generates a new where-line for each type. @@ -32,22 +31,27 @@ impl WhereLine { /// ``` fn gen_lines_recur(mut acc: Vec, types: &[syn::Type]) -> Vec { // use the previous predicate to make the new type - let tp = acc.last().cloned().expect("should never recurse without the base...").absorb(); + let tp = acc + .last() + .cloned() + .expect("should never recurse without the base...") + .absorb(); let pred = PluckParam::from((types[0].clone(), acc.len() as u8 + 1)); - acc.push(Self{tp, pred}); + acc.push(Self { tp, pred }); if types.len() == 1 { return acc; } - Self::gen_lines_recur(acc, &types[1..]) + Self::gen_lines_recur(acc, &types[1..]) } /// L0: Plucker fn gen_base(tp: &syn::Type) -> Self { - let pred = syn::parse2( quote!{frunk::hlist::Plucker<#tp, L1>} ).expect("quote the base plucker"); + let pred = + syn::parse2(quote! {frunk::hlist::Plucker<#tp, L1>}).expect("quote the base plucker"); // Create the WhereLine WhereLine { - tp: syn::parse2( quote!{L0} ).expect("quote the L0"), + tp: syn::parse2(quote! {L0}).expect("quote the L0"), pred: PluckParam(pred), } } @@ -57,8 +61,8 @@ impl WhereLine { pub(crate) fn absorb(self) -> syn::Type { let WhereLine { tp, pred } = self; let pred = pred.0; - - let res = quote!{<#tp as #pred>::Remainder}; + + let res = quote! {<#tp as #pred>::Remainder}; syn::parse2(res).expect("absorbing") } } @@ -75,12 +79,14 @@ impl From for syn::WherePredicate { /// shim allowing PredicateVec::from(line_vec).into() where a `syn::WhereClause` is needed pub(crate) struct PredicateVec { - pub(crate) preds: Vec + pub(crate) preds: Vec, } impl From> for PredicateVec { fn from(value: Vec) -> Self { - Self{ preds: value.into_iter().map(syn::WherePredicate::from).collect() } + Self { + preds: value.into_iter().map(syn::WherePredicate::from).collect(), + } } } impl From for syn::WhereClause { diff --git a/proc-macros/src/lib.rs b/proc-macros/src/lib.rs index 5b718a935..8a5d9ab62 100644 --- a/proc-macros/src/lib.rs +++ b/proc-macros/src/lib.rs @@ -46,4 +46,3 @@ pub fn Path(input: TokenStream) -> TokenStream { // println!("ast: [{}]", ast); TokenStream::from(ast) } - From a3084b6a4786e845eec126bba54ca10941beb12c Mon Sep 17 00:00:00 2001 From: Ben PHL Date: Fri, 1 Sep 2023 14:14:31 +0200 Subject: [PATCH 07/11] Fix some import name-spacing --- derives/src/list_builder.rs | 2 +- derives/src/list_builder/type_helpers/pluck_param.rs | 4 +++- derives/src/list_builder/type_helpers/where_line.rs | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/derives/src/list_builder.rs b/derives/src/list_builder.rs index efed4e8f0..551aa0511 100644 --- a/derives/src/list_builder.rs +++ b/derives/src/list_builder.rs @@ -155,7 +155,7 @@ fn gen_stmts(fields: &Vec, args: &[Ident]) -> Block { let next_list = syn::Ident::new(&format!("l{}", next_list), Span::call_site()); let list_n_tok = syn::Ident::new(&format!("l{}", list_n), Span::call_site()); let stmt: Stmt = syn::parse2(quote! { - let (#id, #next_list) = frunk::hlist::Plucker::pluck(#list_n_tok); + let (#id, #next_list) = ::frunk::hlist::Plucker::pluck(#list_n_tok); }) .expect(""); stmts.push(stmt); diff --git a/derives/src/list_builder/type_helpers/pluck_param.rs b/derives/src/list_builder/type_helpers/pluck_param.rs index caa41c896..2dac25a71 100644 --- a/derives/src/list_builder/type_helpers/pluck_param.rs +++ b/derives/src/list_builder/type_helpers/pluck_param.rs @@ -1,9 +1,11 @@ +use proc_macro2::Span; + #[derive(Clone)] pub(crate) struct PluckParam(pub(crate) syn::TypeParamBound); impl From<(syn::Type, u8)> for PluckParam { fn from((ty, n): (syn::Type, u8)) -> Self { // Create an ident for "LN" where N is the u8 value let l_ident = syn::Ident::new(&format!("L{}", n), Span::call_site()); - return Self(syn::parse2(quote! {frunk::hlist::Plucker<#ty, #l_ident>}).unwrap()); + return Self(syn::parse2(quote! {::frunk::hlist::Plucker<#ty, #l_ident>}).unwrap()); } } diff --git a/derives/src/list_builder/type_helpers/where_line.rs b/derives/src/list_builder/type_helpers/where_line.rs index 95df70b3b..36ee6d351 100644 --- a/derives/src/list_builder/type_helpers/where_line.rs +++ b/derives/src/list_builder/type_helpers/where_line.rs @@ -48,7 +48,7 @@ impl WhereLine { /// L0: Plucker fn gen_base(tp: &syn::Type) -> Self { let pred = - syn::parse2(quote! {frunk::hlist::Plucker<#tp, L1>}).expect("quote the base plucker"); + syn::parse2(quote! {::frunk::hlist::Plucker<#tp, L1>}).expect("quote the base plucker"); // Create the WhereLine WhereLine { tp: syn::parse2(quote! {L0}).expect("quote the L0"), From eb45b29d988e764db1eb7c71fe0aaf4ed7fef7b1 Mon Sep 17 00:00:00 2001 From: Ben PHL Date: Sun, 3 Sep 2023 14:15:51 +0200 Subject: [PATCH 08/11] Add annotations to specify pluck-type and mapping --- derives/src/lib.rs | 3 +- derives/src/list_builder.rs | 97 +++++++++++++------ .../src/list_builder/type_helpers/arg_pair.rs | 4 +- .../list_builder/type_helpers/pluck_param.rs | 2 +- .../list_builder/type_helpers/where_line.rs | 2 +- 5 files changed, 76 insertions(+), 32 deletions(-) diff --git a/derives/src/lib.rs b/derives/src/lib.rs index 4a476f01d..e1149f089 100644 --- a/derives/src/lib.rs +++ b/derives/src/lib.rs @@ -1,5 +1,6 @@ #![recursion_limit = "128"] #![doc(html_playground_url = "https://play.rust-lang.org/")] +#![cfg_attr(feature = "nightly", feature(proc_macro_diagnostic))] //! Frunk Derives //! //! This library holds logic for the nice custom derives in Frunk. @@ -87,7 +88,7 @@ pub fn labelled_generic(input: TokenStream) -> TokenStream { /// assert_eq!(built, manually_made); /// assert_eq!(list, hlist![42u32]); /// ``` -#[proc_macro_derive(ListBuild, attributes(list_build_ignore))] +#[proc_macro_derive(ListBuild, attributes(list_build_ignore, plucker))] pub fn list_build(item: TokenStream) -> TokenStream { list_builder::list_build_inner(item) } diff --git a/derives/src/list_builder.rs b/derives/src/list_builder.rs index 551aa0511..b88bd878c 100644 --- a/derives/src/list_builder.rs +++ b/derives/src/list_builder.rs @@ -3,9 +3,11 @@ use proc_macro::TokenStream; use proc_macro2::Span; use quote::quote; use syn::{ - parse_macro_input, punctuated::Punctuated, token::Comma, Block, DeriveInput, GenericParam, - Generics, Ident, Stmt, + parse_macro_input, punctuated::Punctuated, token::Comma, Block, DeriveInput, + GenericParam, Generics, Ident, Stmt, }; +#[cfg(feature = "nightly")] +use syn::spanned::Spanned; mod type_helpers; use type_helpers::{ArgPair, PredicateVec, WhereLine}; @@ -16,10 +18,7 @@ pub fn list_build_inner(item: TokenStream) -> TokenStream { let (input, annotated_fields, non_annotated_fields) = parse_fields(input); let block = gen_stmts( - &annotated_fields - .iter() - .map(|ArgPair { ident, .. }| ident.clone()) - .collect(), + &annotated_fields, &non_annotated_fields .iter() .map(|ArgPair { ident, .. }| ident.clone()) @@ -34,7 +33,7 @@ pub fn list_build_inner(item: TokenStream) -> TokenStream { } let types = annotated_fields .iter() - .map(|ArgPair { tp, .. }| tp.clone()) + .map(|(ArgPair { tp, .. }, _)| tp.clone()) .collect::>(); // make all where-clauses @@ -96,7 +95,7 @@ pub fn list_build_inner(item: TokenStream) -> TokenStream { } /// collects the field name/type pairs, splitting them according to fields being built by the list /// or as args passed into the constructor -fn parse_fields(mut input: DeriveInput) -> (DeriveInput, Vec, Vec) { +fn parse_fields(mut input: DeriveInput) -> (DeriveInput, Vec<( ArgPair, Option )>, Vec) { let mut list_built = Vec::new(); let mut ignored_fields = Vec::new(); @@ -106,23 +105,55 @@ fn parse_fields(mut input: DeriveInput) -> (DeriveInput, Vec, Vec 1 + { + #[cfg(feature = "nightly")] + field + .span() + .unwrap() + .error("Redundant pluck-type on ignored field") + .emit(); + + eprintln!("field {} is annotated with a pluck-specification and a build-ignore", quote!{ #field }); + } + + let ignored_field = field .attrs .iter() .any(|attr| attr.path().is_ident("list_build_ignore")); - let field_ident = field.ident.clone().expect("field ident"); // Assuming named fields - let field_type = field.ty.clone(); // Type + let mut map_expr: Option = None; + let arg_parser = |input: syn::parse::ParseStream| { + let ty: syn::Type = input.parse().expect("type here"); + // Check for comma for additional arguments + let _ = input.parse::>().expect("comma here"); + let _ = input.parse::().expect("'map' here"); + input.parse::().expect("equalse sign here"); + map_expr = Some(input.parse().expect("parsing expression")); + Ok(ty) + + }; + let ty = field + .attrs + .iter() + .find(|attr| attr.path().is_ident("plucker")) + .map(|atr| atr.parse_args_with(arg_parser).expect("mapping with arg parser")) + .unwrap_or(field.ty.clone()); - if ignored { - ignored_fields.push((field_ident, field_type).into()); + if ignored_field { + ignored_fields.push((field.ident.clone().expect("field_ident"), ty).into()); } else { - list_built.push((field_ident, field_type).into()); - // Remove the hl_field attribute - field - .attrs - .retain(|attr| !attr.path().is_ident("list_build_ignore")); - } + list_built.push(((field.ident.clone().expect("field_ident"), ty).into(), map_expr)); + }; } } (input, list_built, ignored_fields) @@ -146,29 +177,40 @@ fn make_generic_params(count: usize) -> Punctuated { // ``` // ...and for the fileds ignored, just moves from the function argument to the rusulting structs // field -fn gen_stmts(fields: &Vec, args: &[Ident]) -> Block { +fn gen_stmts(fields: &Vec<(ArgPair, Option)>, args: &[Ident]) -> Block { let mut list_n = 0; let mut stmts: Vec = vec![]; // Generate the "let (field, lX) = lY.pluck();" statements - for id in fields { + for (arg_pair, expr) in fields { let next_list = list_n + 1; let next_list = syn::Ident::new(&format!("l{}", next_list), Span::call_site()); let list_n_tok = syn::Ident::new(&format!("l{}", list_n), Span::call_site()); - let stmt: Stmt = syn::parse2(quote! { - let (#id, #next_list) = ::frunk::hlist::Plucker::pluck(#list_n_tok); - }) - .expect(""); + let field_name = &arg_pair.ident; + let plucking = quote! { + let (#field_name, #next_list) = frunk::hlist::Plucker::pluck(#list_n_tok); + }; + + + let stmt: Stmt = syn::parse2(plucking.clone()) + .unwrap_or_else(|_| panic!("Failed to parse statement: {}", plucking.to_string())); + stmts.push(stmt); + if let Some(expr) = expr { + let mapping = quote!{ + let #field_name = #expr; + }; + stmts.push(syn::parse2(mapping).unwrap()); + }; list_n += 1; } // Generate the "Self { fields... }" part of the block - let all_fields = [&fields[..], args].concat(); + let all_fields = [&fields.iter().map(|(field, _)| field.ident.clone()).collect::>()[..], args].concat(); let list_n_ident = syn::Ident::new(&format!("l{}", list_n), proc_macro2::Span::call_site()); let self_stmt: Stmt = syn::parse2(quote! { return (Self { #(#all_fields,)* }, #list_n_ident); }) - .expect(""); + .expect("generating the Self..."); stmts.push(self_stmt); Block { @@ -176,3 +218,4 @@ fn gen_stmts(fields: &Vec, args: &[Ident]) -> Block { brace_token: Default::default(), } } + diff --git a/derives/src/list_builder/type_helpers/arg_pair.rs b/derives/src/list_builder/type_helpers/arg_pair.rs index e59957ca9..039d7ce55 100644 --- a/derives/src/list_builder/type_helpers/arg_pair.rs +++ b/derives/src/list_builder/type_helpers/arg_pair.rs @@ -1,7 +1,7 @@ // struct field or function argument pub(crate) struct ArgPair { - ident: syn::Ident, - tp: syn::Type, + pub(crate) ident: syn::Ident, + pub(crate) tp: syn::Type, } impl ArgPair { diff --git a/derives/src/list_builder/type_helpers/pluck_param.rs b/derives/src/list_builder/type_helpers/pluck_param.rs index 2dac25a71..5382142df 100644 --- a/derives/src/list_builder/type_helpers/pluck_param.rs +++ b/derives/src/list_builder/type_helpers/pluck_param.rs @@ -6,6 +6,6 @@ impl From<(syn::Type, u8)> for PluckParam { fn from((ty, n): (syn::Type, u8)) -> Self { // Create an ident for "LN" where N is the u8 value let l_ident = syn::Ident::new(&format!("L{}", n), Span::call_site()); - return Self(syn::parse2(quote! {::frunk::hlist::Plucker<#ty, #l_ident>}).unwrap()); + return Self(syn::parse2(quote! {frunk::hlist::Plucker<#ty, #l_ident>}).unwrap()); } } diff --git a/derives/src/list_builder/type_helpers/where_line.rs b/derives/src/list_builder/type_helpers/where_line.rs index 36ee6d351..95df70b3b 100644 --- a/derives/src/list_builder/type_helpers/where_line.rs +++ b/derives/src/list_builder/type_helpers/where_line.rs @@ -48,7 +48,7 @@ impl WhereLine { /// L0: Plucker fn gen_base(tp: &syn::Type) -> Self { let pred = - syn::parse2(quote! {::frunk::hlist::Plucker<#tp, L1>}).expect("quote the base plucker"); + syn::parse2(quote! {frunk::hlist::Plucker<#tp, L1>}).expect("quote the base plucker"); // Create the WhereLine WhereLine { tp: syn::parse2(quote! {L0}).expect("quote the L0"), From 667f8f34d3be275c3f65543964f0eebc0ef91489 Mon Sep 17 00:00:00 2001 From: Ben PHL Date: Sun, 3 Sep 2023 17:01:24 +0200 Subject: [PATCH 09/11] Minor cleanup --- derives/src/list_builder.rs | 14 ++++++-------- .../src/list_builder/type_helpers/pluck_param.rs | 1 + .../src/list_builder/type_helpers/where_line.rs | 1 - 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/derives/src/list_builder.rs b/derives/src/list_builder.rs index b88bd878c..f448bc808 100644 --- a/derives/src/list_builder.rs +++ b/derives/src/list_builder.rs @@ -4,7 +4,7 @@ use proc_macro2::Span; use quote::quote; use syn::{ parse_macro_input, punctuated::Punctuated, token::Comma, Block, DeriveInput, - GenericParam, Generics, Ident, Stmt, + GenericParam, Generics, Stmt, }; #[cfg(feature = "nightly")] use syn::spanned::Spanned; @@ -19,10 +19,7 @@ pub fn list_build_inner(item: TokenStream) -> TokenStream { let block = gen_stmts( &annotated_fields, - &non_annotated_fields - .iter() - .map(|ArgPair { ident, .. }| ident.clone()) - .collect::>()[..], + &non_annotated_fields, ); // hl_new args include the injected list, and values for the non-list built args @@ -37,7 +34,7 @@ pub fn list_build_inner(item: TokenStream) -> TokenStream { .collect::>(); // make all where-clauses - let lines: Vec = WhereLine::gen_lines_top(&types[..]); + let lines: Vec = WhereLine::gen_lines_top(&types); // Take the last clause and absorb that to make the ret-val let ret = WhereLine::absorb(lines.last().expect("last line").clone()); @@ -177,7 +174,7 @@ fn make_generic_params(count: usize) -> Punctuated { // ``` // ...and for the fileds ignored, just moves from the function argument to the rusulting structs // field -fn gen_stmts(fields: &Vec<(ArgPair, Option)>, args: &[Ident]) -> Block { +fn gen_stmts(fields: &[(ArgPair, Option)], args: &[ArgPair]) -> Block { let mut list_n = 0; let mut stmts: Vec = vec![]; // Generate the "let (field, lX) = lY.pluck();" statements @@ -205,7 +202,8 @@ fn gen_stmts(fields: &Vec<(ArgPair, Option)>, args: &[Ident]) -> Bloc } // Generate the "Self { fields... }" part of the block - let all_fields = [&fields.iter().map(|(field, _)| field.ident.clone()).collect::>()[..], args].concat(); + let args = args.iter().map(|ArgPair{ ident, .. }| ident.clone()).collect::>(); + let all_fields = [&fields.iter().map(|(field, _)| &field.ident.clone()).collect::>(), &args].concat(); let list_n_ident = syn::Ident::new(&format!("l{}", list_n), proc_macro2::Span::call_site()); let self_stmt: Stmt = syn::parse2(quote! { return (Self { #(#all_fields,)* }, #list_n_ident); diff --git a/derives/src/list_builder/type_helpers/pluck_param.rs b/derives/src/list_builder/type_helpers/pluck_param.rs index 5382142df..10dee5980 100644 --- a/derives/src/list_builder/type_helpers/pluck_param.rs +++ b/derives/src/list_builder/type_helpers/pluck_param.rs @@ -1,5 +1,6 @@ use proc_macro2::Span; +/// Shim to allow a type-conversions fom a type + LN pair into a paramater bound #[derive(Clone)] pub(crate) struct PluckParam(pub(crate) syn::TypeParamBound); impl From<(syn::Type, u8)> for PluckParam { diff --git a/derives/src/list_builder/type_helpers/where_line.rs b/derives/src/list_builder/type_helpers/where_line.rs index 95df70b3b..44ecf30d6 100644 --- a/derives/src/list_builder/type_helpers/where_line.rs +++ b/derives/src/list_builder/type_helpers/where_line.rs @@ -75,7 +75,6 @@ impl From for syn::WherePredicate { syn::parse2(predicate).expect("whereline to pred") } } -/// each line in the where predicate is the type-binding, and the trait it must impl /// shim allowing PredicateVec::from(line_vec).into() where a `syn::WhereClause` is needed pub(crate) struct PredicateVec { From 19c53599512e5e8441f47c1b5a481128fd61c290 Mon Sep 17 00:00:00 2001 From: Ben PHL Date: Sun, 3 Sep 2023 20:18:16 +0200 Subject: [PATCH 10/11] Organise annotation management --- derives/src/list_builder.rs | 84 ++++++------------- derives/src/list_builder/type_helpers.rs | 2 + .../list_builder/type_helpers/annotations.rs | 64 ++++++++++++++ 3 files changed, 93 insertions(+), 57 deletions(-) create mode 100644 derives/src/list_builder/type_helpers/annotations.rs diff --git a/derives/src/list_builder.rs b/derives/src/list_builder.rs index f448bc808..544858f50 100644 --- a/derives/src/list_builder.rs +++ b/derives/src/list_builder.rs @@ -10,25 +10,27 @@ use syn::{ use syn::spanned::Spanned; mod type_helpers; -use type_helpers::{ArgPair, PredicateVec, WhereLine}; +use type_helpers::{ArgPair, PredicateVec, WhereLine, Annotation}; + +use self::type_helpers::AnnoErr; pub fn list_build_inner(item: TokenStream) -> TokenStream { let input = parse_macro_input!(item as DeriveInput); // get the fields: the list-built fields, and the manually-built fields - let (input, annotated_fields, non_annotated_fields) = parse_fields(input); + let (input, list_built_fields, ignored_fields) = parse_fields(input); let block = gen_stmts( - &annotated_fields, - &non_annotated_fields, + &list_built_fields, + &ignored_fields, ); // hl_new args include the injected list, and values for the non-list built args - let args = ArgPair::make_args(non_annotated_fields); + let args = ArgPair::make_args(ignored_fields); - if annotated_fields.len() == 0 { + if list_built_fields.len() == 0 { panic!("redundant builder annotations"); } - let types = annotated_fields + let types = list_built_fields .iter() .map(|(ArgPair { tp, .. }, _)| tp.clone()) .collect::>(); @@ -60,7 +62,7 @@ pub fn list_build_inner(item: TokenStream) -> TokenStream { ident: fn_ident, generics: Generics { where_clause: Some(PredicateVec::from(lines).into()), - params: make_generic_params(annotated_fields.len()), + params: make_generic_params(list_built_fields.len()), ..Default::default() }, inputs: Punctuated::from_iter(args), @@ -101,56 +103,24 @@ fn parse_fields(mut input: DeriveInput) -> (DeriveInput, Vec<( ArgPair, Option 1 - { - #[cfg(feature = "nightly")] - field - .span() - .unwrap() - .error("Redundant pluck-type on ignored field") - .emit(); - - eprintln!("field {} is annotated with a pluck-specification and a build-ignore", quote!{ #field }); - } - - let ignored_field = field - .attrs - .iter() - .any(|attr| attr.path().is_ident("list_build_ignore")); - - let mut map_expr: Option = None; - let arg_parser = |input: syn::parse::ParseStream| { - let ty: syn::Type = input.parse().expect("type here"); - // Check for comma for additional arguments - let _ = input.parse::>().expect("comma here"); - let _ = input.parse::().expect("'map' here"); - input.parse::().expect("equalse sign here"); - map_expr = Some(input.parse().expect("parsing expression")); - Ok(ty) - - }; - let ty = field - .attrs - .iter() - .find(|attr| attr.path().is_ident("plucker")) - .map(|atr| atr.parse_args_with(arg_parser).expect("mapping with arg parser")) - .unwrap_or(field.ty.clone()); - - if ignored_field { - ignored_fields.push((field.ident.clone().expect("field_ident"), ty).into()); - } else { - list_built.push(((field.ident.clone().expect("field_ident"), ty).into(), map_expr)); + match Annotation::try_from(&field.attrs[..]) { + Ok(Annotation::Plucker { ty, map }) => list_built.push(((field.ident.clone().expect("field_ident"), ty).into(), Some(map))), + Ok(Annotation::Ignore) => ignored_fields.push((field.ident.clone().expect("field_ident"), field.ty.clone()).into()), + Err(AnnoErr::NoMatch) => todo!(), + Err(AnnoErr::XOR) => { + #[cfg(feature = "nightly")] + field + .span() + .unwrap() + .error("Redundant pluck-type on ignored field") + .emit(); + #[cfg(not(feature = "nightly"))] + panic!("don't ignore fields with pluckers"); + } }; + } } (input, list_built, ignored_fields) @@ -203,7 +173,7 @@ fn gen_stmts(fields: &[(ArgPair, Option)], args: &[ArgPair]) -> Block // Generate the "Self { fields... }" part of the block let args = args.iter().map(|ArgPair{ ident, .. }| ident.clone()).collect::>(); - let all_fields = [&fields.iter().map(|(field, _)| &field.ident.clone()).collect::>(), &args].concat(); + let all_fields = [&fields.iter().map(|(field, _)| field.ident.clone()).collect::>()[..], &args[..]].concat(); let list_n_ident = syn::Ident::new(&format!("l{}", list_n), proc_macro2::Span::call_site()); let self_stmt: Stmt = syn::parse2(quote! { return (Self { #(#all_fields,)* }, #list_n_ident); diff --git a/derives/src/list_builder/type_helpers.rs b/derives/src/list_builder/type_helpers.rs index 8de6388a2..957a02f1d 100644 --- a/derives/src/list_builder/type_helpers.rs +++ b/derives/src/list_builder/type_helpers.rs @@ -4,3 +4,5 @@ mod where_line; pub(crate) use where_line::*; mod pluck_param; pub(crate) use pluck_param::*; +mod annotations; +pub(crate) use annotations::*; diff --git a/derives/src/list_builder/type_helpers/annotations.rs b/derives/src/list_builder/type_helpers/annotations.rs new file mode 100644 index 000000000..a2f22eaa2 --- /dev/null +++ b/derives/src/list_builder/type_helpers/annotations.rs @@ -0,0 +1,64 @@ + +pub(crate) enum Annotation { + Plucker{ty: syn::Type, map: syn::Expr}, + Ignore, +} + +pub(crate) enum AnnoErr { + XOR, + NoMatch +} +impl core::convert::TryFrom<&[syn::Attribute]> for Annotation { + type Error = AnnoErr; + fn try_from(attrs: &[syn::Attribute]) -> Result { + let mut annos = attrs.iter().filter_map(|attr| Self::try_from(attr).ok()); + let Some(fst) = annos.next() else { + return Err(AnnoErr::NoMatch); + }; + + if let Some(_) = annos.next() { + return Err(AnnoErr::XOR); + } + Ok(fst) + + } +} +impl TryFrom<&syn::Attribute> for Annotation { + type Error = (); + + fn try_from(attr: &syn::Attribute) -> Result { + match attr.path().get_ident().map(|id| quote!{#id}.to_string()) { + Some(ref s) if s == "plucker" => { + let arg_parser = |input: syn::parse::ParseStream| { + // Eg, with the arg: u8, map=core::convert::From::from(arg_name) + + + // consume leading type + let ty: syn::Type = input.parse().expect("type here"); + // , map=core::convert::From::from(arg_name) + // consume comma + let _ = input.parse::>().expect("comma here"); + // map=core::convert::From::from(arg_name) + // consume `map` + let _ = input.parse::().expect("'map' here"); + // =core::convert::From::from(arg_name) + // consume `=` + input.parse::().expect("equalse sign here"); + // core::convert::From::from(arg_name) + // parse in the expression + let mapping = input.parse().expect("parsing expression"); + Ok((ty, mapping)) + + }; + + let (ty, map) = attr.parse_args_with(arg_parser).expect("mapping with arg parser"); + Ok(Annotation::Plucker{ty, map}) + } + Some(ref s) if s == "list_build_ignore" => Ok(Self::Ignore), + _ => Err(()) + + } + + } +} + From 699707fc2e0fc0c5682d1f9aa9af9843c547e550 Mon Sep 17 00:00:00 2001 From: Ben PHL Date: Sun, 3 Sep 2023 21:38:03 +0200 Subject: [PATCH 11/11] Use quote! more --- derives/src/list_builder.rs | 95 ++++++++----------- .../list_builder/type_helpers/annotations.rs | 21 ++-- 2 files changed, 45 insertions(+), 71 deletions(-) diff --git a/derives/src/list_builder.rs b/derives/src/list_builder.rs index 544858f50..e25025036 100644 --- a/derives/src/list_builder.rs +++ b/derives/src/list_builder.rs @@ -2,15 +2,15 @@ extern crate proc_macro; use proc_macro::TokenStream; use proc_macro2::Span; use quote::quote; -use syn::{ - parse_macro_input, punctuated::Punctuated, token::Comma, Block, DeriveInput, - GenericParam, Generics, Stmt, -}; #[cfg(feature = "nightly")] use syn::spanned::Spanned; +use syn::{ + parse_macro_input, punctuated::Punctuated, token::Comma, Block, DeriveInput, GenericParam, + Generics, Stmt, +}; mod type_helpers; -use type_helpers::{ArgPair, PredicateVec, WhereLine, Annotation}; +use type_helpers::{Annotation, ArgPair, PredicateVec, WhereLine}; use self::type_helpers::AnnoErr; @@ -19,10 +19,7 @@ pub fn list_build_inner(item: TokenStream) -> TokenStream { // get the fields: the list-built fields, and the manually-built fields let (input, list_built_fields, ignored_fields) = parse_fields(input); - let block = gen_stmts( - &list_built_fields, - &ignored_fields, - ); + let block = gen_stmts(&list_built_fields, &ignored_fields); // hl_new args include the injected list, and values for the non-list built args let args = ArgPair::make_args(ignored_fields); @@ -41,47 +38,16 @@ pub fn list_build_inner(item: TokenStream) -> TokenStream { // Take the last clause and absorb that to make the ret-val let ret = WhereLine::absorb(lines.last().expect("last line").clone()); - let fn_ident = syn::Ident::new("hl_new", proc_macro2::Span::call_site()); - let output: syn::ReturnType = syn::ReturnType::Type( - syn::token::RArrow::default(), - Box::new(syn::Type::Tuple(syn::TypeTuple { - paren_token: syn::token::Paren::default(), - elems: { - let mut punctuated = Punctuated::new(); - punctuated.push(syn::Type::Path(syn::TypePath { - qself: None, - path: syn::parse_str("Self").expect("parseing Self"), - })); - punctuated.push(ret); - punctuated - }, - })), - ); - // tie all the signature details together - let sig = syn::Signature { - ident: fn_ident, - generics: Generics { - where_clause: Some(PredicateVec::from(lines).into()), - params: make_generic_params(list_built_fields.len()), - ..Default::default() - }, - inputs: Punctuated::from_iter(args), - - output, - constness: None, - asyncness: None, - unsafety: None, - abi: None, - variadic: None, - fn_token: Default::default(), - paren_token: Default::default(), - }; + let output = quote! { -> (Self, #ret) }; + let where_clause: syn::WhereClause = PredicateVec::from(lines.clone()).into(); + let gens = make_generic_params(list_built_fields.len()); let struct_ident = input.clone().ident; - let fun = syn::ItemFn { - attrs: vec![], - vis: syn::Visibility::Inherited, - sig, - block: Box::new(block), + let fun = quote! { + fn hl_new<#gens>(#(#args),*) #output + #where_clause + { + #block + } }; // et, voile! en a des code magnifique! @@ -94,7 +60,9 @@ pub fn list_build_inner(item: TokenStream) -> TokenStream { } /// collects the field name/type pairs, splitting them according to fields being built by the list /// or as args passed into the constructor -fn parse_fields(mut input: DeriveInput) -> (DeriveInput, Vec<( ArgPair, Option )>, Vec) { +fn parse_fields( + mut input: DeriveInput, +) -> (DeriveInput, Vec<(ArgPair, Option)>, Vec) { let mut list_built = Vec::new(); let mut ignored_fields = Vec::new(); @@ -106,8 +74,12 @@ fn parse_fields(mut input: DeriveInput) -> (DeriveInput, Vec<( ArgPair, Option list_built.push(((field.ident.clone().expect("field_ident"), ty).into(), Some(map))), - Ok(Annotation::Ignore) => ignored_fields.push((field.ident.clone().expect("field_ident"), field.ty.clone()).into()), + Ok(Annotation::Plucker { ty, map }) => list_built.push(( + (field.ident.clone().expect("field_ident"), ty).into(), + Some(map), + )), + Ok(Annotation::Ignore) => ignored_fields + .push((field.ident.clone().expect("field_ident"), field.ty.clone()).into()), Err(AnnoErr::NoMatch) => todo!(), Err(AnnoErr::XOR) => { #[cfg(feature = "nightly")] @@ -120,7 +92,6 @@ fn parse_fields(mut input: DeriveInput) -> (DeriveInput, Vec<( ArgPair, Option)], args: &[ArgPair]) -> Block let (#field_name, #next_list) = frunk::hlist::Plucker::pluck(#list_n_tok); }; - let stmt: Stmt = syn::parse2(plucking.clone()) .unwrap_or_else(|_| panic!("Failed to parse statement: {}", plucking.to_string())); stmts.push(stmt); if let Some(expr) = expr { - let mapping = quote!{ + let mapping = quote! { let #field_name = #expr; }; stmts.push(syn::parse2(mapping).unwrap()); @@ -172,8 +142,18 @@ fn gen_stmts(fields: &[(ArgPair, Option)], args: &[ArgPair]) -> Block } // Generate the "Self { fields... }" part of the block - let args = args.iter().map(|ArgPair{ ident, .. }| ident.clone()).collect::>(); - let all_fields = [&fields.iter().map(|(field, _)| field.ident.clone()).collect::>()[..], &args[..]].concat(); + let args = args + .iter() + .map(|ArgPair { ident, .. }| ident.clone()) + .collect::>(); + let all_fields = [ + &fields + .iter() + .map(|(field, _)| field.ident.clone()) + .collect::>()[..], + &args[..], + ] + .concat(); let list_n_ident = syn::Ident::new(&format!("l{}", list_n), proc_macro2::Span::call_site()); let self_stmt: Stmt = syn::parse2(quote! { return (Self { #(#all_fields,)* }, #list_n_ident); @@ -186,4 +166,3 @@ fn gen_stmts(fields: &[(ArgPair, Option)], args: &[ArgPair]) -> Block brace_token: Default::default(), } } - diff --git a/derives/src/list_builder/type_helpers/annotations.rs b/derives/src/list_builder/type_helpers/annotations.rs index a2f22eaa2..ae15aa817 100644 --- a/derives/src/list_builder/type_helpers/annotations.rs +++ b/derives/src/list_builder/type_helpers/annotations.rs @@ -1,12 +1,11 @@ - pub(crate) enum Annotation { - Plucker{ty: syn::Type, map: syn::Expr}, + Plucker { ty: syn::Type, map: syn::Expr }, Ignore, } pub(crate) enum AnnoErr { XOR, - NoMatch + NoMatch, } impl core::convert::TryFrom<&[syn::Attribute]> for Annotation { type Error = AnnoErr; @@ -20,19 +19,17 @@ impl core::convert::TryFrom<&[syn::Attribute]> for Annotation { return Err(AnnoErr::XOR); } Ok(fst) - } } impl TryFrom<&syn::Attribute> for Annotation { type Error = (); fn try_from(attr: &syn::Attribute) -> Result { - match attr.path().get_ident().map(|id| quote!{#id}.to_string()) { + match attr.path().get_ident().map(|id| quote! {#id}.to_string()) { Some(ref s) if s == "plucker" => { let arg_parser = |input: syn::parse::ParseStream| { // Eg, with the arg: u8, map=core::convert::From::from(arg_name) - // consume leading type let ty: syn::Type = input.parse().expect("type here"); // , map=core::convert::From::from(arg_name) @@ -48,17 +45,15 @@ impl TryFrom<&syn::Attribute> for Annotation { // parse in the expression let mapping = input.parse().expect("parsing expression"); Ok((ty, mapping)) - }; - let (ty, map) = attr.parse_args_with(arg_parser).expect("mapping with arg parser"); - Ok(Annotation::Plucker{ty, map}) + let (ty, map) = attr + .parse_args_with(arg_parser) + .expect("mapping with arg parser"); + Ok(Annotation::Plucker { ty, map }) } Some(ref s) if s == "list_build_ignore" => Ok(Self::Ignore), - _ => Err(()) - + _ => Err(()), } - } } -