::bok::impl_package to simplify trait implemet
Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
parent
cc77805029
commit
ddca7cc452
@ -21,6 +21,7 @@ use ::syn::{parse::Parser, parse_macro_input, DeriveInput};
|
|||||||
|
|
||||||
/// Use as #[::bok::repository(MyBaseRepo)]
|
/// Use as #[::bok::repository(MyBaseRepo)]
|
||||||
/// Will setup a `base` field that is the given base repo
|
/// Will setup a `base` field that is the given base repo
|
||||||
|
/// and add `#[derive(::bok::Repository)]`
|
||||||
///
|
///
|
||||||
/// e.g.: `#[::bok::repository(::bok::RepositoryEmpty)]`
|
/// e.g.: `#[::bok::repository(::bok::RepositoryEmpty)]`
|
||||||
#[proc_macro_attribute]
|
#[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
|
/// 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
|
/// adds extension capabilities to a repo
|
||||||
#[proc_macro_derive(Repository)]
|
#[proc_macro_derive(Repository)]
|
||||||
pub fn derive_repository(input: TokenStream) -> TokenStream {
|
pub fn derive_repository(input: TokenStream) -> TokenStream {
|
||||||
@ -94,14 +102,18 @@ pub fn derive_repository(input: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
TokenStream::from(expanded)
|
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
|
/// will create a new function, same name with `_code` that
|
||||||
/// returns the ::proc_macro2::TokenStream of the function
|
/// 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.:
|
/// e.g.:
|
||||||
/// ```
|
/// ```
|
||||||
/// impl MyPackage {
|
/// impl MyPackage {
|
||||||
/// #[::bok::to_code]
|
/// #[::bok::pkg_fn_to_code]
|
||||||
/// fn build() {
|
/// fn build() {
|
||||||
/// ...
|
/// ...
|
||||||
/// }
|
/// }
|
||||||
@ -110,10 +122,23 @@ pub fn derive_repository(input: TokenStream) -> TokenStream {
|
|||||||
/// let tokens : ::proc_macro2::TokenStream = p.build_code()
|
/// let tokens : ::proc_macro2::TokenStream = p.build_code()
|
||||||
/// ```
|
/// ```
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn to_code(_attrs: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn pkg_fn_to_code(_attrs: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let ast_orig: ::syn::ItemFn = parse_macro_input!(input as ::syn::ItemFn);
|
let mut ast_orig: ::syn::ItemFn =
|
||||||
|
parse_macro_input!(input as ::syn::ItemFn);
|
||||||
|
|
||||||
let mut ast_new = ast_orig.clone();
|
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 =
|
let new_fn_name =
|
||||||
quote::format_ident!("{}", ast_orig.sig.ident.to_string() + "_code");
|
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;
|
use ::quote::ToTokens;
|
||||||
let fn_block = ast_new.block.to_token_stream();
|
let fn_block = ast_new.block.to_token_stream();
|
||||||
|
|
||||||
//let quote_macro = ::quote::format_ident!("quote");
|
|
||||||
|
|
||||||
let asdf = quote! {
|
let asdf = quote! {
|
||||||
#ast_orig
|
#ast_orig
|
||||||
fn #new_fn_name () -> ::proc_macro2::TokenStream {
|
fn #new_fn_name (&self) -> ::proc_macro2::TokenStream {
|
||||||
//::quote::#quote_macro! {
|
|
||||||
::quote::quote! #fn_block
|
::quote::quote! #fn_block
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
asdf.into()
|
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)]
|
/// Use as #[::bok::package(MyBasePackage)]
|
||||||
/// Will setup a `base` field that is the given base package
|
/// 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]
|
#[proc_macro_attribute]
|
||||||
pub fn package(attrs: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn package(attrs: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let mut ast = parse_macro_input!(input as DeriveInput);
|
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)]
|
/// #[derive(::bok::Package)]
|
||||||
|
/// pub struct MyPkg {
|
||||||
|
/// base: OtherPkg
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
/// adds:
|
/// adds:
|
||||||
/// * Builder pattern to a package
|
/// * Builder pattern to a package
|
||||||
/// * deref for builder towards `.base`
|
|
||||||
/// * getters/setters for all package fields
|
/// * getters/setters for all package fields
|
||||||
/// * deref for package
|
/// * deref for package
|
||||||
#[proc_macro_derive(Package)]
|
#[proc_macro_derive(Package)]
|
||||||
pub fn derive_package(input: TokenStream) -> TokenStream {
|
pub fn derive_package(input: TokenStream) -> TokenStream {
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
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 = input.ident.clone();
|
||||||
let name_builder = quote::format_ident!("{name}Builder");
|
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 opt_types2 = opt_types.clone();
|
||||||
let non_opt_types2 = non_opt_types.clone();
|
let non_opt_types2 = non_opt_types.clone();
|
||||||
|
|
||||||
let impl_base = quote! {
|
{
|
||||||
impl ::bok::Pkg for #name {
|
// remove the `base` field from the struct
|
||||||
fn base(&self) -> Option<&impl ::bok::Pkg> {
|
let ::syn::Data::Struct(ref mut ds) = input_nobase.data else {
|
||||||
Some(&self.base)
|
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! {
|
let pkg_struct = quote! {
|
||||||
#input2
|
#input_nobase
|
||||||
};
|
};
|
||||||
let mut pkg_struct_str = String::new();
|
let mut pkg_struct_str = String::new();
|
||||||
{
|
{
|
||||||
@ -378,8 +423,6 @@ pub fn derive_package(input: TokenStream) -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
#impl_base
|
|
||||||
|
|
||||||
impl ::core::fmt::Display for #name {
|
impl ::core::fmt::Display for #name {
|
||||||
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>)
|
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>)
|
||||||
->::core::fmt::Result
|
->::core::fmt::Result
|
||||||
@ -401,9 +444,13 @@ pub fn derive_package(input: TokenStream) -> TokenStream {
|
|||||||
use ::core::fmt::Write;
|
use ::core::fmt::Write;
|
||||||
|
|
||||||
write!(out,
|
write!(out,
|
||||||
"#[::bok::to_code]\n\
|
"\
|
||||||
fn {}(&self) -> Result<(), ()> {{\n{}\n}}\n",
|
fn {}(&self) -> Result<(), ()> {{\n\
|
||||||
name, fn_str)
|
{}\n\
|
||||||
|
}}\n",
|
||||||
|
name,
|
||||||
|
fn_str,
|
||||||
|
)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
::core::fmt::Result::Ok(())
|
::core::fmt::Result::Ok(())
|
||||||
@ -414,9 +461,14 @@ pub fn derive_package(input: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
let pkg_empty = ::bok::PkgEmpty{};
|
let pkg_empty = ::bok::PkgEmpty{};
|
||||||
let mut pkg_string = String::new();
|
let mut pkg_string = String::new();
|
||||||
write!(&mut pkg_string, "{}", #pkg_struct_str)?;
|
|
||||||
write!(&mut pkg_string,
|
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)
|
::std::stringify!(#name)
|
||||||
)?;
|
)?;
|
||||||
maybe_add_fn(&mut pkg_string, "prepare", &self.prepare_code().to_string())?;
|
maybe_add_fn(&mut pkg_string, "prepare", &self.prepare_code().to_string())?;
|
||||||
|
@ -31,9 +31,9 @@ impl ::std::default::Default for One {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl One {
|
#[::bok::impl_package]
|
||||||
#[::bok_macro::to_code]
|
impl ::bok::Pkg for One {
|
||||||
fn test() -> u32 {
|
fn build(&self) -> ::core::result::Result<(), ()> {
|
||||||
42
|
ret = ::core::result::Result::Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -30,3 +30,5 @@ impl ::std::default::Default for Two {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[::bok::impl_package]
|
||||||
|
impl ::bok::Pkg for Two {}
|
||||||
|
@ -15,7 +15,9 @@
|
|||||||
* limitations under the License.
|
* 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;
|
//use ::std::any::Any;
|
||||||
|
|
||||||
@ -85,7 +87,7 @@ impl Repository for RepositoryEmpty {}
|
|||||||
|
|
||||||
/// Implement common package operations
|
/// Implement common package operations
|
||||||
pub trait Pkg: ::std::default::Default + ::core::fmt::Display {
|
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<(), ()> {
|
fn prepare(&self) -> ::core::result::Result<(), ()> {
|
||||||
self.base().unwrap().prepare()
|
self.base().unwrap().prepare()
|
||||||
}
|
}
|
||||||
@ -149,43 +151,43 @@ impl Pkg for PkgEmpty {
|
|||||||
None::<Self>.as_ref()
|
None::<Self>.as_ref()
|
||||||
}
|
}
|
||||||
fn prepare(&self) -> ::core::result::Result<(), ()> {
|
fn prepare(&self) -> ::core::result::Result<(), ()> {
|
||||||
Ok(())
|
::core::result::Result::Ok(())
|
||||||
}
|
}
|
||||||
fn prepare_code(&self) -> ::proc_macro2::TokenStream {
|
fn prepare_code(&self) -> ::proc_macro2::TokenStream {
|
||||||
::quote::quote! {
|
::quote::quote! {
|
||||||
Ok(())
|
ret = ::core::result::Result::Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn configure(&self) -> ::core::result::Result<(), ()> {
|
fn configure(&self) -> ::core::result::Result<(), ()> {
|
||||||
Ok(())
|
::core::result::Result::Ok(())
|
||||||
}
|
}
|
||||||
fn configure_code(&self) -> ::proc_macro2::TokenStream {
|
fn configure_code(&self) -> ::proc_macro2::TokenStream {
|
||||||
::quote::quote! {
|
::quote::quote! {
|
||||||
Ok(())
|
ret = ::core::result::Result::Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn build(&self) -> ::core::result::Result<(), ()> {
|
fn build(&self) -> ::core::result::Result<(), ()> {
|
||||||
Ok(())
|
::core::result::Result::Ok(())
|
||||||
}
|
}
|
||||||
fn build_code(&self) -> ::proc_macro2::TokenStream {
|
fn build_code(&self) -> ::proc_macro2::TokenStream {
|
||||||
::quote::quote! {
|
::quote::quote! {
|
||||||
Ok(())
|
ret = ::core::result::Result::Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn check(&self) -> ::core::result::Result<(), ()> {
|
fn check(&self) -> ::core::result::Result<(), ()> {
|
||||||
Ok(())
|
::core::result::Result::Ok(())
|
||||||
}
|
}
|
||||||
fn check_code(&self) -> ::proc_macro2::TokenStream {
|
fn check_code(&self) -> ::proc_macro2::TokenStream {
|
||||||
::quote::quote! {
|
::quote::quote! {
|
||||||
Ok(())
|
ret = ::core::result::Result::Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn install(&self) -> ::core::result::Result<(), ()> {
|
fn install(&self) -> ::core::result::Result<(), ()> {
|
||||||
Ok(())
|
::core::result::Result::Ok(())
|
||||||
}
|
}
|
||||||
fn install_code(&self) -> ::proc_macro2::TokenStream {
|
fn install_code(&self) -> ::proc_macro2::TokenStream {
|
||||||
::quote::quote! {
|
::quote::quote! {
|
||||||
Ok(())
|
ret = ::core::result::Result::Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user