diff --git a/bok-macro/src/collection.rs b/bok-macro/src/collection.rs new file mode 100644 index 0000000..6e70486 --- /dev/null +++ b/bok-macro/src/collection.rs @@ -0,0 +1,112 @@ +/* + * Copyright 2024 Luca Fulchir + * + * Licensed under the Apache License, Version 2.0 with LLVM exception (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License and of the exception at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * https://spdx.org/licenses/LLVM-exception.html + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use ::proc_macro::TokenStream; +use ::quote::quote; +use ::syn::parse_macro_input; + +pub(crate) fn collection( + attrs: TokenStream, + input: TokenStream, +) -> TokenStream { + let ast = parse_macro_input!(input as ::syn::ItemStruct); + if ast.ident.to_string() != "Collection" { + use ::syn::spanned::Spanned; + return ::syn::Error::new( + ast.span(), + "#[::bok_macro::collection(..)] should only be called on `struct \ + Collection{}`", + ) + .to_compile_error() + .into(); + } + use crate::PathList; + + let deps_list = parse_macro_input!(attrs as PathList); + let mut all_deps_builder = deps_list.0.iter(); + let name = all_deps_builder.next().expect( + "`#[::bok_macro::collection(..)]` must have at least two arguments, \ + the first is the repo path", + ); + + let mut asdf = Vec::new(); + for d in deps_list.0.iter() { + let d_str = d + .segments + .iter() + .fold(String::new(), |a, b| a + "_" + &b.ident.to_string()); + asdf.push(::quote::format_ident!("{}", d_str)); + } + + quote! { + use ::bok_macro::collection_dep; + #( + const #asdf : u32 = 42; + ) + * + #(#[::bok_macro::collection_dep(#all_deps_builder)]) + * + pub struct Collection { + repo: ::std::sync::Arc<#name>, + pkgs: ::std::sync::RwLock< + ::std::vec::Vec< + ::std::sync::Arc< + dyn ::bok::PkgBuilder>>>, + } + impl ::bok::Collection for Collection {} + } + .into() +} + +pub(crate) fn collection_dep( + attrs: TokenStream, + input: TokenStream, + __source_path: TokenStream, +) -> TokenStream { + let local = parse_macro_input!(input as ::syn::ItemStruct); + let dep = parse_macro_input!(attrs as ::syn::ItemTrait); + + let local_ident = &local.ident; + let dep_ident = parse_macro_input!(__source_path as ::syn::Path); + + let mut all_deps = Vec::with_capacity(dep.items.len()); + for fn_it in dep.items.iter() { + let ::syn::TraitItem::Fn(func) = fn_it else { + continue; + }; + let sig = &func.sig; + let pkgbuilder_name = &sig.ident; + + let builder: ::syn::ImplItemFn = ::syn::parse_quote! { + #sig { + ::std::boxed::Box::new( + self.repo.#pkgbuilder_name() + ) + } + }; + all_deps.push(builder); + } + + quote! { + #local + impl #dep_ident for #local_ident { + #(#all_deps) + * + } + } + .into() +} diff --git a/bok-macro/src/lib.rs b/bok-macro/src/lib.rs index 15272e3..4363c25 100644 --- a/bok-macro/src/lib.rs +++ b/bok-macro/src/lib.rs @@ -18,6 +18,7 @@ #![feature(proc_macro_span)] use ::proc_macro::TokenStream; +mod collection; mod pkgs; mod repos; @@ -80,7 +81,7 @@ pub fn repo_impl(attrs: TokenStream, input: TokenStream) -> TokenStream { crate::repos::repo_impl(attrs, input) } -/// Internal. **Do not use unless you know what you are doing** +/// **Internal. Do not use unless you know what you are doing** /// Create the methods that will return the builders and packages /// Usage: /// ``` @@ -96,7 +97,7 @@ 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** +/// **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 /// @@ -114,6 +115,23 @@ pub fn repo_impl_pkg_deps( crate::repos::repo_impl_pkg_deps(attrs, input) } +/// **Internal** +/// +/// add implementation of a collection +#[proc_macro_attribute] +pub fn collection(attrs: TokenStream, input: TokenStream) -> TokenStream { + crate::collection::collection(attrs, input) +} + +/// **Internal** +/// +/// add implementation of a `BokBuilderDeps` trait +#[::macro_magic::import_tokens_attr] +#[proc_macro_attribute] +pub fn collection_dep(attrs: TokenStream, input: TokenStream) -> TokenStream { + crate::collection::collection_dep(attrs, input, __source_path) +} + // // ####### Package stuff ########## // @@ -208,7 +226,7 @@ pub fn package_impl(attrs: TokenStream, input: TokenStream) -> TokenStream { crate::pkgs::package_impl(attrs, input) } -/// Internal, **don't use unless you know what you are doing.** +/// ** Internal, don't use unless you know what you are doing.** /// Use as #[::bok::package_impl_base(::my::Package)] /// implements common ::bok::Pkg methods and takes the package methods /// not implemented by the user from the base package @@ -224,7 +242,7 @@ pub fn package_impl(attrs: TokenStream, input: TokenStream) -> TokenStream { /// e.g.: /// ``` /// #[::bok::package_impl_base(MyPackage)] -/// #[::macro_magic::export_tokens(bok_pkg_mypackage)] +/// #[::macro_magic::export_tokens(_bok_pkg_mypackage)] /// impl ::bok::Pkg for MyPackage { /// fn build(&self) -> Result<(),()> { /// ... @@ -240,6 +258,38 @@ pub fn package_impl_base( crate::pkgs::package_impl_base(attrs, input) } +/// ** Internal, don't use unless you know what you are doing.** +/// Use as #[::bok::package_impl_builder(::my::Package)] +/// implements common ::bok::PkgBuilder methods and takes +/// the package methods not implemented by the user from the base package +/// +/// does not export symbols. +/// +/// should be called with the same path as the package that implements +/// ::bok::Pkg +/// +/// will rewrite itself with the exported impl path of the base package +/// so that macro_magic can import the base impl +/// +/// e.g.: +/// ``` +/// #[::bok::package_impl_builder(MyPackage)] +/// #[::macro_magic::export_tokens(_bok_pkgbuilder_mypackage)] +/// impl ::bok::PkgBuilder for MyPackage { +/// fn set_dependencies(&self, ...) -> Result<(),()> { +/// ... +/// } +/// } +/// ``` +#[::macro_magic::import_tokens_attr] +#[proc_macro_attribute] +pub fn package_impl_builder( + attrs: TokenStream, + input: TokenStream, +) -> TokenStream { + crate::pkgs::package_impl_builder(attrs, input, __source_path) +} + /// Unless you know what you are doing, use `#[::bok::package(MyBasePackage)]` /// instead needs a `.base` field /// diff --git a/bok-macro/src/pkgs.rs b/bok-macro/src/pkgs.rs index 0dccd7f..2a7805d 100644 --- a/bok-macro/src/pkgs.rs +++ b/bok-macro/src/pkgs.rs @@ -268,6 +268,17 @@ pub(crate) fn deps_build( quote::format_ident!("{}", ident.to_string().to_case(Case::Snake)) }) .collect::>(); + let deps_builders = packages + .0 + .iter() + .map(|x| { + let ident = x.segments.last().unwrap().ident.clone(); + quote::format_ident!( + "{}_builder", + ident.to_string().to_case(Case::Snake) + ) + }) + .collect::>(); quote! { #(#local_attrs) @@ -283,7 +294,7 @@ pub(crate) fn deps_build( } #[::macro_magic::export_tokens(#pkg_builder_trait)] pub trait #pkg_builder_trait { - #(fn #deps(&self) -> ::std::boxed::Box;) + #(fn #deps_builders(&self) -> ::std::boxed::Box;) * } } @@ -303,41 +314,47 @@ pub(crate) fn package_impl( ) -> TokenStream { let mut ast = parse_macro_input!(input as ::syn::ItemImpl); - if let Some((_, trait_name, _)) = &ast.trait_ { - let s = &trait_name.segments; - if s.len() != 2 - || s[0].ident.to_string() != "bok" - || s[1].ident.to_string() != "Pkg" - { - // - // only add the generic parameter and nothing else - // - let ::syn::Type::Path(t_id) = &ast.self_ty.as_ref() else { - return ::syn::Error::new( - ast.span(), - "package_impl: Type is not Path", - ) - .to_compile_error() - .into(); - }; - let trait_deps = ::quote::format_ident!( - "BokDeps{}", - t_id.path.segments.last().unwrap().ident - ); - let g: ::syn::ItemImpl = ::syn::parse_quote! { - impl trait_name for #t_id where R: ::bok::Repository + #trait_deps {} - }; - ast.generics = g.generics; - ast.self_ty = g.self_ty; - // TODO: if the trait is `::std::default::Default` - // then add the `_bok_base` and `_bok_repo` defaults automatically - return quote! { - #ast - } + let Some((_, trait_name, _)) = &ast.trait_ else { + return ::syn::Error::new( + ast.span(), + "package_impl: trait name not foun", + ) + .to_compile_error() + .into(); + }; + let s = &trait_name.segments; + if s.len() != 2 + || s[0].ident.to_string() != "bok" + || (s[1].ident.to_string() != "Pkg" + && s[1].ident.to_string() != "PkgBuilder") + { + // + // only add the generic parameter and nothing else + // + let ::syn::Type::Path(t_id) = &ast.self_ty.as_ref() else { + return ::syn::Error::new( + ast.span(), + "package_impl: Type is not Path", + ) + .to_compile_error() .into(); + }; + let trait_deps = ::quote::format_ident!( + "BokDeps{}", + t_id.path.segments.last().unwrap().ident + ); + let g: ::syn::ItemImpl = ::syn::parse_quote! { + impl trait_name for #t_id where R: ::bok::Repository + #trait_deps {} + }; + ast.generics = g.generics; + ast.self_ty = g.self_ty; + // TODO: if the trait is `::std::default::Default` + // then add the `_bok_base` and `_bok_repo` defaults automatically + return quote! { + #ast } + .into(); } - let ast = ast; // remove mut let name_pkg = match &*ast.self_ty { ::syn::Type::Path(tp) => match tp.path.get_ident() { @@ -360,6 +377,16 @@ pub(crate) fn package_impl( .into() } }; + + if trait_name.segments[1].ident.to_string() == "PkgBuilder" { + return quote! { + use ::bok_macro::package_impl_builder; + #[::bok_macro::package_impl_builder(#name_pkg)] + #ast + } + .into(); + } + let full_path_name = proc_macro2::Ident::new( &("_bok_pkg_".to_owned() + &name_pkg.to_string()), proc_macro2::Span::call_site(), @@ -518,6 +545,267 @@ pub(crate) fn package_impl_base( .into(); } +// implement PkgBuilder +pub(crate) fn package_impl_builder( + attrs: TokenStream, + input: TokenStream, + __source_path: TokenStream, +) -> TokenStream { + let local = parse_macro_input!(input as ::syn::ItemImpl); + let pkg = parse_macro_input!(attrs as ::syn::ItemStruct); + + let local_path = &local.trait_.as_ref().unwrap().1; + if local_path.segments.len() != 2 + || local_path.segments[0].ident.to_string() != "bok" + || local_path.segments[1].ident.to_string() != "PkgBuilder" + { + return ::syn::Error::new( + local.span(), + "package_impl_builder: can only be called on `impl \ + ::bok::PkgBuilder`", + ) + .to_compile_error() + .into(); + } + let local_funcs = local.items.iter().filter(|&it| match it { + ::syn::ImplItem::Fn(_) => true, + _ => false, + }); + + let ::syn::Fields::Named(elements) = pkg.fields else { + return ::syn::Error::new( + local.span(), + "package_impl_builder: only named fields supported in base pkg", + ) + .to_compile_error() + .into(); + }; + + let pkg_name = pkg.ident; + let name_builder = ::quote::format_ident!("{}Builder", pkg_name); + let trait_deps = ::quote::format_ident!("BokDeps{}", pkg_name); + let non_opt_fields = elements.named.iter().filter_map(|field| { + if let Some(id) = field.ident.clone() { + if id.to_string() == "version" + || id.to_string() == "_bok_base" + || id.to_string() == "_bok_repo" + { + return None; + } + } + match &field.ty { + syn::Type::Path(pth) => { + let first_path = pth.path.segments.first().unwrap(); + if first_path.ident == "Option" { + None + } else { + let id = &field.ident; + Some(quote! {#id}) + } + } + t => Some(quote! {#t}), + } + }); + let opt_fields = elements.named.iter().filter_map(|field| { + if let Some(id) = field.ident.clone() { + if id.to_string() == "version" + || id.to_string() == "_bok_base" + || id.to_string() == "_bok_repo" + { + return None; + } + } + match &field.ty { + syn::Type::Path(pth) => { + let first_path = pth.path.segments.first().unwrap(); + if first_path.ident == "Option" { + let id = &field.ident; + Some(quote! {#id}) + } else { + None + } + } + _ => None, + } + }); + let non_opt_types = elements.named.iter().filter_map(|field| { + if let Some(id) = field.ident.clone() { + if id.to_string() == "version" + || id.to_string() == "_bok_base" + || id.to_string() == "_bok_repo" + { + return None; + } + } + match &field.ty { + syn::Type::Path(pth) => { + let first_path = pth.path.segments.first().unwrap(); + if first_path.ident == "Option" { + None + } else { + let t = &field.ty; + Some(quote! {#t}) + } + } + _ => None, + } + }); + let opt_types = elements.named.iter().filter_map(|field| { + if let Some(id) = field.ident.clone() { + if id.to_string() == "version" + || id.to_string() == "_bok_base" + || id.to_string() == "_bok_repo" + { + return None; + } + } + if let ::syn::Type::Path(pth) = &field.ty { + let first_path = pth.path.segments.first().unwrap(); + if first_path.ident == "Option" { + if let syn::PathArguments::AngleBracketed( + syn::AngleBracketedGenericArguments { args, .. }, + ) = &first_path.arguments + { + if let Some(syn::GenericArgument::Type(syn::Type::Path( + p, + ))) = args.first() + { + let id = &p.path.segments.first().unwrap().ident; + return Some(quote! {#id}); + } + } + } + } + None + }); + + let non_opt_types2 = 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 opt_fields2 = opt_fields.clone(); + let opt_fields3 = opt_fields.clone(); + let opt_fields4 = opt_fields.clone(); + let opt_types2 = 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(), + ); + + quote! { + #[derive(::std::default::Default, ::std::fmt::Debug)] + pub struct #name_builder + where + R: ::bok::Repository + 'static + #trait_deps, + { + _bok_repo: ::std::marker::PhantomData, + #(#non_opt_fields: ::std::option::Option<#non_opt_types>,)* + #(#opt_fields: ::std::option::Option<#opt_types>,)* + } + //#[::macro_magic::export_tokens(#full_export_path_name)] + impl ::bok::PkgBuilder for #name_builder + where + R: ::bok::Repository + 'static + #trait_deps, + { + fn name(&self) -> ::bok::PkgName { + use ::bok::Pkg; + #pkg_name::::default().name() + } + fn path(&self) -> ::bok::Path<::bok::PkgName> { + use ::bok::Pkg; + #pkg_name::::default().path() + } + fn version(&self) -> ::bok::Version { + use ::bok::Pkg; + #pkg_name::::default().version() + } + fn as_any(&self) -> &dyn ::std::any::Any { + self + } + fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any { + self + } + fn default_unused(&mut self) -> &mut dyn ::bok::PkgBuilder { + let def = #pkg_name::::default(); + #(if self.#non_opt_fields5.is_none() { + self.#non_opt_fields5 = Some(def.#non_opt_fields5); + })* + + self + } + fn build(&mut self) -> + Result< + Box, + ::std::boxed::Box + > { + #(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(),)* + }) + ) + } + #(#local_funcs) + * + } + impl #name_builder + where + R: ::bok::Repository +'static + #trait_deps, + { + pub fn new() -> Self { + Self { + _bok_repo: ::std::marker::PhantomData::default(), + #(#non_opt_fields6: None,)* + #(#opt_fields4: None,)* + } + } + pub fn as_any(&self) -> &dyn ::std::any::Any { + self + } + pub fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any { + self + } + pub fn as_builder(&self) -> &dyn ::bok::PkgBuilder { + self + } + 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 { + 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)); + } + self.#non_opt_fields4 = Some(val); + self + }) + * + #(pub fn #opt_fields3 (&mut self, val : #opt_types2) -> &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)); + } + self.#opt_fields3 = Some(val); + self + }) + * + } + } + .into() +} + // implements ::bok::Pkg: // * add base methods // * copy the non-implemented methods from the base @@ -586,7 +874,7 @@ pub(crate) fn package_impl_base_add( for mut impl_item in ast.items.iter_mut() { if let ::syn::ImplItem::Fn(ref mut fn_impl) = &mut impl_item { - if ["prepare", "configure", "build", "check", "install"] + if ["prepare", "configure", "build", "test", "install"] .iter() .find(|&f| *f == fn_impl.sig.ident.to_string().as_str()) .is_none() @@ -687,8 +975,7 @@ pub(crate) fn package_impl_base_add( } Ok(()) } - for name in - ["prepare", "configure", "build", "check", "install"].into_iter() + for name in ["prepare", "configure", "build", "test", "install"].into_iter() { let Err(e) = maybe_add_fn(&name_pkg.to_string(), ast, &base_ast, name) else { @@ -707,8 +994,6 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream { let name = input.ident.clone(); let name_builder = quote::format_ident!("{name}Builder"); - let name_builder2 = name_builder.clone(); - let name_builder3 = name_builder.clone(); let elements = match &input.data { ::syn::Data::Struct(s) => match &s.fields { syn::Fields::Named(n) => n.named.clone(), @@ -752,108 +1037,6 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream { None }); let all_types2 = all_types.clone(); - let non_opt_fields = elements.iter().filter_map(|field| { - if let Some(id) = field.ident.clone() { - if id.to_string() == "version" - || id.to_string() == "_bok_base" - || id.to_string() == "_bok_repo" - { - return None; - } - } - match &field.ty { - syn::Type::Path(pth) => { - let first_path = pth.path.segments.first().unwrap(); - if first_path.ident == "Option" { - None - } else { - let id = &field.ident; - Some(quote! {#id}) - } - } - t => Some(quote! {#t}), - } - }); - let opt_fields = elements.iter().filter_map(|field| { - if let Some(id) = field.ident.clone() { - if id.to_string() == "version" - || id.to_string() == "_bok_base" - || id.to_string() == "_bok_repo" - { - return None; - } - } - match &field.ty { - syn::Type::Path(pth) => { - let first_path = pth.path.segments.first().unwrap(); - if first_path.ident == "Option" { - let id = &field.ident; - Some(quote! {#id}) - } else { - None - } - } - _ => None, - } - }); - let non_opt_types = elements.iter().filter_map(|field| { - if let Some(id) = field.ident.clone() { - if id.to_string() == "version" - || id.to_string() == "_bok_base" - || id.to_string() == "_bok_repo" - { - return None; - } - } - match &field.ty { - syn::Type::Path(pth) => { - let first_path = pth.path.segments.first().unwrap(); - if first_path.ident == "Option" { - None - } else { - let t = &field.ty; - Some(quote! {#t}) - } - } - _ => None, - } - }); - let opt_types = elements.iter().filter_map(|field| { - if let Some(id) = field.ident.clone() { - if id.to_string() == "version" - || id.to_string() == "_bok_base" - || id.to_string() == "_bok_repo" - { - return None; - } - } - if let ::syn::Type::Path(pth) = &field.ty { - let first_path = pth.path.segments.first().unwrap(); - if first_path.ident == "Option" { - if let syn::PathArguments::AngleBracketed( - syn::AngleBracketedGenericArguments { args, .. }, - ) = &first_path.arguments - { - if let Some(syn::GenericArgument::Type(syn::Type::Path( - p, - ))) = args.first() - { - let id = &p.path.segments.first().unwrap().ident; - return Some(quote! {#id}); - } - } - } - } - None - }); - 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 opt_fields2 = opt_fields.clone(); - let opt_fields3 = opt_fields.clone(); - let opt_types2 = opt_types.clone(); - let non_opt_types2 = non_opt_types.clone(); { let ::syn::Data::Struct(ref mut ds) = input.data else { @@ -941,7 +1124,7 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream { maybe_add_fn(&mut pkg_string, "prepare", &self.prepare_code().to_string())?; maybe_add_fn(&mut pkg_string, "configure", &self.configure_code().to_string())?; maybe_add_fn(&mut pkg_string, "build", &self.build_code().to_string())?; - maybe_add_fn(&mut pkg_string, "check", &self.check_code().to_string())?; + maybe_add_fn(&mut pkg_string, "test", &self.test_code().to_string())?; maybe_add_fn(&mut pkg_string, "install", &self.install_code().to_string())?; write!(&mut pkg_string, "}}\n")?; let re_parsed = ::syn::parse_file(&pkg_string).unwrap(); @@ -969,97 +1152,6 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream { &mut self.#all_fields3 })* } - #[derive(::std::default::Default, ::std::fmt::Debug)] - pub struct #name_builder - where - R: ::bok::Repository + 'static + #trait_deps, - { - _bok_repo: ::std::marker::PhantomData, - #(#non_opt_fields: ::std::option::Option<#non_opt_types>,)* - #(#opt_fields: ::std::option::Option<#opt_types>,)* - } - impl ::bok::PkgBuilder for #name_builder2 - where - R: ::bok::Repository + 'static + #trait_deps, - { - fn name(&self) -> ::bok::PkgName { - use ::bok::Pkg; - #name::::default().name() - } - fn path(&self) -> ::bok::Path<::bok::PkgName> { - use ::bok::Pkg; - #name::::default().path() - } - fn version(&self) -> ::bok::Version { - use ::bok::Pkg; - #name::::default().version() - } - fn as_any(&self) -> &dyn ::std::any::Any { - self - } - fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any { - self - } - fn default_unused(&mut self) -> &mut dyn ::bok::PkgBuilder { - let def = #name::::default(); - #(if self.#non_opt_fields5.is_none() { - self.#non_opt_fields5 = Some(def.#non_opt_fields5); - })* - - self - } - fn build(&mut self) -> Result, ::std::boxed::Box> { - #(if self.#non_opt_fields2.is_none() { - return ::std::result::Result::Err("unset field".into()); - })* - Ok( - Box::new(#name:: { - _bok_base: ::std::marker::PhantomData::default(), - _bok_repo: ::std::marker::PhantomData::default(), - version: #name::::default().version, - #(#non_opt_fields3 : self.#non_opt_fields3.clone().unwrap(),)* - #(#opt_fields2 : self.#opt_fields2.clone(),)* - }) - ) - } - } - impl #name_builder3 - where - R: ::bok::Repository +'static + #trait_deps, - { - pub fn as_any(&self) -> &dyn ::std::any::Any { - self - } - pub fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any { - self - } - pub fn as_builder(&self) -> &dyn ::bok::PkgBuilder { - self - } - 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 { - if self.#non_opt_fields4 != None && - self.#non_opt_fields4 != Some(val) { - panic!("Package \"{}\": mandatory attribute set multiple times: \"{}\"", - ::std::stringify!(#name), - ::std::stringify!(#non_opt_fields4)); - } - self.#non_opt_fields4 = Some(val); - self - })* - #(pub fn #opt_fields3 (&mut self, val : #opt_types2) -> &mut Self { - if self.#opt_fields3 != None && - self.#opt_fields3 != Some(val) { - panic!("Package \"{}\": optional attribute set multiple times: \"{}\"", - ::std::stringify!(#name), - ::std::stringify!(#opt_fields3)); - } - self.#opt_fields3 = Some(val); - self - })* - } }; TokenStream::from(expanded) diff --git a/bok-macro/src/repos.rs b/bok-macro/src/repos.rs index 8b5fea7..87b328c 100644 --- a/bok-macro/src/repos.rs +++ b/bok-macro/src/repos.rs @@ -193,8 +193,7 @@ pub(crate) fn derive_repository(input: TokenStream) -> TokenStream { } // holds the list of all package names, snake case let mut all_pkgs = Vec::<::syn::Ident>::with_capacity(items.fields.len()); - let mut all_deps_builder = - Vec::<::syn::Path>::with_capacity(items.fields.len()); + let mut all_deps_builder = Vec::with_capacity(items.fields.len()); for it in items.fields.iter() { let Some(id) = &it.ident else { continue }; @@ -205,12 +204,31 @@ pub(crate) fn derive_repository(input: TokenStream) -> TokenStream { let ::syn::Type::Path(raw_path) = &it.ty else { continue; }; + let phantom_args = &raw_path.path.segments.last().unwrap().arguments; + let ::syn::PathArguments::AngleBracketed(phantom_args) = phantom_args + else { + continue; + }; + let Some(pkg_t) = phantom_args.args.iter().find(|it| { + if let ::syn::GenericArgument::Type(_) = it { + return true; + } + false + }) else { + continue; + }; + let ::syn::GenericArgument::Type(::syn::Type::Path(pkg_t)) = pkg_t + else { + continue; + }; + let name = id_str.strip_prefix("_p_").unwrap().to_owned(); all_pkgs.push(::quote::format_ident!("{}", name)); - let mut dep_builder_trait = raw_path.path.clone(); + let mut dep_builder_trait = pkg_t.path.clone(); let pkg_t = &mut dep_builder_trait.segments.last_mut().unwrap(); pkg_t.ident = ::quote::format_ident!("BokBuilderDeps{}", pkg_t.ident); + pkg_t.arguments = ::syn::PathArguments::None; all_deps_builder.push(dep_builder_trait); } @@ -243,14 +261,9 @@ pub(crate) fn derive_repository(input: TokenStream) -> TokenStream { self } } - //#[::bok_macro::collection(#(#all_deps_builder,)*)] - struct Collection { - repo: ::std::sync::Arc<#name>, - pkgs: ::std::sync::RwLock< - ::std::vec::Vec< - ::std::sync::Arc< - dyn ::bok::PkgBuilder>>>, - } + use ::bok_macro::collection; + #[::bok_macro::collection(#name, #(#all_deps_builder),*)] + pub struct Collection {} }.into() } @@ -526,6 +539,12 @@ pub(crate) fn repo_impl_methods( }; t.path.clone() }; + let t_builder_id = { + let mut b_id = t_id.clone(); + let last = b_id.segments.last_mut().unwrap(); + last.ident = ::quote::format_ident!("{}Builder", last.ident); + b_id + }; let pkg_name = { let segment = t_id.segments.iter().last().unwrap(); @@ -535,6 +554,7 @@ pub(crate) fn repo_impl_methods( segment.ident.to_string().to_case(Case::Snake) ) }; + let pkgbuilder_name = ::quote::format_ident!("{}_builder", pkg_name); all_pkgs_types.push(t_id.clone()); if local .items @@ -552,18 +572,30 @@ pub(crate) fn repo_impl_methods( continue; } - let mut t_id_build = t_id.clone(); + let mut t_id_generic = t_id.clone(); if let ::syn::PathArguments::AngleBracketed(args) = - &mut t_id_build.segments.last_mut().unwrap().arguments + &mut t_id_generic.segments.last_mut().unwrap().arguments + { + args.colon2_token = Some(::syn::token::PathSep::default()); + } + let mut t_builder_id_generic = t_builder_id.clone(); + if let ::syn::PathArguments::AngleBracketed(args) = + &mut t_builder_id_generic.segments.last_mut().unwrap().arguments { args.colon2_token = Some(::syn::token::PathSep::default()); } let pkg_fn: ::syn::ImplItemFn = ::syn::parse_quote! { pub fn #pkg_name(&self) -> #t_id { - #t_id_build::default() + #t_id_generic::default() + } + }; + let pkgbuilder_fn: ::syn::ImplItemFn = ::syn::parse_quote! { + pub fn #pkgbuilder_name(&self) -> #t_builder_id { + #t_builder_id_generic::new() } }; fn_to_add.push(pkg_fn); + fn_to_add.push(pkgbuilder_fn); } // keep sorted for easier debugging when expanding macros diff --git a/bok-utils/src/main.rs b/bok-utils/src/main.rs index 6b17d07..fedb1d5 100644 --- a/bok-utils/src/main.rs +++ b/bok-utils/src/main.rs @@ -27,17 +27,19 @@ fn main() { use ::bok::Repository; println!("pkgs1: {}", pkgs1.name()); println!("pkgs2: {}", pkgs2.name()); + /* let build_deps = ::bok::deps::Build(::bok::Collection::new()); let run_deps = ::bok::deps::Runtime(::bok::Collection::new()); let one = pkgs1.one(); use ::bok::Pkg; - let res = one.dependencies_set(pkgs1, &build_deps, &run_deps); - println!("{:?}", res); + //let res = one.dependencies_set(pkgs1, &build_deps, &run_deps); + //println!("{:?}", res); let two = pkgs2.two(); - let res = two.dependencies_set(pkgs2, &build_deps, &run_deps); - println!("{:?}", res); + //let res = two.dependencies_set(pkgs2, &build_deps, &run_deps); + //println!("{:?}", res); + */ /* let mut pb: pkgs::one::OneBuilder = pkgs1.one(); diff --git a/bok-utils/src/repos/pkgs/one.rs b/bok-utils/src/repos/pkgs/one.rs index c580c15..fac6816 100644 --- a/bok-utils/src/repos/pkgs/one.rs +++ b/bok-utils/src/repos/pkgs/one.rs @@ -46,11 +46,16 @@ impl ::bok::Pkg for One { fn build(&self) -> ::core::result::Result<(), ()> { ::core::result::Result::Ok(()) } - fn dependencies_set( +} + +#[::bok::package_impl] +impl ::bok::PkgBuilder for One { + fn set_dependencies( &self, repo: ::std::sync::Arc, - _build: &::bok::deps::Build, - _runtime: &::bok::deps::Runtime, + _build: ::bok::deps::Build, + _runtime: ::bok::deps::Runtime, + _test: ::bok::deps::Test, ) -> Result<(), ()> { //TODO: automatically add repo type conversion via macro let repo_any = repo.as_any(); diff --git a/bok-utils/src/repos/pkgs/three.rs b/bok-utils/src/repos/pkgs/three.rs index 75eb459..a41afc1 100644 --- a/bok-utils/src/repos/pkgs/three.rs +++ b/bok-utils/src/repos/pkgs/three.rs @@ -47,11 +47,15 @@ impl ::bok::Pkg for Three { fn build(&self) -> ::core::result::Result<(), ()> { ::core::result::Result::Ok(()) } - fn dependencies_set( +} +#[::bok::package_impl] +impl ::bok::PkgBuilder for Three { + fn set_dependencies( &self, _repo: ::std::sync::Arc, - _build: &::bok::deps::Build, - _runtime: &::bok::deps::Runtime, + _build: ::bok::deps::Build, + _runtime: ::bok::deps::Runtime, + _test: ::bok::deps::Test, ) -> Result<(), ()> { Ok(()) } diff --git a/bok-utils/src/repos/pkgs/two.rs b/bok-utils/src/repos/pkgs/two.rs index 0aceb18..75f8576 100644 --- a/bok-utils/src/repos/pkgs/two.rs +++ b/bok-utils/src/repos/pkgs/two.rs @@ -42,12 +42,16 @@ impl ::std::default::Default for Two { } #[::bok::package_impl] -impl ::bok::Pkg for Two { - fn dependencies_set( +impl ::bok::Pkg for Two {} + +#[::bok::package_impl] +impl ::bok::PkgBuilder for Two { + fn set_dependencies( &self, repo: ::std::sync::Arc, - build: &::bok::deps::Build, - _runtime: &::bok::deps::Runtime, + build: ::bok::deps::Build, + _runtime: ::bok::deps::Runtime, + _test: ::bok::deps::Test, ) -> Result<(), ()> { //TODO: automatically add repo type conversion via macro let repo_any = repo.as_any(); diff --git a/bok/src/collection.rs b/bok/src/collection.rs index 5981168..0bf4d6a 100644 --- a/bok/src/collection.rs +++ b/bok/src/collection.rs @@ -35,81 +35,4 @@ type ArcLock = Arc>; type PkgMap = BTreeMap, (Arc, ArcLock)>; -/// Collection of packages, subset of what is present on one repository only -pub struct Collection { - repos: RwLock>>, - repos_pkgs: RwLock, PkgMap>>, -} - -impl Collection { - /// Create a new empty collection of pkgs - pub fn new() -> Collection { - Collection { - repos: RwLock::new(Vec::with_capacity(8)), - repos_pkgs: RwLock::new(BTreeMap::new()), - } - } - pub fn add_pkg( - &self, - r: Arc, - p: ArcLock, - ) -> Result<(), Error> { - let repo_name = r.path(); - let mut repos = self.repos.write().unwrap(); - let mut repos_pkgs = self.repos_pkgs.write().unwrap(); - if repos - .binary_search_by(|x| x.path().cmp(&repo_name)) - .is_err() - { - repos.push(r.clone()); - repos.sort_by(|a, b| a.path().cmp(&b.path())) - } - let pkg_name = p.read().unwrap().path(); - match repos_pkgs.get_mut(&repo_name) { - Some(pkg_map) => match pkg_map.get(&pkg_name) { - Some((r, p)) => { - return Err(Error::AlreadyPresent((r.clone(), p.clone()))); - } - None => { - pkg_map.insert(pkg_name, (r, p)); - } - }, - None => { - let mut pkg_map: PkgMap = BTreeMap::new(); - pkg_map.insert(pkg_name, (r, p)); - repos_pkgs.insert(repo_name, pkg_map); - } - } - - Ok(()) - } - pub fn find( - &self, - pkg_path: Path, - ) -> Result<(Arc, ArcLock), Error> { - let repos_pkgs = self.repos_pkgs.read().unwrap(); - for (_, pkg_map) in repos_pkgs.iter() { - match pkg_map.get(&pkg_path) { - Some((r, p)) => { - return Ok((r.clone(), p.clone())); - } - None => {} - } - } - Err(Error::NotFound) - } - pub fn find_in_repo( - &self, - repo_path: Path, - pkg_path: Path, - ) -> Result<(Arc, ArcLock), Error> { - let repos_pkgs = self.repos_pkgs.read().unwrap(); - match repos_pkgs.get(&repo_path) { - None => Err(Error::NotFound), - Some(pkg_map) => match pkg_map.get(&pkg_path) { - Some((r, p)) => Ok((r.clone(), p.clone())), - None => Err(Error::NotFound), - }, - } - } -} +pub trait Collection {} diff --git a/bok/src/lib.rs b/bok/src/lib.rs index 70fdbde..b28f553 100644 --- a/bok/src/lib.rs +++ b/bok/src/lib.rs @@ -22,8 +22,9 @@ mod names; pub use collection::Collection; pub use names::{Path, PkgName, RepoName}; pub mod deps { - pub struct Build(pub crate::Collection); - pub struct Runtime(pub crate::Collection); + pub struct Build(pub ::std::sync::Arc); + pub struct Runtime(pub ::std::sync::Arc); + pub struct Test(pub ::std::sync::Arc); } pub use ::bok_macro::{ @@ -95,27 +96,21 @@ pub trait Pkg: fn version(&self) -> Version; fn as_any(&self) -> &dyn ::std::any::Any; fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any; - fn dependencies_set( - &self, - repo: Arc, - build: &deps::Build, - runtime: &deps::Runtime, - ) -> Result<(), ()>; fn prepare(&self) -> ::core::result::Result<(), ()>; fn prepare_code(&self) -> ::proc_macro2::TokenStream; fn configure(&self) -> ::core::result::Result<(), ()>; fn configure_code(&self) -> ::proc_macro2::TokenStream; fn build(&self) -> ::core::result::Result<(), ()>; fn build_code(&self) -> ::proc_macro2::TokenStream; - fn check(&self) -> ::core::result::Result<(), ()>; - fn check_code(&self) -> ::proc_macro2::TokenStream; + fn test(&self) -> ::core::result::Result<(), ()>; + fn test_code(&self) -> ::proc_macro2::TokenStream; fn install(&self) -> ::core::result::Result<(), ()>; fn install_code(&self) -> ::proc_macro2::TokenStream; fn package(&self) -> ::core::result::Result<(), ()> { self.prepare()?; self.configure()?; self.build()?; - self.check()?; + self.test()?; self.install() } fn hash(&self) -> Hash; @@ -123,12 +118,19 @@ pub trait Pkg: } pub trait PkgBuilder: ::core::fmt::Debug + ::std::any::Any { - fn name(&self) -> crate::PkgName; - fn path(&self) -> crate::Path; - fn version(&self) -> crate::Version; + fn name(&self) -> PkgName; + fn path(&self) -> Path; + fn version(&self) -> Version; fn as_any(&self) -> &dyn ::std::any::Any; fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any; - fn default_unused(&mut self) -> &mut dyn crate::PkgBuilder; + fn default_unused(&mut self) -> &mut dyn PkgBuilder; + fn set_dependencies( + &self, + repo: Arc, + build: deps::Build, + runtime: deps::Runtime, + test: deps::Test, + ) -> Result<(), ()>; fn build( &mut self, ) -> Result, ::std::boxed::Box>; @@ -183,14 +185,6 @@ impl Pkg for PkgEmpty { fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any { self } - fn dependencies_set( - &self, - _repo: Arc, - _build: &deps::Build, - _runtime: &deps::Runtime, - ) -> Result<(), ()> { - Ok(()) - } fn prepare(&self) -> ::core::result::Result<(), ()> { ::core::result::Result::Ok(()) } @@ -215,10 +209,10 @@ impl Pkg for PkgEmpty { ::core::result::Result::Ok(()); } } - fn check(&self) -> ::core::result::Result<(), ()> { + fn test(&self) -> ::core::result::Result<(), ()> { ::core::result::Result::Ok(()) } - fn check_code(&self) -> ::proc_macro2::TokenStream { + fn test_code(&self) -> ::proc_macro2::TokenStream { ::quote::quote! { ::core::result::Result::Ok(()); }