From ddca7cc452708be0e577f4b9a2a7998540b4d1e1 Mon Sep 17 00:00:00 2001 From: Luca Fulchir Date: Wed, 27 Mar 2024 17:58:18 +0100 Subject: [PATCH] ::bok::impl_package to simplify trait implemet Signed-off-by: Luca Fulchir --- bok-macro/src/lib.rs | 192 +++++++++++++++++++++++------------- bok-utils/src/pkgs/one.rs | 8 +- bok-utils/src/pkgs/three.rs | 6 ++ bok-utils/src/pkgs/two.rs | 2 + bok/src/lib.rs | 26 ++--- 5 files changed, 148 insertions(+), 86 deletions(-) diff --git a/bok-macro/src/lib.rs b/bok-macro/src/lib.rs index 56affd3..33d17f3 100644 --- a/bok-macro/src/lib.rs +++ b/bok-macro/src/lib.rs @@ -21,6 +21,7 @@ use ::syn::{parse::Parser, parse_macro_input, DeriveInput}; /// Use as #[::bok::repository(MyBaseRepo)] /// Will setup a `base` field that is the given base repo +/// and add `#[derive(::bok::Repository)]` /// /// e.g.: `#[::bok::repository(::bok::RepositoryEmpty)]` #[proc_macro_attribute] @@ -51,8 +52,15 @@ pub fn repository(attrs: TokenStream, input: TokenStream) -> TokenStream { } /// Unless you know what you are doing, use `#[::bok::repository(MyBaseRepo)]` instead +/// needs a `.base` field /// -/// Use as #[derive(::bok::Repository)] +/// Use like: +/// ``` +/// #[derive(::bok::Repository)] +/// pub struct MyRepo { +/// base : BaseRepo, +/// } +/// ``` /// adds extension capabilities to a repo #[proc_macro_derive(Repository)] pub fn derive_repository(input: TokenStream) -> TokenStream { @@ -94,14 +102,18 @@ pub fn derive_repository(input: TokenStream) -> TokenStream { TokenStream::from(expanded) } -/// Use on a function as `#[::bok::to_code]` + +/// Use on a function as `#[::bok::pkg_fn_to_code]` /// will create a new function, same name with `_code` that /// returns the ::proc_macro2::TokenStream of the function /// +/// **Don't use this if you are using `#[::bok::package(...)]` +/// it is already automatically added to all members of trait `::bok::Pkg`** +/// /// e.g.: /// ``` /// impl MyPackage { -/// #[::bok::to_code] +/// #[::bok::pkg_fn_to_code] /// fn build() { /// ... /// } @@ -110,10 +122,23 @@ pub fn derive_repository(input: TokenStream) -> TokenStream { /// let tokens : ::proc_macro2::TokenStream = p.build_code() /// ``` #[proc_macro_attribute] -pub fn to_code(_attrs: TokenStream, input: TokenStream) -> TokenStream { - let ast_orig: ::syn::ItemFn = parse_macro_input!(input as ::syn::ItemFn); +pub fn pkg_fn_to_code(_attrs: TokenStream, input: TokenStream) -> TokenStream { + let mut ast_orig: ::syn::ItemFn = + parse_macro_input!(input as ::syn::ItemFn); let mut ast_new = ast_orig.clone(); + ast_new.block = ast_orig.block.clone(); + + // Add a prefix and suffix statement + // where we declare and return the result + let prefix_ret: ::syn::Stmt = ::syn::parse_quote! { + let mut ret : ::core::result::Result<(),()> = ::core::result::Result::Err(()); + }; + let suffix_ret: ::syn::Stmt = ::syn::parse_quote! { + return ret; + }; + ast_orig.block.stmts.insert(0, prefix_ret); + ast_orig.block.stmts.push(suffix_ret); let new_fn_name = quote::format_ident!("{}", ast_orig.sig.ident.to_string() + "_code"); @@ -121,66 +146,25 @@ pub fn to_code(_attrs: TokenStream, input: TokenStream) -> TokenStream { use ::quote::ToTokens; let fn_block = ast_new.block.to_token_stream(); - //let quote_macro = ::quote::format_ident!("quote"); - let asdf = quote! { #ast_orig - fn #new_fn_name () -> ::proc_macro2::TokenStream { - //::quote::#quote_macro! { + fn #new_fn_name (&self) -> ::proc_macro2::TokenStream { ::quote::quote! #fn_block - //} } }; asdf.into() - /* - let fn_block_pb = ::syn::punctuated::Punctuated::< - ::syn::Expr, - ::syn::Token![,], - >::parse_terminated - .parse2(fn_block) - .unwrap(); - */ - /* - let fn_block_pb = ::syn::parse::Parser::parse2( - //::syn::punctuated::Punctuated::<::syn::Expr, ::syn::Token![,]>::parse_terminated, - |input: ParseStream<'_>| ::syn::Result::Ok({ input.clone() }), - fn_block, - ) - .unwrap(); - */ - /* - let fn_block_pb = ::syn::punctuated::Punctuated::< - //::syn::parse::ParseBuffer<'_>, - proc_macro2::TokenStream, - ::syn::Token![,], - >::parse_terminated - .parse2(fn_block) - .unwrap(); - */ - /* - let wtf: ::syn::parse::ParseBuffer<'_> = - parse_macro_input!(fn_block_pb as ::syn::parse::ParseBuffer).into(); - */ - /* - match ::syn::Block::parse_within(&fn_block) { - //match ::syn::Block::parse_within(&wtf) { - Ok(_) => { - // - } - Err(_) => panic!("can't parse code block"), - } - */ - /* - use ::quote::ToTokens; - ast_new.into_token_stream().into() - */ } /// Use as #[::bok::package(MyBasePackage)] /// Will setup a `base` field that is the given base package +/// and add `#[derive(::bok::Package)]` /// -/// e.g.: `#[::bok::package(::bok::PkgEmpty)]` +/// e.g.: +/// ``` +/// #[::bok::package(::bok::PkgEmpty)] +/// pub struct MyPkg {} +/// ``` #[proc_macro_attribute] pub fn package(attrs: TokenStream, input: TokenStream) -> TokenStream { let mut ast = parse_macro_input!(input as DeriveInput); @@ -208,18 +192,65 @@ pub fn package(attrs: TokenStream, input: TokenStream) -> TokenStream { } } -/// Unless you know what you are doing, use `#[::bok::package(MyBasePackage)]` instead +/// Use as #[::bok::impl_package] +/// Will include basic macros on functions and .base() method /// +/// e.g.: +/// ``` +/// #[::bok::impl_package] +/// impl ::bok::Pkg for MyPackage { +/// fn build(&self) -> Result<(),()> { +/// ... +/// } +/// } +/// ``` +#[proc_macro_attribute] +pub fn impl_package(_attrs: TokenStream, input: TokenStream) -> TokenStream { + let mut ast = parse_macro_input!(input as ::syn::ItemImpl); + + let base: ::syn::ImplItem = ::syn::parse_quote! { + fn base(&self) -> ::core::option::Option<&impl ::bok::Pkg> { + Some(&self.base) + } + }; + + for mut impl_item in ast.items.iter_mut() { + if let ::syn::ImplItem::Fn(ref mut fn_impl) = &mut impl_item { + let fn_with_code: ::syn::ImplItemFn = ::syn::parse_quote! { + #[::bok::pkg_fn_to_code] + #fn_impl + }; + *fn_impl = fn_with_code; + } else { + panic!("wtf"); + } + } + + ast.items.push(base); + + quote! { + #ast + } + .into() +} + +/// Unless you know what you are doing, use `#[::bok::package(MyBasePackage)]` instead +/// needs a `.base` field +/// +/// ``` /// #[derive(::bok::Package)] +/// pub struct MyPkg { +/// base: OtherPkg +/// } +/// ``` /// adds: /// * Builder pattern to a package -/// * deref for builder towards `.base` /// * getters/setters for all package fields /// * deref for package #[proc_macro_derive(Package)] pub fn derive_package(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); - let input2 = input.clone(); + let mut input_nobase = input.clone(); let name = input.ident.clone(); let name_builder = quote::format_ident!("{name}Builder"); @@ -359,16 +390,30 @@ pub fn derive_package(input: TokenStream) -> TokenStream { let opt_types2 = opt_types.clone(); let non_opt_types2 = non_opt_types.clone(); - let impl_base = quote! { - impl ::bok::Pkg for #name { - fn base(&self) -> Option<&impl ::bok::Pkg> { - Some(&self.base) + { + // remove the `base` field from the struct + let ::syn::Data::Struct(ref mut ds) = input_nobase.data else { + panic!("::bok::package expected a struct"); + }; + let ::syn::Fields::Named(ref mut old_nf) = ds.fields else { + panic!("::bok::package expected a named field"); + }; + let mut new_nf = ::syn::punctuated::Punctuated::< + ::syn::Field, + ::syn::token::Comma, + >::new(); + for f in old_nf.named.iter() { + if let Some(ref id) = f.ident { + if id.to_string() == "base" { + continue; + } } + new_nf.push(f.clone()); } - }; - + old_nf.named = new_nf; + } let pkg_struct = quote! { - #input2 + #input_nobase }; let mut pkg_struct_str = String::new(); { @@ -378,8 +423,6 @@ pub fn derive_package(input: TokenStream) -> TokenStream { } let expanded = quote! { - #impl_base - impl ::core::fmt::Display for #name { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) ->::core::fmt::Result @@ -401,9 +444,13 @@ pub fn derive_package(input: TokenStream) -> TokenStream { use ::core::fmt::Write; write!(out, - "#[::bok::to_code]\n\ - fn {}(&self) -> Result<(), ()> {{\n{}\n}}\n", - name, fn_str) + "\ + fn {}(&self) -> Result<(), ()> {{\n\ + {}\n\ + }}\n", + name, + fn_str, + ) } else { ::core::fmt::Result::Ok(()) @@ -414,9 +461,14 @@ pub fn derive_package(input: TokenStream) -> TokenStream { let pkg_empty = ::bok::PkgEmpty{}; let mut pkg_string = String::new(); - write!(&mut pkg_string, "{}", #pkg_struct_str)?; write!(&mut pkg_string, - "impl ::bok::Pkg for {} {{\n", + "#[::bok::package({})]\n\ + {}", + ::std::stringify!(#base_type), + #pkg_struct_str)?; + write!(&mut pkg_string, + "#[::bok::impl_package]\n\ + impl ::bok::Pkg for {} {{\n", ::std::stringify!(#name) )?; maybe_add_fn(&mut pkg_string, "prepare", &self.prepare_code().to_string())?; diff --git a/bok-utils/src/pkgs/one.rs b/bok-utils/src/pkgs/one.rs index cff9ffd..8e615f9 100644 --- a/bok-utils/src/pkgs/one.rs +++ b/bok-utils/src/pkgs/one.rs @@ -31,9 +31,9 @@ impl ::std::default::Default for One { } } -impl One { - #[::bok_macro::to_code] - fn test() -> u32 { - 42 +#[::bok::impl_package] +impl ::bok::Pkg for One { + fn build(&self) -> ::core::result::Result<(), ()> { + ret = ::core::result::Result::Ok(()); } } diff --git a/bok-utils/src/pkgs/three.rs b/bok-utils/src/pkgs/three.rs index c30bad6..c46b688 100644 --- a/bok-utils/src/pkgs/three.rs +++ b/bok-utils/src/pkgs/three.rs @@ -30,3 +30,9 @@ impl ::std::default::Default for Three { } } } +#[::bok::impl_package] +impl ::bok::Pkg for Three { + fn build(&self) -> ::core::result::Result<(), ()> { + ret = ::core::result::Result::Ok(()); + } +} diff --git a/bok-utils/src/pkgs/two.rs b/bok-utils/src/pkgs/two.rs index 3126a32..f65e4c0 100644 --- a/bok-utils/src/pkgs/two.rs +++ b/bok-utils/src/pkgs/two.rs @@ -30,3 +30,5 @@ impl ::std::default::Default for Two { } } } +#[::bok::impl_package] +impl ::bok::Pkg for Two {} diff --git a/bok/src/lib.rs b/bok/src/lib.rs index b33b77b..ff9e5c7 100644 --- a/bok/src/lib.rs +++ b/bok/src/lib.rs @@ -15,7 +15,9 @@ * limitations under the License. */ -pub use ::bok_macro::{package, repository, to_code, Package, Repository}; +pub use ::bok_macro::{ + impl_package, package, pkg_fn_to_code, repository, Package, Repository, +}; //use ::std::any::Any; @@ -85,7 +87,7 @@ impl Repository for RepositoryEmpty {} /// Implement common package operations pub trait Pkg: ::std::default::Default + ::core::fmt::Display { - fn base(&self) -> Option<&impl Pkg>; + fn base(&self) -> ::core::option::Option<&impl Pkg>; fn prepare(&self) -> ::core::result::Result<(), ()> { self.base().unwrap().prepare() } @@ -149,43 +151,43 @@ impl Pkg for PkgEmpty { None::.as_ref() } fn prepare(&self) -> ::core::result::Result<(), ()> { - Ok(()) + ::core::result::Result::Ok(()) } fn prepare_code(&self) -> ::proc_macro2::TokenStream { ::quote::quote! { - Ok(()) + ret = ::core::result::Result::Ok(()); } } fn configure(&self) -> ::core::result::Result<(), ()> { - Ok(()) + ::core::result::Result::Ok(()) } fn configure_code(&self) -> ::proc_macro2::TokenStream { ::quote::quote! { - Ok(()) + ret = ::core::result::Result::Ok(()); } } fn build(&self) -> ::core::result::Result<(), ()> { - Ok(()) + ::core::result::Result::Ok(()) } fn build_code(&self) -> ::proc_macro2::TokenStream { ::quote::quote! { - Ok(()) + ret = ::core::result::Result::Ok(()); } } fn check(&self) -> ::core::result::Result<(), ()> { - Ok(()) + ::core::result::Result::Ok(()) } fn check_code(&self) -> ::proc_macro2::TokenStream { ::quote::quote! { - Ok(()) + ret = ::core::result::Result::Ok(()); } } fn install(&self) -> ::core::result::Result<(), ()> { - Ok(()) + ::core::result::Result::Ok(()) } fn install_code(&self) -> ::proc_macro2::TokenStream { ::quote::quote! { - Ok(()) + ret = ::core::result::Result::Ok(()); } } }