Collection: more work, pkg_builders

Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
Luca Fulchir 2024-12-08 18:08:20 +01:00
parent 2e75c8a8f9
commit c80cabe74e
Signed by: luca.fulchir
GPG Key ID: 8F6440603D13A78E
10 changed files with 585 additions and 367 deletions

112
bok-macro/src/collection.rs Normal file
View File

@ -0,0 +1,112 @@
/*
* Copyright 2024 Luca Fulchir <luca.fulchir@runesauth.com>
*
* 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()
}

View File

@ -18,6 +18,7 @@
#![feature(proc_macro_span)] #![feature(proc_macro_span)]
use ::proc_macro::TokenStream; use ::proc_macro::TokenStream;
mod collection;
mod pkgs; mod pkgs;
mod repos; mod repos;
@ -80,7 +81,7 @@ pub fn repo_impl(attrs: TokenStream, input: TokenStream) -> TokenStream {
crate::repos::repo_impl(attrs, input) 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 /// Create the methods that will return the builders and packages
/// Usage: /// Usage:
/// ``` /// ```
@ -96,7 +97,7 @@ pub fn repo_impl_methods(
crate::repos::repo_impl_methods(attrs, input, __source_path) 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...", /// given a Repository and a trait "BokDeps...",
/// implement all the fn returning the dependencies needed by the package /// 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) 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 ########## // ####### Package stuff ##########
// //
@ -208,7 +226,7 @@ pub fn package_impl(attrs: TokenStream, input: TokenStream) -> TokenStream {
crate::pkgs::package_impl(attrs, input) 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)] /// Use as #[::bok::package_impl_base(::my::Package)]
/// implements common ::bok::Pkg methods and takes the package methods /// implements common ::bok::Pkg methods and takes the package methods
/// not implemented by the user from the base package /// not implemented by the user from the base package
@ -224,7 +242,7 @@ pub fn package_impl(attrs: TokenStream, input: TokenStream) -> TokenStream {
/// e.g.: /// e.g.:
/// ``` /// ```
/// #[::bok::package_impl_base(MyPackage)] /// #[::bok::package_impl_base(MyPackage)]
/// #[::macro_magic::export_tokens(bok_pkg_mypackage)] /// #[::macro_magic::export_tokens(_bok_pkg_mypackage)]
/// impl ::bok::Pkg for MyPackage { /// impl ::bok::Pkg for MyPackage {
/// fn build(&self) -> Result<(),()> { /// fn build(&self) -> Result<(),()> {
/// ... /// ...
@ -240,6 +258,38 @@ pub fn package_impl_base(
crate::pkgs::package_impl_base(attrs, input) 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)]` /// Unless you know what you are doing, use `#[::bok::package(MyBasePackage)]`
/// instead needs a `.base` field /// instead needs a `.base` field
/// ///

View File

@ -268,6 +268,17 @@ pub(crate) fn deps_build(
quote::format_ident!("{}", ident.to_string().to_case(Case::Snake)) quote::format_ident!("{}", ident.to_string().to_case(Case::Snake))
}) })
.collect::<Vec<::syn::Ident>>(); .collect::<Vec<::syn::Ident>>();
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::<Vec<::syn::Ident>>();
quote! { quote! {
#(#local_attrs) #(#local_attrs)
@ -283,7 +294,7 @@ pub(crate) fn deps_build(
} }
#[::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(&self) -> ::std::boxed::Box<dyn ::bok::PkgBuilder>;) #(fn #deps_builders(&self) -> ::std::boxed::Box<dyn ::bok::PkgBuilder>;)
* *
} }
} }
@ -303,11 +314,19 @@ pub(crate) fn package_impl(
) -> TokenStream { ) -> TokenStream {
let mut ast = parse_macro_input!(input as ::syn::ItemImpl); let mut ast = parse_macro_input!(input as ::syn::ItemImpl);
if let Some((_, trait_name, _)) = &ast.trait_ { 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; let s = &trait_name.segments;
if s.len() != 2 if s.len() != 2
|| s[0].ident.to_string() != "bok" || s[0].ident.to_string() != "bok"
|| s[1].ident.to_string() != "Pkg" || (s[1].ident.to_string() != "Pkg"
&& s[1].ident.to_string() != "PkgBuilder")
{ {
// //
// only add the generic parameter and nothing else // only add the generic parameter and nothing else
@ -336,8 +355,6 @@ pub(crate) fn package_impl(
} }
.into(); .into();
} }
}
let ast = ast; // remove mut
let name_pkg = match &*ast.self_ty { let name_pkg = match &*ast.self_ty {
::syn::Type::Path(tp) => match tp.path.get_ident() { ::syn::Type::Path(tp) => match tp.path.get_ident() {
@ -360,6 +377,16 @@ pub(crate) fn package_impl(
.into() .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( let full_path_name = proc_macro2::Ident::new(
&("_bok_pkg_".to_owned() + &name_pkg.to_string()), &("_bok_pkg_".to_owned() + &name_pkg.to_string()),
proc_macro2::Span::call_site(), proc_macro2::Span::call_site(),
@ -518,6 +545,267 @@ pub(crate) fn package_impl_base(
.into(); .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<R>
where
R: ::bok::Repository + 'static + #trait_deps,
{
_bok_repo: ::std::marker::PhantomData<R>,
#(#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<R> ::bok::PkgBuilder for #name_builder<R>
where
R: ::bok::Repository + 'static + #trait_deps,
{
fn name(&self) -> ::bok::PkgName {
use ::bok::Pkg;
#pkg_name::<R>::default().name()
}
fn path(&self) -> ::bok::Path<::bok::PkgName> {
use ::bok::Pkg;
#pkg_name::<R>::default().path()
}
fn version(&self) -> ::bok::Version {
use ::bok::Pkg;
#pkg_name::<R>::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::<R>::default();
#(if self.#non_opt_fields5.is_none() {
self.#non_opt_fields5 = Some(def.#non_opt_fields5);
})*
self
}
fn build(&mut self) ->
Result<
Box<dyn ::bok::Pkg>,
::std::boxed::Box<dyn ::std::error::Error>
> {
#(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(),)*
})
)
}
#(#local_funcs)
*
}
impl<R> #name_builder<R>
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: // implements ::bok::Pkg:
// * add base methods // * add base methods
// * copy the non-implemented methods from the base // * 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() { for mut impl_item in ast.items.iter_mut() {
if let ::syn::ImplItem::Fn(ref mut fn_impl) = &mut impl_item { 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() .iter()
.find(|&f| *f == fn_impl.sig.ident.to_string().as_str()) .find(|&f| *f == fn_impl.sig.ident.to_string().as_str())
.is_none() .is_none()
@ -687,8 +975,7 @@ pub(crate) fn package_impl_base_add(
} }
Ok(()) Ok(())
} }
for name in for name in ["prepare", "configure", "build", "test", "install"].into_iter()
["prepare", "configure", "build", "check", "install"].into_iter()
{ {
let Err(e) = maybe_add_fn(&name_pkg.to_string(), ast, &base_ast, name) let Err(e) = maybe_add_fn(&name_pkg.to_string(), ast, &base_ast, name)
else { else {
@ -707,8 +994,6 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream {
let name = input.ident.clone(); let name = input.ident.clone();
let name_builder = quote::format_ident!("{name}Builder"); 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 { let elements = match &input.data {
::syn::Data::Struct(s) => match &s.fields { ::syn::Data::Struct(s) => match &s.fields {
syn::Fields::Named(n) => n.named.clone(), syn::Fields::Named(n) => n.named.clone(),
@ -752,108 +1037,6 @@ pub(crate) fn derive_package(input: TokenStream) -> TokenStream {
None None
}); });
let all_types2 = all_types.clone(); 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 { 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, "prepare", &self.prepare_code().to_string())?;
maybe_add_fn(&mut pkg_string, "configure", &self.configure_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, "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())?; maybe_add_fn(&mut pkg_string, "install", &self.install_code().to_string())?;
write!(&mut pkg_string, "}}\n")?; write!(&mut pkg_string, "}}\n")?;
let re_parsed = ::syn::parse_file(&pkg_string).unwrap(); 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 &mut self.#all_fields3
})* })*
} }
#[derive(::std::default::Default, ::std::fmt::Debug)]
pub struct #name_builder<R>
where
R: ::bok::Repository + 'static + #trait_deps,
{
_bok_repo: ::std::marker::PhantomData<R>,
#(#non_opt_fields: ::std::option::Option<#non_opt_types>,)*
#(#opt_fields: ::std::option::Option<#opt_types>,)*
}
impl<R> ::bok::PkgBuilder for #name_builder2<R>
where
R: ::bok::Repository + 'static + #trait_deps,
{
fn name(&self) -> ::bok::PkgName {
use ::bok::Pkg;
#name::<R>::default().name()
}
fn path(&self) -> ::bok::Path<::bok::PkgName> {
use ::bok::Pkg;
#name::<R>::default().path()
}
fn version(&self) -> ::bok::Version {
use ::bok::Pkg;
#name::<R>::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::<R>::default();
#(if self.#non_opt_fields5.is_none() {
self.#non_opt_fields5 = Some(def.#non_opt_fields5);
})*
self
}
fn build(&mut self) -> Result<Box<dyn ::bok::Pkg>, ::std::boxed::Box<dyn ::std::error::Error>> {
#(if self.#non_opt_fields2.is_none() {
return ::std::result::Result::Err("unset field".into());
})*
Ok(
Box::new(#name::<R> {
_bok_base: ::std::marker::PhantomData::default(),
_bok_repo: ::std::marker::PhantomData::default(),
version: #name::<R>::default().version,
#(#non_opt_fields3 : self.#non_opt_fields3.clone().unwrap(),)*
#(#opt_fields2 : self.#opt_fields2.clone(),)*
})
)
}
}
impl<R> #name_builder3<R>
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) TokenStream::from(expanded)

View File

@ -193,8 +193,7 @@ pub(crate) fn derive_repository(input: TokenStream) -> TokenStream {
} }
// holds the list of all package names, snake case // holds the list of all package names, snake case
let mut all_pkgs = Vec::<::syn::Ident>::with_capacity(items.fields.len()); let mut all_pkgs = Vec::<::syn::Ident>::with_capacity(items.fields.len());
let mut all_deps_builder = let mut all_deps_builder = Vec::with_capacity(items.fields.len());
Vec::<::syn::Path>::with_capacity(items.fields.len());
for it in items.fields.iter() { for it in items.fields.iter() {
let Some(id) = &it.ident else { continue }; 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 { let ::syn::Type::Path(raw_path) = &it.ty else {
continue; 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(); let name = id_str.strip_prefix("_p_").unwrap().to_owned();
all_pkgs.push(::quote::format_ident!("{}", name)); 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(); let pkg_t = &mut dep_builder_trait.segments.last_mut().unwrap();
pkg_t.ident = ::quote::format_ident!("BokBuilderDeps{}", pkg_t.ident); pkg_t.ident = ::quote::format_ident!("BokBuilderDeps{}", pkg_t.ident);
pkg_t.arguments = ::syn::PathArguments::None;
all_deps_builder.push(dep_builder_trait); all_deps_builder.push(dep_builder_trait);
} }
@ -243,14 +261,9 @@ pub(crate) fn derive_repository(input: TokenStream) -> TokenStream {
self self
} }
} }
//#[::bok_macro::collection(#(#all_deps_builder,)*)] use ::bok_macro::collection;
struct Collection { #[::bok_macro::collection(#name, #(#all_deps_builder),*)]
repo: ::std::sync::Arc<#name>, pub struct Collection {}
pkgs: ::std::sync::RwLock<
::std::vec::Vec<
::std::sync::Arc<
dyn ::bok::PkgBuilder>>>,
}
}.into() }.into()
} }
@ -526,6 +539,12 @@ pub(crate) fn repo_impl_methods(
}; };
t.path.clone() 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 pkg_name = {
let segment = t_id.segments.iter().last().unwrap(); 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) segment.ident.to_string().to_case(Case::Snake)
) )
}; };
let pkgbuilder_name = ::quote::format_ident!("{}_builder", pkg_name);
all_pkgs_types.push(t_id.clone()); all_pkgs_types.push(t_id.clone());
if local if local
.items .items
@ -552,18 +572,30 @@ pub(crate) fn repo_impl_methods(
continue; continue;
} }
let mut t_id_build = t_id.clone(); let mut t_id_generic = t_id.clone();
if let ::syn::PathArguments::AngleBracketed(args) = 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()); args.colon2_token = Some(::syn::token::PathSep::default());
} }
let pkg_fn: ::syn::ImplItemFn = ::syn::parse_quote! { let pkg_fn: ::syn::ImplItemFn = ::syn::parse_quote! {
pub fn #pkg_name(&self) -> #t_id { 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(pkg_fn);
fn_to_add.push(pkgbuilder_fn);
} }
// keep sorted for easier debugging when expanding macros // keep sorted for easier debugging when expanding macros

View File

@ -27,17 +27,19 @@ fn main() {
use ::bok::Repository; use ::bok::Repository;
println!("pkgs1: {}", pkgs1.name()); println!("pkgs1: {}", pkgs1.name());
println!("pkgs2: {}", pkgs2.name()); println!("pkgs2: {}", pkgs2.name());
/*
let build_deps = ::bok::deps::Build(::bok::Collection::new()); let build_deps = ::bok::deps::Build(::bok::Collection::new());
let run_deps = ::bok::deps::Runtime(::bok::Collection::new()); let run_deps = ::bok::deps::Runtime(::bok::Collection::new());
let one = pkgs1.one(); let one = pkgs1.one();
use ::bok::Pkg; use ::bok::Pkg;
let res = one.dependencies_set(pkgs1, &build_deps, &run_deps); //let res = one.dependencies_set(pkgs1, &build_deps, &run_deps);
println!("{:?}", res); //println!("{:?}", res);
let two = pkgs2.two(); let two = pkgs2.two();
let res = two.dependencies_set(pkgs2, &build_deps, &run_deps); //let res = two.dependencies_set(pkgs2, &build_deps, &run_deps);
println!("{:?}", res); //println!("{:?}", res);
*/
/* /*
let mut pb: pkgs::one::OneBuilder = pkgs1.one(); let mut pb: pkgs::one::OneBuilder = pkgs1.one();

View File

@ -46,11 +46,16 @@ impl ::bok::Pkg for One {
fn build(&self) -> ::core::result::Result<(), ()> { fn build(&self) -> ::core::result::Result<(), ()> {
::core::result::Result::Ok(()) ::core::result::Result::Ok(())
} }
fn dependencies_set( }
#[::bok::package_impl]
impl ::bok::PkgBuilder for One {
fn set_dependencies(
&self, &self,
repo: ::std::sync::Arc<dyn ::bok::Repository>, repo: ::std::sync::Arc<dyn ::bok::Repository>,
_build: &::bok::deps::Build, _build: ::bok::deps::Build,
_runtime: &::bok::deps::Runtime, _runtime: ::bok::deps::Runtime,
_test: ::bok::deps::Test,
) -> Result<(), ()> { ) -> Result<(), ()> {
//TODO: automatically add repo type conversion via macro //TODO: automatically add repo type conversion via macro
let repo_any = repo.as_any(); let repo_any = repo.as_any();

View File

@ -47,11 +47,15 @@ impl ::bok::Pkg for Three {
fn build(&self) -> ::core::result::Result<(), ()> { fn build(&self) -> ::core::result::Result<(), ()> {
::core::result::Result::Ok(()) ::core::result::Result::Ok(())
} }
fn dependencies_set( }
#[::bok::package_impl]
impl ::bok::PkgBuilder for Three {
fn set_dependencies(
&self, &self,
_repo: ::std::sync::Arc<dyn ::bok::Repository>, _repo: ::std::sync::Arc<dyn ::bok::Repository>,
_build: &::bok::deps::Build, _build: ::bok::deps::Build,
_runtime: &::bok::deps::Runtime, _runtime: ::bok::deps::Runtime,
_test: ::bok::deps::Test,
) -> Result<(), ()> { ) -> Result<(), ()> {
Ok(()) Ok(())
} }

View File

@ -42,12 +42,16 @@ impl ::std::default::Default for Two {
} }
#[::bok::package_impl] #[::bok::package_impl]
impl ::bok::Pkg for Two { impl ::bok::Pkg for Two {}
fn dependencies_set(
#[::bok::package_impl]
impl ::bok::PkgBuilder for Two {
fn set_dependencies(
&self, &self,
repo: ::std::sync::Arc<dyn ::bok::Repository>, repo: ::std::sync::Arc<dyn ::bok::Repository>,
build: &::bok::deps::Build, build: ::bok::deps::Build,
_runtime: &::bok::deps::Runtime, _runtime: ::bok::deps::Runtime,
_test: ::bok::deps::Test,
) -> Result<(), ()> { ) -> Result<(), ()> {
//TODO: automatically add repo type conversion via macro //TODO: automatically add repo type conversion via macro
let repo_any = repo.as_any(); let repo_any = repo.as_any();

View File

@ -35,81 +35,4 @@ type ArcLock<T> = Arc<RwLock<T>>;
type PkgMap = type PkgMap =
BTreeMap<Path<PkgName>, (Arc<dyn Repository>, ArcLock<dyn PkgBuilder>)>; BTreeMap<Path<PkgName>, (Arc<dyn Repository>, ArcLock<dyn PkgBuilder>)>;
/// Collection of packages, subset of what is present on one repository only pub trait Collection {}
pub struct Collection {
repos: RwLock<Vec<Arc<dyn Repository>>>,
repos_pkgs: RwLock<BTreeMap<Path<RepoName>, 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<dyn Repository>,
p: ArcLock<dyn PkgBuilder>,
) -> 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<PkgName>,
) -> Result<(Arc<dyn Repository>, ArcLock<dyn PkgBuilder>), 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<RepoName>,
pkg_path: Path<PkgName>,
) -> Result<(Arc<dyn Repository>, ArcLock<dyn PkgBuilder>), 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),
},
}
}
}

View File

@ -22,8 +22,9 @@ mod names;
pub use collection::Collection; pub use collection::Collection;
pub use names::{Path, PkgName, RepoName}; pub use names::{Path, PkgName, RepoName};
pub mod deps { pub mod deps {
pub struct Build(pub crate::Collection); pub struct Build(pub ::std::sync::Arc<dyn crate::Collection>);
pub struct Runtime(pub crate::Collection); pub struct Runtime(pub ::std::sync::Arc<dyn crate::Collection>);
pub struct Test(pub ::std::sync::Arc<dyn crate::Collection>);
} }
pub use ::bok_macro::{ pub use ::bok_macro::{
@ -95,27 +96,21 @@ pub trait Pkg:
fn version(&self) -> Version; fn version(&self) -> Version;
fn as_any(&self) -> &dyn ::std::any::Any; fn as_any(&self) -> &dyn ::std::any::Any;
fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any; fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any;
fn dependencies_set(
&self,
repo: Arc<dyn Repository>,
build: &deps::Build,
runtime: &deps::Runtime,
) -> Result<(), ()>;
fn prepare(&self) -> ::core::result::Result<(), ()>; fn prepare(&self) -> ::core::result::Result<(), ()>;
fn prepare_code(&self) -> ::proc_macro2::TokenStream; fn prepare_code(&self) -> ::proc_macro2::TokenStream;
fn configure(&self) -> ::core::result::Result<(), ()>; fn configure(&self) -> ::core::result::Result<(), ()>;
fn configure_code(&self) -> ::proc_macro2::TokenStream; fn configure_code(&self) -> ::proc_macro2::TokenStream;
fn build(&self) -> ::core::result::Result<(), ()>; fn build(&self) -> ::core::result::Result<(), ()>;
fn build_code(&self) -> ::proc_macro2::TokenStream; fn build_code(&self) -> ::proc_macro2::TokenStream;
fn check(&self) -> ::core::result::Result<(), ()>; fn test(&self) -> ::core::result::Result<(), ()>;
fn check_code(&self) -> ::proc_macro2::TokenStream; fn test_code(&self) -> ::proc_macro2::TokenStream;
fn install(&self) -> ::core::result::Result<(), ()>; fn install(&self) -> ::core::result::Result<(), ()>;
fn install_code(&self) -> ::proc_macro2::TokenStream; fn install_code(&self) -> ::proc_macro2::TokenStream;
fn package(&self) -> ::core::result::Result<(), ()> { fn package(&self) -> ::core::result::Result<(), ()> {
self.prepare()?; self.prepare()?;
self.configure()?; self.configure()?;
self.build()?; self.build()?;
self.check()?; self.test()?;
self.install() self.install()
} }
fn hash(&self) -> Hash; fn hash(&self) -> Hash;
@ -123,12 +118,19 @@ pub trait Pkg:
} }
pub trait PkgBuilder: ::core::fmt::Debug + ::std::any::Any { pub trait PkgBuilder: ::core::fmt::Debug + ::std::any::Any {
fn name(&self) -> crate::PkgName; fn name(&self) -> PkgName;
fn path(&self) -> crate::Path<crate::PkgName>; fn path(&self) -> Path<PkgName>;
fn version(&self) -> crate::Version; fn version(&self) -> Version;
fn as_any(&self) -> &dyn ::std::any::Any; fn as_any(&self) -> &dyn ::std::any::Any;
fn as_any_mut(&mut self) -> &mut 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<dyn Repository>,
build: deps::Build,
runtime: deps::Runtime,
test: deps::Test,
) -> Result<(), ()>;
fn build( fn build(
&mut self, &mut self,
) -> Result<Box<dyn Pkg>, ::std::boxed::Box<dyn ::std::error::Error>>; ) -> Result<Box<dyn Pkg>, ::std::boxed::Box<dyn ::std::error::Error>>;
@ -183,14 +185,6 @@ impl Pkg for PkgEmpty {
fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any { fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any {
self self
} }
fn dependencies_set(
&self,
_repo: Arc<dyn Repository>,
_build: &deps::Build,
_runtime: &deps::Runtime,
) -> Result<(), ()> {
Ok(())
}
fn prepare(&self) -> ::core::result::Result<(), ()> { fn prepare(&self) -> ::core::result::Result<(), ()> {
::core::result::Result::Ok(()) ::core::result::Result::Ok(())
} }
@ -215,10 +209,10 @@ impl Pkg for PkgEmpty {
::core::result::Result::Ok(()); ::core::result::Result::Ok(());
} }
} }
fn check(&self) -> ::core::result::Result<(), ()> { fn test(&self) -> ::core::result::Result<(), ()> {
::core::result::Result::Ok(()) ::core::result::Result::Ok(())
} }
fn check_code(&self) -> ::proc_macro2::TokenStream { fn test_code(&self) -> ::proc_macro2::TokenStream {
::quote::quote! { ::quote::quote! {
::core::result::Result::Ok(()); ::core::result::Result::Ok(());
} }