::bok::impl_package to simplify trait implemet

Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
Luca Fulchir 2024-03-27 17:58:18 +01:00
parent cc77805029
commit ddca7cc452
Signed by: luca.fulchir
GPG Key ID: 8F6440603D13A78E
5 changed files with 148 additions and 86 deletions

View File

@ -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())?;

View File

@ -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(());
}
}

View File

@ -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(());
}
}

View File

@ -30,3 +30,5 @@ impl ::std::default::Default for Two {
}
}
}
#[::bok::impl_package]
impl ::bok::Pkg for Two {}

View File

@ -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::<Self>.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(());
}
}
}