Package dependencies
Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
parent
631c3a19e5
commit
00e93ce612
@ -84,7 +84,7 @@ pub fn repo_impl(attrs: TokenStream, input: TokenStream) -> TokenStream {
|
||||
/// Create the methods that will return the builders and packages
|
||||
/// Usage:
|
||||
/// ```
|
||||
/// #[::bok_macro::repo_impl(MyRepo)]
|
||||
/// #[::bok_macro::repo_impl_methods(MyRepo)]
|
||||
/// impl MyRepo{}
|
||||
/// ```
|
||||
#[::macro_magic::import_tokens_attr]
|
||||
@ -96,6 +96,24 @@ pub fn repo_impl_methods(
|
||||
crate::repos::repo_impl_methods(attrs, input, __source_path)
|
||||
}
|
||||
|
||||
/// Internal. **Do not use unless you know what you are doing**
|
||||
/// given a Repository and a trait "BokDeps...",
|
||||
/// implement all the fn returning the dependencies needed by the package
|
||||
///
|
||||
/// Usage:
|
||||
/// ```
|
||||
/// #[::bok_macro::repo_impl_(MyRepo)]
|
||||
/// impl MyRepo{}
|
||||
/// ```
|
||||
#[::macro_magic::import_tokens_attr]
|
||||
#[proc_macro_attribute]
|
||||
pub fn repo_impl_pkg_deps(
|
||||
attrs: TokenStream,
|
||||
input: TokenStream,
|
||||
) -> TokenStream {
|
||||
crate::repos::repo_impl_pkg_deps(attrs, input)
|
||||
}
|
||||
|
||||
//
|
||||
// ####### Package stuff ##########
|
||||
//
|
||||
@ -138,6 +156,19 @@ pub fn package(attrs: TokenStream, input: TokenStream) -> TokenStream {
|
||||
crate::pkgs::package(attrs, input, __source_path)
|
||||
}
|
||||
|
||||
/// Specify one or more build-time dependencies
|
||||
///
|
||||
/// e.g:
|
||||
/// ```
|
||||
/// #[::bok::package(::bok::PkgEmpty)]
|
||||
/// #[::bok::deps_build(some::Package, other::Package)]
|
||||
/// pub struct MyPkg {}
|
||||
/// ```
|
||||
#[proc_macro_attribute]
|
||||
pub fn deps_build(attrs: TokenStream, input: TokenStream) -> TokenStream {
|
||||
crate::pkgs::deps_build(attrs, input)
|
||||
}
|
||||
|
||||
/// Use as #[::bok::package_impl]
|
||||
/// will add `#[::bok::package_impl_base(..)]` with proper arguments
|
||||
/// and export the resulting symbols
|
||||
@ -205,3 +236,34 @@ pub fn package_impl_base(
|
||||
pub fn derive_package(input: TokenStream) -> TokenStream {
|
||||
crate::pkgs::derive_package(input)
|
||||
}
|
||||
|
||||
// ==== Common package stuff ====
|
||||
|
||||
pub(crate) struct PathList(Vec<::syn::Path>);
|
||||
|
||||
impl ::syn::parse::Parse for PathList {
|
||||
fn parse(input: ::syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
use ::syn::punctuated::Punctuated;
|
||||
let raw =
|
||||
Punctuated::<::syn::Path, ::syn::Token![,]>::parse_terminated(
|
||||
input,
|
||||
)?;
|
||||
let mut result = Vec::with_capacity(raw.len());
|
||||
for r in raw.into_iter() {
|
||||
result.push(r)
|
||||
}
|
||||
Ok(PathList(result))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn path_to_magic_export(p: &::syn::Path) -> ::syn::Path {
|
||||
let mut tmp = p.clone();
|
||||
let new_id = {
|
||||
macro_magic::mm_core::export_tokens_macro_ident(
|
||||
&tmp.segments.last().unwrap().ident,
|
||||
)
|
||||
};
|
||||
tmp.segments.last_mut().unwrap().ident = new_id;
|
||||
|
||||
tmp
|
||||
}
|
||||
|
@ -157,6 +157,52 @@ pub(crate) fn package(
|
||||
.into()
|
||||
}
|
||||
|
||||
pub(crate) fn deps_build(
|
||||
attrs: TokenStream,
|
||||
input: TokenStream,
|
||||
) -> TokenStream {
|
||||
let packages = parse_macro_input!(attrs as crate::PathList);
|
||||
|
||||
let local = parse_macro_input!(input as ::syn::ItemStruct);
|
||||
let ::syn::Fields::Named(local_fields) = local.fields else {
|
||||
use ::syn::spanned::Spanned;
|
||||
return ::syn::Error::new(
|
||||
local.fields.span(),
|
||||
"unnamed fields are not supported",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
};
|
||||
let local_attrs = local.attrs.iter();
|
||||
let local_fields = local_fields.named.iter();
|
||||
let generics = local.generics;
|
||||
let ident = local.ident;
|
||||
let vis = local.vis;
|
||||
|
||||
let pkg_trait = quote::format_ident!("BokDeps{}", ident);
|
||||
|
||||
let deps = packages
|
||||
.0
|
||||
.iter()
|
||||
.map(|x| x.segments.last().unwrap().ident.clone())
|
||||
.collect::<Vec<::syn::Ident>>();
|
||||
|
||||
quote! {
|
||||
#(#local_attrs)
|
||||
*
|
||||
#vis struct #ident<#generics> {
|
||||
#(#local_fields),
|
||||
*
|
||||
}
|
||||
#[::macro_magic::export_tokens(#pkg_trait)]
|
||||
pub trait #pkg_trait {
|
||||
#(fn #deps(&self) -> ::std::boxed::Box<dyn ::bok::Pkg>;)
|
||||
*
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
// package_impl has 3 stages, and uses macro_magic import for the last two:
|
||||
// * called without parameters => add default ::bok::Pkg functions, then add the
|
||||
// same macro with the package type as argument
|
||||
|
@ -19,122 +19,6 @@ use ::proc_macro::TokenStream;
|
||||
use ::quote::quote;
|
||||
use ::syn::{parse_macro_input, DeriveInput, Fields, ItemImpl, ItemStruct};
|
||||
|
||||
pub(crate) fn repo_impl(
|
||||
_attrs: TokenStream,
|
||||
input: TokenStream,
|
||||
) -> TokenStream {
|
||||
let local = parse_macro_input!(input as ItemImpl);
|
||||
|
||||
let reponame = &local.self_ty;
|
||||
|
||||
quote! {
|
||||
#[::bok_macro::repo_impl_methods(#reponame)]
|
||||
#local
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub(crate) fn repo_impl_methods(
|
||||
attrs: TokenStream,
|
||||
input: TokenStream,
|
||||
__source_name: TokenStream,
|
||||
) -> TokenStream {
|
||||
let base = parse_macro_input!(attrs as ItemStruct);
|
||||
let local = parse_macro_input!(input as ItemImpl);
|
||||
let source_name = parse_macro_input!(__source_name as ::syn::Path);
|
||||
|
||||
if let ::syn::Type::Path(self_type_path) = local.self_ty.as_ref() {
|
||||
if self_type_path.path != source_name {
|
||||
return ::syn::Error::new(
|
||||
proc_macro2::Span::call_site(),
|
||||
"#[::bok_macro::repo_impl_methods(..)]: argument and impl \
|
||||
type differ",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
} else {
|
||||
return ::syn::Error::new(
|
||||
proc_macro2::Span::call_site(),
|
||||
"#[::bok_macro::repo_impl_methods(..)]: argument and impl type \
|
||||
differ",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let local_attrs = local.attrs.iter();
|
||||
let generics = local.generics;
|
||||
let ident = &local.self_ty;
|
||||
let items = local.items.iter();
|
||||
|
||||
let ::syn::Fields::Named(base_fields) = base.fields else {
|
||||
return ::syn::Error::new(
|
||||
proc_macro2::Span::call_site(),
|
||||
"#[::bok_macro::repo_impl_methods(..)]: type has unsupported \
|
||||
unnamed fields",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
};
|
||||
|
||||
let mut fn_to_add = Vec::new();
|
||||
for f in base_fields.named.iter() {
|
||||
if !f
|
||||
.ident
|
||||
.as_ref()
|
||||
.is_some_and(|id| id.to_string().starts_with("_p_"))
|
||||
{
|
||||
continue;
|
||||
};
|
||||
let ::syn::Type::Path(f_type) = &f.ty else {
|
||||
continue;
|
||||
};
|
||||
let t_phantom = &f_type.path;
|
||||
let t_id = {
|
||||
let args = &t_phantom
|
||||
.segments
|
||||
.last()
|
||||
.expect("t_phantom no last?")
|
||||
.arguments;
|
||||
let ::syn::PathArguments::AngleBracketed(bracketed) = &args else {
|
||||
panic!("phantom without anglebracket?");
|
||||
};
|
||||
let ::syn::GenericArgument::Type(::syn::Type::Path(t)) =
|
||||
bracketed.args.first().expect("phantom bracketed, no args")
|
||||
else {
|
||||
panic!("phantom bracketed, generic not a type path");
|
||||
};
|
||||
t.path.clone()
|
||||
};
|
||||
let pkg_name = {
|
||||
let segment = t_id.segments.iter().last().unwrap();
|
||||
|
||||
&segment.ident
|
||||
};
|
||||
let pkg_fn: ::syn::ImplItemFn = ::syn::parse_quote! {
|
||||
pub fn #pkg_name(&self) -> #t_id {
|
||||
#t_id::default()
|
||||
}
|
||||
};
|
||||
fn_to_add.push(pkg_fn);
|
||||
}
|
||||
|
||||
let new_fn = fn_to_add.iter();
|
||||
|
||||
quote! {
|
||||
#(#local_attrs)
|
||||
*
|
||||
impl #ident<#generics> {
|
||||
#(#items)
|
||||
*
|
||||
#(#new_fn)
|
||||
*
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub(crate) fn repository(
|
||||
attrs: TokenStream,
|
||||
input: TokenStream,
|
||||
@ -368,22 +252,6 @@ pub(crate) fn derive_repository(input: TokenStream) -> TokenStream {
|
||||
}
|
||||
}.into()
|
||||
}
|
||||
struct PathList(Vec<::syn::Path>);
|
||||
|
||||
impl ::syn::parse::Parse for PathList {
|
||||
fn parse(input: ::syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
use ::syn::punctuated::Punctuated;
|
||||
let raw =
|
||||
Punctuated::<::syn::Path, ::syn::Token![,]>::parse_terminated(
|
||||
input,
|
||||
)?;
|
||||
let mut result = Vec::with_capacity(raw.len());
|
||||
for r in raw.into_iter() {
|
||||
result.push(r)
|
||||
}
|
||||
Ok(PathList(result))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn repo_packages(
|
||||
attrs: TokenStream,
|
||||
@ -446,7 +314,7 @@ pub(crate) fn repo_packages(
|
||||
})
|
||||
.collect::<Vec<&::syn::Field>>();
|
||||
|
||||
let packages = parse_macro_input!(attrs as PathList);
|
||||
let packages = parse_macro_input!(attrs as crate::PathList);
|
||||
|
||||
// the packages added manually must not be repeated manually.
|
||||
// but they will override any other package added by
|
||||
@ -530,20 +398,213 @@ pub(crate) fn repo_packages(
|
||||
.into()
|
||||
}
|
||||
|
||||
fn path_to_snake_case(path: &::syn::Path) -> String {
|
||||
let mut s = String::new();
|
||||
pub(crate) fn repo_impl(
|
||||
_attrs: TokenStream,
|
||||
input: TokenStream,
|
||||
) -> TokenStream {
|
||||
let local = parse_macro_input!(input as ItemImpl);
|
||||
|
||||
let mut is_first = true;
|
||||
for segment in path.segments.iter() {
|
||||
if !is_first {
|
||||
s += "_";
|
||||
} else {
|
||||
is_first = false
|
||||
let reponame = &local.self_ty;
|
||||
|
||||
quote! {
|
||||
#[::bok_macro::repo_impl_methods(#reponame)]
|
||||
#local
|
||||
}
|
||||
s += segment.ident.to_string().as_str();
|
||||
}
|
||||
|
||||
use ::convert_case::{Case, Casing};
|
||||
|
||||
s.to_case(Case::Snake)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub(crate) fn repo_impl_methods(
|
||||
attrs: TokenStream,
|
||||
input: TokenStream,
|
||||
__source_name: TokenStream,
|
||||
) -> TokenStream {
|
||||
let base = parse_macro_input!(attrs as ItemStruct);
|
||||
let local = parse_macro_input!(input as ItemImpl);
|
||||
let source_name = parse_macro_input!(__source_name as ::syn::Path);
|
||||
|
||||
if let ::syn::Type::Path(self_type_path) = local.self_ty.as_ref() {
|
||||
if self_type_path.path != source_name {
|
||||
return ::syn::Error::new(
|
||||
proc_macro2::Span::call_site(),
|
||||
"#[::bok_macro::repo_impl_methods(..)]: argument and impl \
|
||||
type differ",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
} else {
|
||||
return ::syn::Error::new(
|
||||
proc_macro2::Span::call_site(),
|
||||
"#[::bok_macro::repo_impl_methods(..)]: argument and impl type \
|
||||
differ",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let local_attrs = local.attrs.iter();
|
||||
let generics = local.generics;
|
||||
let ::syn::Type::Path(local_tp) = local.self_ty.as_ref() else {
|
||||
return ::syn::Error::new(
|
||||
proc_macro2::Span::call_site(),
|
||||
"#[::bok_macro::repo_impl_methods(..)]: no ident?",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
};
|
||||
let local_ident = local_tp.path.get_ident().expect("NOT AN IDENT");
|
||||
let items = local.items.iter();
|
||||
// FIXME: make sure `items` does not have methods that are not packages in
|
||||
// the impl
|
||||
|
||||
let ::syn::Fields::Named(base_fields) = base.fields else {
|
||||
return ::syn::Error::new(
|
||||
proc_macro2::Span::call_site(),
|
||||
"#[::bok_macro::repo_impl_methods(..)]: type has unsupported \
|
||||
unnamed fields",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
};
|
||||
|
||||
let mut all_pkgs_types = Vec::with_capacity(base_fields.named.len());
|
||||
|
||||
let mut fn_to_add = Vec::new();
|
||||
for f in base_fields.named.iter() {
|
||||
if !f
|
||||
.ident
|
||||
.as_ref()
|
||||
.is_some_and(|id| id.to_string().starts_with("_p_"))
|
||||
{
|
||||
continue;
|
||||
};
|
||||
let ::syn::Type::Path(f_type) = &f.ty else {
|
||||
continue;
|
||||
};
|
||||
let t_phantom = &f_type.path;
|
||||
let t_id = {
|
||||
let args = &t_phantom
|
||||
.segments
|
||||
.last()
|
||||
.expect("t_phantom no last?")
|
||||
.arguments;
|
||||
let ::syn::PathArguments::AngleBracketed(bracketed) = &args else {
|
||||
panic!("phantom without anglebracket?");
|
||||
};
|
||||
let ::syn::GenericArgument::Type(::syn::Type::Path(t)) =
|
||||
bracketed.args.first().expect("phantom bracketed, no args")
|
||||
else {
|
||||
panic!("phantom bracketed, generic not a type path");
|
||||
};
|
||||
t.path.clone()
|
||||
};
|
||||
let pkg_name = {
|
||||
let segment = t_id.segments.iter().last().unwrap();
|
||||
|
||||
&segment.ident
|
||||
};
|
||||
all_pkgs_types.push(t_id.clone());
|
||||
if local
|
||||
.items
|
||||
.iter()
|
||||
.find(|&func_it| {
|
||||
let ::syn::ImplItem::Fn(func) = &func_it else {
|
||||
return false;
|
||||
};
|
||||
func.sig.ident.to_string() == pkg_name.to_string()
|
||||
})
|
||||
.is_some()
|
||||
{
|
||||
// the user overrode the `::default()` package build
|
||||
// don't try to add it again
|
||||
continue;
|
||||
}
|
||||
let pkg_fn: ::syn::ImplItemFn = ::syn::parse_quote! {
|
||||
pub fn #pkg_name(&self) -> #t_id {
|
||||
#t_id::default()
|
||||
}
|
||||
};
|
||||
fn_to_add.push(pkg_fn);
|
||||
}
|
||||
|
||||
let new_fn = fn_to_add.iter();
|
||||
|
||||
let mut all_impl_deps = Vec::with_capacity(all_pkgs_types.len());
|
||||
|
||||
for p_type in all_pkgs_types.into_iter() {
|
||||
let bok_dep_trait = {
|
||||
let mut tmp = p_type.clone();
|
||||
tmp.segments.last_mut().unwrap().ident = quote::format_ident!(
|
||||
"BokDeps{}",
|
||||
tmp.segments.last().unwrap().ident
|
||||
);
|
||||
tmp
|
||||
};
|
||||
let pkg_trait_impl = &bok_dep_trait;
|
||||
let dep_view = quote! {
|
||||
#[::bok_macro::repo_impl_pkg_deps(#pkg_trait_impl)]
|
||||
impl #bok_dep_trait for #local_ident {}
|
||||
};
|
||||
all_impl_deps.push(dep_view);
|
||||
break;
|
||||
}
|
||||
|
||||
quote! {
|
||||
#(#local_attrs)
|
||||
*
|
||||
impl #local_ident<#generics> {
|
||||
#(#items)
|
||||
*
|
||||
#(#new_fn)
|
||||
*
|
||||
}
|
||||
#(#all_impl_deps)
|
||||
*
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub(crate) fn repo_impl_pkg_deps(
|
||||
attrs: TokenStream,
|
||||
input: TokenStream,
|
||||
) -> TokenStream {
|
||||
let trait_deps = parse_macro_input!(attrs as ::syn::ItemTrait);
|
||||
let local = parse_macro_input!(input as ItemImpl);
|
||||
|
||||
let impl_trait = local
|
||||
.trait_
|
||||
.expect("#[::bok_macro::repo_impl_pkg_deps()]: no trait found")
|
||||
.1;
|
||||
let local_attrs = local.attrs.iter();
|
||||
let generics = local.generics;
|
||||
let ident = &local.self_ty;
|
||||
|
||||
//let deps = Vec::<::syn::TraitItemFn>::new();
|
||||
let deps = trait_deps
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|x| {
|
||||
if let ::syn::TraitItem::Fn(func) = &x {
|
||||
let name = &func.sig.ident;
|
||||
let dep_impl: ::syn::TraitItemFn = ::syn::parse_quote! {
|
||||
fn #name(&self) -> ::std::boxed::Box<dyn ::bok::Pkg> {
|
||||
::std::boxed::Box::new(self.#name())
|
||||
}
|
||||
};
|
||||
Some(dep_impl)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<::syn::TraitItemFn>>();
|
||||
|
||||
quote! {
|
||||
#(#local_attrs)
|
||||
*
|
||||
impl #impl_trait for #ident<#generics> {
|
||||
#(#deps)
|
||||
*
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ pub mod pkgs;
|
||||
|
||||
// FIXME: why?
|
||||
use ::bok_macro::repo_impl_methods;
|
||||
|
||||
use ::bok_macro::repo_impl_pkg_deps;
|
||||
// Export multiple packages in this module
|
||||
use ::bok::repository;
|
||||
|
||||
|
@ -22,6 +22,7 @@ use ::bok::package;
|
||||
/// Example package
|
||||
/// Automatically implements `.builder().my_attr(42).build()` pattern
|
||||
#[::bok::package(::bok::PkgEmpty)]
|
||||
#[::bok::deps_build()]
|
||||
pub struct One {
|
||||
pub my_attr: u32,
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ use ::bok::package;
|
||||
|
||||
/// Example package
|
||||
#[::bok::package(::bok::PkgEmpty)]
|
||||
#[::bok::deps_build(super::One)]
|
||||
pub struct Three {
|
||||
pub my_attr: u32,
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ use ::bok::package;
|
||||
|
||||
/// Example package
|
||||
#[::bok::package(super::one::One)]
|
||||
#[::bok::deps_build(super::one::One)]
|
||||
pub struct Two {
|
||||
pub my_attr2: u32,
|
||||
}
|
||||
|
@ -27,8 +27,8 @@ pub mod deps {
|
||||
}
|
||||
|
||||
pub use ::bok_macro::{
|
||||
package, package_impl, pkg_fn_to_code, repo_impl, repo_packages,
|
||||
repository, Package, Repository,
|
||||
deps_build, package, package_impl, pkg_fn_to_code, repo_impl,
|
||||
repo_packages, repository, Package, Repository,
|
||||
};
|
||||
pub use ::semver::{BuildMetadata, Prerelease, Version};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user