diff --git a/bok-macro/src/lib.rs b/bok-macro/src/lib.rs index 4363c25..95e24c9 100644 --- a/bok-macro/src/lib.rs +++ b/bok-macro/src/lib.rs @@ -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) } diff --git a/bok-macro/src/pkgs.rs b/bok-macro/src/pkgs.rs index 2a7805d..33755ce 100644 --- a/bok-macro/src/pkgs.rs +++ b/bok-macro/src/pkgs.rs @@ -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)])" + // "#[deps(my::pkg)]" -->> "#[deps(my::pkg)])" let mut rewrite = false; let repo_argument = { let generic_id: ::syn::Path = ::syn::parse_quote!(id); @@ -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::>(); + 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::>(); + 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;) * + #(fn #deps_builders(&self) + -> ::std::boxed::Box;) + * } #[::macro_magic::export_tokens(#pkg_builder_trait)] pub trait #pkg_builder_trait { - #(fn #deps_builders(&self) -> ::std::boxed::Box;) + #(fn #deps_builders2(&self) + -> ::std::boxed::Box;) * } } @@ -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 ::bok::PkgBuilder for #name_builder 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:: { - _bok_base: ::std::marker::PhantomData::default(), - _bok_repo: ::std::marker::PhantomData::default(), - version: #pkg_name::::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::::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 #trait_options for #name_builder + 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); } } diff --git a/bok-macro/src/repos.rs b/bok-macro/src/repos.rs index 87b328c..f158e4a 100644 --- a/bok-macro/src/repos.rs +++ b/bok-macro/src/repos.rs @@ -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 { + 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) * } diff --git a/bok-utils/src/repos/pkgs/one.rs b/bok-utils/src/repos/pkgs/one.rs index fac6816..7959659 100644 --- a/bok-utils/src/repos/pkgs/one.rs +++ b/bok-utils/src/repos/pkgs/one.rs @@ -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 { diff --git a/bok-utils/src/repos/pkgs/three.rs b/bok-utils/src/repos/pkgs/three.rs index a41afc1..57a0f1f 100644 --- a/bok-utils/src/repos/pkgs/three.rs +++ b/bok-utils/src/repos/pkgs/three.rs @@ -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, } diff --git a/bok-utils/src/repos/pkgs/two.rs b/bok-utils/src/repos/pkgs/two.rs index 75f8576..3df8ebd 100644 --- a/bok-utils/src/repos/pkgs/two.rs +++ b/bok-utils/src/repos/pkgs/two.rs @@ -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] diff --git a/bok/src/lib.rs b/bok/src/lib.rs index b28f553..f1e8b3c 100644 --- a/bok/src/lib.rs +++ b/bok/src/lib.rs @@ -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};