::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)]
|
||||
/// 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())?;
|
||||
|
@ -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(());
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
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(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user