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) crate::pkgs::package_path(attrs, input, __source_path)
} }
/// Specify one or more build-time dependencies /// Specify one or more dependencies
/// ///
/// e.g: /// e.g:
/// ``` /// ```
/// #[::bok::package(::bok::PkgEmpty)] /// #[::bok::package(::bok::PkgEmpty)]
/// #[::bok::deps_build(some::Package, other::Package)] /// #[::bok::deps(some::Package, other::Package)]
/// pub struct MyPkg {} /// pub struct MyPkg {}
/// ``` /// ```
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn deps_build(attrs: TokenStream, input: TokenStream) -> TokenStream { pub fn deps(attrs: TokenStream, input: TokenStream) -> TokenStream {
crate::pkgs::deps_build(attrs, input) 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] /// Use as #[::bok::package_impl]
/// adds the right generics to the package. /// adds the right generics to the package.
/// ///
@ -303,7 +322,7 @@ pub fn package_impl_builder(
/// * Builder pattern to a package /// * Builder pattern to a package
/// * 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, attributes(option))]
pub fn derive_package(input: TokenStream) -> TokenStream { pub fn derive_package(input: TokenStream) -> TokenStream {
crate::pkgs::derive_package(input) crate::pkgs::derive_package(input)
} }

View File

@ -209,16 +209,13 @@ pub(crate) fn package_path(
.into() .into()
} }
pub(crate) fn deps_build( pub(crate) fn deps(attrs: TokenStream, input: TokenStream) -> TokenStream {
attrs: TokenStream,
input: TokenStream,
) -> TokenStream {
let mut packages = parse_macro_input!(attrs as crate::PathList); let mut packages = parse_macro_input!(attrs as crate::PathList);
let local = parse_macro_input!(input as ::syn::ItemStruct); let local = parse_macro_input!(input as ::syn::ItemStruct);
// make sure all the packages have generics, and if not add the repo as // make sure all the packages have generics, and if not add the repo as
// generic aka: rewrite // generic aka: rewrite
// "#[deps_build(my::pkg)]" -->> "#[deps_build(my::pkg<R>)])" // "#[deps(my::pkg)]" -->> "#[deps(my::pkg<R>)])"
let mut rewrite = false; let mut rewrite = false;
let repo_argument = { let repo_argument = {
let generic_id: ::syn::Path = ::syn::parse_quote!(id<R>); let generic_id: ::syn::Path = ::syn::parse_quote!(id<R>);
@ -234,7 +231,7 @@ pub(crate) fn deps_build(
if rewrite { if rewrite {
let p_list = packages.0.into_iter(); let p_list = packages.0.into_iter();
return quote! { return quote! {
#[::bok::deps_build(#(#p_list,)*)] #[::bok::deps(#(#p_list,)*)]
#local #local
} }
.into(); .into();
@ -279,6 +276,21 @@ pub(crate) fn deps_build(
) )
}) })
.collect::<Vec<::syn::Ident>>(); .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! { quote! {
#(#local_attrs) #(#local_attrs)
@ -291,10 +303,14 @@ pub(crate) fn deps_build(
pub trait #pkg_trait: ::bok::Repository { pub trait #pkg_trait: ::bok::Repository {
#(fn #deps(&self) -> ::std::boxed::Box<dyn ::bok::Pkg>;) #(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)] #[::macro_magic::export_tokens(#pkg_builder_trait)]
pub trait #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 pkg_name = pkg.ident;
let name_builder = ::quote::format_ident!("{}Builder", pkg_name); let name_builder = ::quote::format_ident!("{}Builder", pkg_name);
let trait_deps = ::quote::format_ident!("BokDeps{}", 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| { let non_opt_fields = elements.named.iter().filter_map(|field| {
if let Some(id) = field.ident.clone() { if let Some(id) = field.ident.clone() {
if id.to_string() == "version" if id.to_string() == "version"
@ -593,6 +610,14 @@ pub(crate) fn package_impl_builder(
return None; return None;
} }
} }
if field
.attrs
.iter()
.find(|&a| a.path().is_ident("option"))
.is_none()
{
return None;
}
match &field.ty { match &field.ty {
syn::Type::Path(pth) => { syn::Type::Path(pth) => {
let first_path = pth.path.segments.first().unwrap(); let first_path = pth.path.segments.first().unwrap();
@ -615,6 +640,14 @@ pub(crate) fn package_impl_builder(
return None; return None;
} }
} }
if field
.attrs
.iter()
.find(|&a| a.path().is_ident("option"))
.is_none()
{
return None;
}
match &field.ty { match &field.ty {
syn::Type::Path(pth) => { syn::Type::Path(pth) => {
let first_path = pth.path.segments.first().unwrap(); let first_path = pth.path.segments.first().unwrap();
@ -637,6 +670,14 @@ pub(crate) fn package_impl_builder(
return None; return None;
} }
} }
if field
.attrs
.iter()
.find(|&a| a.path().is_ident("option"))
.is_none()
{
return None;
}
match &field.ty { match &field.ty {
syn::Type::Path(pth) => { syn::Type::Path(pth) => {
let first_path = pth.path.segments.first().unwrap(); let first_path = pth.path.segments.first().unwrap();
@ -659,6 +700,14 @@ pub(crate) fn package_impl_builder(
return None; 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 { if let ::syn::Type::Path(pth) = &field.ty {
let first_path = pth.path.segments.first().unwrap(); let first_path = pth.path.segments.first().unwrap();
if first_path.ident == "Option" { 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_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_fields2 = non_opt_fields.clone();
let non_opt_fields3 = non_opt_fields.clone(); let non_opt_fields3 = non_opt_fields.clone();
let non_opt_fields4 = non_opt_fields.clone(); let non_opt_fields4 = non_opt_fields.clone();
let non_opt_fields5 = non_opt_fields.clone(); let non_opt_fields5 = non_opt_fields.clone();
let non_opt_fields6 = 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_fields2 = opt_fields.clone();
let opt_fields3 = opt_fields.clone(); let opt_fields3 = opt_fields.clone();
let opt_fields4 = 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_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( let full_export_path_name = proc_macro2::Ident::new(
&("_bok_pkgbuilder_".to_owned() + &pkg_name.to_string()), &("_bok_pkgbuilder_".to_owned() + &pkg_name.to_string()),
proc_macro2::Span::call_site(), proc_macro2::Span::call_site(),
@ -704,7 +761,7 @@ pub(crate) fn package_impl_builder(
#(#non_opt_fields: ::std::option::Option<#non_opt_types>,)* #(#non_opt_fields: ::std::option::Option<#non_opt_types>,)*
#(#opt_fields: ::std::option::Option<#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> impl<R> ::bok::PkgBuilder for #name_builder<R>
where where
R: ::bok::Repository + 'static + #trait_deps, R: ::bok::Repository + 'static + #trait_deps,
@ -743,15 +800,12 @@ pub(crate) fn package_impl_builder(
#(if self.#non_opt_fields2.is_none() { #(if self.#non_opt_fields2.is_none() {
return ::std::result::Result::Err("unset field".into()); return ::std::result::Result::Err("unset field".into());
})* })*
Ok( let mut pkg = ::std::boxed::Box::new(#pkg_name::<R>::default());
Box::new(#pkg_name::<R> { #(pkg.#non_opt_fields3 = self.#non_opt_fields3.clone().unwrap();)
_bok_base: ::std::marker::PhantomData::default(), *
_bok_repo: ::std::marker::PhantomData::default(), #(pkg.#opt_fields2 = self.#opt_fields2.clone();)
version: #pkg_name::<R>::default().version, *
#(#non_opt_fields3 : self.#non_opt_fields3.clone().unwrap(),)* Ok(pkg)
#(#opt_fields2 : self.#opt_fields2.clone(),)*
})
)
} }
#(#local_funcs) #(#local_funcs)
* *
@ -779,26 +833,55 @@ pub(crate) fn package_impl_builder(
pub fn as_builder_mut(&mut self) -> &mut dyn ::bok::PkgBuilder { pub fn as_builder_mut(&mut self) -> &mut dyn ::bok::PkgBuilder {
self 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 && if self.#non_opt_fields4 != None &&
self.#non_opt_fields4 != Some(val) { self.#non_opt_fields4 != Some(val) {
panic!("Package \"{}\": mandatory attribute set multiple times: \"{}\"", panic!("Package \"{}\": mandatory attribute set multiple times: \"{}\"",
::std::stringify!(#pkg_name), ::std::stringify!(#pkg_name),
::std::stringify!(#non_opt_fields4)); ::std::stringify!(#non_opt_fields4));
} }
// FIXME: 'val' validation
self.#non_opt_fields4 = Some(val); 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 && if self.#opt_fields3 != None &&
self.#opt_fields3 != Some(val) { self.#opt_fields3 != Some(val) {
panic!("Package \"{}\": optional attribute set multiple times: \"{}\"", panic!("Package \"{}\": optional attribute set multiple times: \"{}\"",
::std::stringify!(#pkg_name), ::std::stringify!(#pkg_name),
::std::stringify!(#opt_fields3)); ::std::stringify!(#opt_fields3));
} }
// FIXME: 'val' validation
self.#opt_fields3 = Some(val); 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_base"
&& id.to_string() != "_bok_repo" && id.to_string() != "_bok_repo"
{ {
if field
.attrs
.iter()
.find(|&a| a.path().is_ident("option"))
.is_none()
{
return None;
}
return Some(&field.ident); 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 trait_deps = parse_macro_input!(attrs as ::syn::ItemTrait);
let local = parse_macro_input!(input as ItemImpl); let local = parse_macro_input!(input as ItemImpl);
let impl_trait = local let pkg_trait = local
.trait_ .trait_
.expect("#[::bok_macro::repo_impl_pkg_deps()]: no trait found") .expect("#[::bok_macro::repo_impl_pkg_deps()]: no trait found")
.1; .1;
let local_attrs = local.attrs.iter(); let local_attrs = local.attrs.iter();
let (_, generics, _) = local.generics.split_for_impl(); let (_, generics, _) = local.generics.split_for_impl();
let ident = &local.self_ty; let ident = &local.self_ty;
//let deps = Vec::<::syn::TraitItemFn>::new();
let deps = trait_deps let deps = trait_deps
.items .items
.iter() .iter()
.filter_map(|x| { .filter_map(|x| {
if let ::syn::TraitItem::Fn(func) = &x { if let ::syn::TraitItem::Fn(func) = &x {
let name = &func.sig.ident; let name = &func.sig.ident;
let output = &func.sig.output;
let dep_impl: ::syn::TraitItemFn = ::syn::parse_quote! { 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()) ::std::boxed::Box::new(self.#name())
} }
}; };
@ -682,7 +683,7 @@ pub(crate) fn repo_impl_pkg_deps(
quote! { quote! {
#(#local_attrs) #(#local_attrs)
* *
impl #impl_trait for #ident #generics { impl #pkg_trait for #ident #generics {
#(#deps) #(#deps)
* *
} }

View File

@ -18,11 +18,14 @@
/// Example package /// Example package
/// Automatically implements `.builder().my_attr(42).build()` pattern /// Automatically implements `.builder().my_attr(42).build()` pattern
#[::bok::package(::bok::PkgEmpty)] #[::bok::package(::bok::PkgEmpty)]
#[::bok::deps_build()] #[::bok::deps()]
pub struct One { 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] #[::bok::package_impl]
impl ::std::default::Default for One { impl ::std::default::Default for One {
fn default() -> Self { fn default() -> Self {

View File

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

View File

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

View File

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