Pkg Options and their tracking in dependencies

Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
Luca Fulchir 2024-12-11 18:40:16 +01:00
parent c80cabe74e
commit 590fdbf25a
Signed by: luca.fulchir
GPG Key ID: 8F6440603D13A78E
7 changed files with 152 additions and 38 deletions

View File

@ -190,19 +190,38 @@ pub fn package_path(attrs: TokenStream, input: TokenStream) -> TokenStream {
crate::pkgs::package_path(attrs, input, __source_path)
}
/// Specify one or more build-time dependencies
/// Specify one or more dependencies
///
/// e.g:
/// ```
/// #[::bok::package(::bok::PkgEmpty)]
/// #[::bok::deps_build(some::Package, other::Package)]
/// #[::bok::deps(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)
pub fn deps(attrs: TokenStream, input: TokenStream) -> TokenStream {
crate::pkgs::deps(attrs, input)
}
/*
/// attribute for package optional feature fields
/// marks a struct field as a package option
/// the PkgBuilder will create apposite get/set and traits for the options
///
/// e.g:
/// ```
/// #[::bok::package(::bok::PkgEmpty)]
/// pub struct MyPkg {
/// #[::bok::option]
/// with_ssl: bool,
/// }
/// ```
#[proc_macro_attribute]
pub fn option(attrs: TokenStream, input: TokenStream) -> TokenStream {
crate::pkgs::option(attrs, input)
}
*/
/// Use as #[::bok::package_impl]
/// adds the right generics to the package.
///
@ -303,7 +322,7 @@ pub fn package_impl_builder(
/// * Builder pattern to a package
/// * getters/setters for all package fields
/// * deref for package
#[proc_macro_derive(Package)]
#[proc_macro_derive(Package, attributes(option))]
pub fn derive_package(input: TokenStream) -> TokenStream {
crate::pkgs::derive_package(input)
}

View File

@ -209,16 +209,13 @@ pub(crate) fn package_path(
.into()
}
pub(crate) fn deps_build(
attrs: TokenStream,
input: TokenStream,
) -> TokenStream {
pub(crate) fn deps(attrs: TokenStream, input: TokenStream) -> TokenStream {
let mut packages = parse_macro_input!(attrs as crate::PathList);
let local = parse_macro_input!(input as ::syn::ItemStruct);
// make sure all the packages have generics, and if not add the repo as
// generic aka: rewrite
// "#[deps_build(my::pkg)]" -->> "#[deps_build(my::pkg<R>)])"
// "#[deps(my::pkg)]" -->> "#[deps(my::pkg<R>)])"
let mut rewrite = false;
let repo_argument = {
let generic_id: ::syn::Path = ::syn::parse_quote!(id<R>);
@ -234,7 +231,7 @@ pub(crate) fn deps_build(
if rewrite {
let p_list = packages.0.into_iter();
return quote! {
#[::bok::deps_build(#(#p_list,)*)]
#[::bok::deps(#(#p_list,)*)]
#local
}
.into();
@ -279,6 +276,21 @@ pub(crate) fn deps_build(
)
})
.collect::<Vec<::syn::Ident>>();
let deps_builders2 = deps_builders.clone();
let deps_options = packages
.0
.iter()
.map(|x| {
let mut trait_option_path = x.clone();
let last_segment = trait_option_path.segments.last_mut().unwrap();
last_segment.ident =
quote::format_ident!("BokOptions{}", last_segment.ident);
last_segment.arguments = ::syn::PathArguments::None;
trait_option_path
})
.collect::<Vec<::syn::Path>>();
let deps_options2 = deps_options.clone();
let deps_options3 = deps_options.clone();
quote! {
#(#local_attrs)
@ -291,10 +303,14 @@ pub(crate) fn deps_build(
pub trait #pkg_trait: ::bok::Repository {
#(fn #deps(&self) -> ::std::boxed::Box<dyn ::bok::Pkg>;)
*
#(fn #deps_builders(&self)
-> ::std::boxed::Box<dyn #deps_options2>;)
*
}
#[::macro_magic::export_tokens(#pkg_builder_trait)]
pub trait #pkg_builder_trait {
#(fn #deps_builders(&self) -> ::std::boxed::Box<dyn ::bok::PkgBuilder>;)
#(fn #deps_builders2(&self)
-> ::std::boxed::Box<dyn #deps_options3>;)
*
}
}
@ -584,6 +600,7 @@ pub(crate) fn package_impl_builder(
let pkg_name = pkg.ident;
let name_builder = ::quote::format_ident!("{}Builder", pkg_name);
let trait_deps = ::quote::format_ident!("BokDeps{}", pkg_name);
let trait_options = ::quote::format_ident!("BokOptions{}", pkg_name);
let non_opt_fields = elements.named.iter().filter_map(|field| {
if let Some(id) = field.ident.clone() {
if id.to_string() == "version"
@ -593,6 +610,14 @@ pub(crate) fn package_impl_builder(
return None;
}
}
if field
.attrs
.iter()
.find(|&a| a.path().is_ident("option"))
.is_none()
{
return None;
}
match &field.ty {
syn::Type::Path(pth) => {
let first_path = pth.path.segments.first().unwrap();
@ -615,6 +640,14 @@ pub(crate) fn package_impl_builder(
return None;
}
}
if field
.attrs
.iter()
.find(|&a| a.path().is_ident("option"))
.is_none()
{
return None;
}
match &field.ty {
syn::Type::Path(pth) => {
let first_path = pth.path.segments.first().unwrap();
@ -637,6 +670,14 @@ pub(crate) fn package_impl_builder(
return None;
}
}
if field
.attrs
.iter()
.find(|&a| a.path().is_ident("option"))
.is_none()
{
return None;
}
match &field.ty {
syn::Type::Path(pth) => {
let first_path = pth.path.segments.first().unwrap();
@ -659,6 +700,14 @@ pub(crate) fn package_impl_builder(
return None;
}
}
if field
.attrs
.iter()
.find(|&a| a.path().is_ident("option"))
.is_none()
{
return None;
}
if let ::syn::Type::Path(pth) = &field.ty {
let first_path = pth.path.segments.first().unwrap();
if first_path.ident == "Option" {
@ -680,15 +729,23 @@ pub(crate) fn package_impl_builder(
});
let non_opt_types2 = non_opt_types.clone();
let non_opt_types3 = non_opt_types.clone();
let non_opt_types4 = non_opt_types.clone();
let non_opt_fields2 = non_opt_fields.clone();
let non_opt_fields3 = non_opt_fields.clone();
let non_opt_fields4 = non_opt_fields.clone();
let non_opt_fields5 = non_opt_fields.clone();
let non_opt_fields6 = non_opt_fields.clone();
let non_opt_fields7 = non_opt_fields.clone();
let non_opt_fields8 = non_opt_fields.clone();
let opt_fields2 = opt_fields.clone();
let opt_fields3 = opt_fields.clone();
let opt_fields4 = opt_fields.clone();
let opt_fields5 = opt_fields.clone();
let opt_fields6 = opt_fields.clone();
let opt_types2 = opt_types.clone();
let opt_types3 = opt_types.clone();
let opt_types4 = opt_types.clone();
let full_export_path_name = proc_macro2::Ident::new(
&("_bok_pkgbuilder_".to_owned() + &pkg_name.to_string()),
proc_macro2::Span::call_site(),
@ -704,7 +761,7 @@ pub(crate) fn package_impl_builder(
#(#non_opt_fields: ::std::option::Option<#non_opt_types>,)*
#(#opt_fields: ::std::option::Option<#opt_types>,)*
}
//#[::macro_magic::export_tokens(#full_export_path_name)]
#[::macro_magic::export_tokens(#full_export_path_name)]
impl<R> ::bok::PkgBuilder for #name_builder<R>
where
R: ::bok::Repository + 'static + #trait_deps,
@ -743,15 +800,12 @@ pub(crate) fn package_impl_builder(
#(if self.#non_opt_fields2.is_none() {
return ::std::result::Result::Err("unset field".into());
})*
Ok(
Box::new(#pkg_name::<R> {
_bok_base: ::std::marker::PhantomData::default(),
_bok_repo: ::std::marker::PhantomData::default(),
version: #pkg_name::<R>::default().version,
#(#non_opt_fields3 : self.#non_opt_fields3.clone().unwrap(),)*
#(#opt_fields2 : self.#opt_fields2.clone(),)*
})
)
let mut pkg = ::std::boxed::Box::new(#pkg_name::<R>::default());
#(pkg.#non_opt_fields3 = self.#non_opt_fields3.clone().unwrap();)
*
#(pkg.#opt_fields2 = self.#opt_fields2.clone();)
*
Ok(pkg)
}
#(#local_funcs)
*
@ -779,26 +833,55 @@ pub(crate) fn package_impl_builder(
pub fn as_builder_mut(&mut self) -> &mut dyn ::bok::PkgBuilder {
self
}
#(pub fn #non_opt_fields4 (&mut self, val : #non_opt_types2) -> &mut Self {
#(pub fn #non_opt_fields4 (&mut self, val : #non_opt_types2)
-> ::std::result::Result<&mut Self, ()> {
if self.#non_opt_fields4 != None &&
self.#non_opt_fields4 != Some(val) {
panic!("Package \"{}\": mandatory attribute set multiple times: \"{}\"",
::std::stringify!(#pkg_name),
::std::stringify!(#non_opt_fields4));
}
// FIXME: 'val' validation
self.#non_opt_fields4 = Some(val);
self
Ok(self)
})
*
#(pub fn #opt_fields3 (&mut self, val : #opt_types2) -> &mut Self {
#(pub fn #opt_fields3 (&mut self, val : #opt_types2)
-> ::std::result::Result<&mut Self, ()> {
if self.#opt_fields3 != None &&
self.#opt_fields3 != Some(val) {
panic!("Package \"{}\": optional attribute set multiple times: \"{}\"",
::std::stringify!(#pkg_name),
::std::stringify!(#opt_fields3));
}
// FIXME: 'val' validation
self.#opt_fields3 = Some(val);
self
Ok(self)
})
*
}
pub trait #trait_options: ::bok::PkgBuilder {
#(fn #non_opt_fields7 (&mut self, val : #non_opt_types3)
-> ::std::result::Result<(),()>;)
*
#(fn #opt_fields5 (&mut self, val : #opt_types3)
-> ::std::result::Result<(),()>;)
*
}
impl<R> #trait_options for #name_builder<R>
where
R: ::bok::Repository +'static + #trait_deps,
{
#(fn #non_opt_fields8 (&mut self, val : #non_opt_types4)
-> ::std::result::Result<(),()> {
self.#non_opt_fields8(val)?;
Ok(())
})
*
#(fn #opt_fields6 (&mut self, val : #opt_types4)
-> ::std::result::Result<(),()>{
self.#opt_fields6(val)?
Ok(())
})
*
}
@ -1007,6 +1090,14 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream {
&& id.to_string() != "_bok_base"
&& id.to_string() != "_bok_repo"
{
if field
.attrs
.iter()
.find(|&a| a.path().is_ident("option"))
.is_none()
{
return None;
}
return Some(&field.ident);
}
}

View File

@ -652,23 +652,24 @@ pub(crate) fn repo_impl_pkg_deps(
let trait_deps = parse_macro_input!(attrs as ::syn::ItemTrait);
let local = parse_macro_input!(input as ItemImpl);
let impl_trait = local
let pkg_trait = local
.trait_
.expect("#[::bok_macro::repo_impl_pkg_deps()]: no trait found")
.1;
let local_attrs = local.attrs.iter();
let (_, generics, _) = local.generics.split_for_impl();
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 output = &func.sig.output;
let dep_impl: ::syn::TraitItemFn = ::syn::parse_quote! {
fn #name(&self) -> ::std::boxed::Box<dyn ::bok::Pkg> {
fn #name(&self) #output {
::std::boxed::Box::new(self.#name())
}
};
@ -682,7 +683,7 @@ pub(crate) fn repo_impl_pkg_deps(
quote! {
#(#local_attrs)
*
impl #impl_trait for #ident #generics {
impl #pkg_trait for #ident #generics {
#(#deps)
*
}

View File

@ -18,11 +18,14 @@
/// Example package
/// Automatically implements `.builder().my_attr(42).build()` pattern
#[::bok::package(::bok::PkgEmpty)]
#[::bok::deps_build()]
#[::bok::deps()]
pub struct One {
pub my_attr: u32,
#[option]
my_attr: u32,
}
// TODO: move this into the PkgBuilder
// and have the package reference the builder
#[::bok::package_impl]
impl ::std::default::Default for One {
fn default() -> Self {

View File

@ -17,7 +17,7 @@
/// Example package
#[::bok::package(::bok::PkgEmpty)]
#[::bok::deps_build(super::One)]
#[::bok::deps(crate::repos::pkgs::One)]
pub struct Three {
pub my_attr: u32,
}

View File

@ -16,10 +16,10 @@
*/
/// Example package
#[::bok::package(super::one::One)]
#[::bok::deps_build(super::one::One)]
#[::bok::package(crate::repos::pkgs::one::One)]
#[::bok::deps(crate::repos::pkgs::one::One)]
pub struct Two {
pub my_attr2: u32,
my_attr2: u32,
}
#[::bok::package_impl]

View File

@ -28,8 +28,8 @@ pub mod deps {
}
pub use ::bok_macro::{
deps_build, package, package_impl, pkg_fn_to_code, repo_impl,
repo_packages, repository, Package, Repository,
deps, package, package_impl, pkg_fn_to_code, repo_impl, repo_packages,
repository, Package, Repository,
};
pub use ::semver::{BuildMetadata, Prerelease, Version};