2024-11-16 11:52:55 +00:00
|
|
|
/*
|
|
|
|
* 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::Parser, parse_macro_input, DeriveInput};
|
2024-11-29 15:59:22 +00:00
|
|
|
use syn::{parse_quote, spanned::Spanned};
|
2024-11-16 11:52:55 +00:00
|
|
|
|
|
|
|
pub(crate) fn pkg_fn_to_code(
|
|
|
|
_attrs: TokenStream,
|
|
|
|
input: TokenStream,
|
|
|
|
) -> TokenStream {
|
2024-11-29 15:59:22 +00:00
|
|
|
let ast_orig: ::syn::ItemFn = parse_macro_input!(input as ::syn::ItemFn);
|
2024-11-16 11:52:55 +00:00
|
|
|
|
|
|
|
let mut ast_new = ast_orig.clone();
|
|
|
|
|
2024-11-29 15:59:22 +00:00
|
|
|
let new_fn_name = quote::format_ident!("{}_code", ast_orig.sig.ident);
|
|
|
|
ast_new.sig.ident = quote::format_ident!("{}_code", new_fn_name);
|
2024-11-16 11:52:55 +00:00
|
|
|
use ::quote::ToTokens;
|
2024-11-29 15:59:22 +00:00
|
|
|
let fn_block = ast_orig.block.to_token_stream();
|
2024-11-16 11:52:55 +00:00
|
|
|
|
|
|
|
let asdf = quote! {
|
|
|
|
#ast_orig
|
|
|
|
fn #new_fn_name (&self) -> ::proc_macro2::TokenStream {
|
|
|
|
::quote::quote! #fn_block
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
asdf.into()
|
|
|
|
}
|
|
|
|
|
2024-12-03 21:40:16 +00:00
|
|
|
pub(crate) fn package(attrs: TokenStream, input: TokenStream) -> TokenStream {
|
|
|
|
let local = parse_macro_input!(input as ::syn::ItemStruct);
|
|
|
|
let mut path = parse_macro_input!(attrs as ::syn::Path);
|
|
|
|
|
|
|
|
let last = path.segments.last().unwrap();
|
|
|
|
if last.arguments.is_empty() {
|
|
|
|
// make sure all the packages have generics, and if not add the
|
|
|
|
// repo as generic aka: rewrite
|
|
|
|
// "#[package(my::pkg)]" -->> "#[package(my::pkg<R>)])"
|
|
|
|
// unless it's '::bok::PkgEmpty'
|
|
|
|
if path.segments.len() != 2
|
|
|
|
|| path.segments[0].ident.to_string() != "bok"
|
|
|
|
|| path.segments[1].ident.to_string() != "PkgEmpty"
|
|
|
|
{
|
|
|
|
let repo_argument = {
|
|
|
|
let generic_id: ::syn::Path = ::syn::parse_quote!(id<R>);
|
|
|
|
generic_id.segments.last().unwrap().arguments.clone()
|
|
|
|
};
|
|
|
|
path.segments.last_mut().unwrap().arguments = repo_argument;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return quote! {
|
|
|
|
use ::bok_macro::package_path;
|
|
|
|
#[::bok_macro::package_path(#path)]
|
|
|
|
#local
|
|
|
|
}
|
|
|
|
.into();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn package_path(
|
2024-11-29 15:59:22 +00:00
|
|
|
attrs: TokenStream,
|
|
|
|
input: TokenStream,
|
|
|
|
__source_path: TokenStream,
|
|
|
|
) -> TokenStream {
|
|
|
|
use ::syn::{spanned::Spanned, Fields, ItemStruct};
|
|
|
|
let local = parse_macro_input!(input as ItemStruct);
|
|
|
|
let local_span = local.span();
|
|
|
|
let Fields::Named(local_fields) = local.fields else {
|
|
|
|
use ::syn::spanned::Spanned;
|
|
|
|
return ::syn::Error::new(
|
|
|
|
local.fields.span(),
|
2024-12-03 16:26:53 +00:00
|
|
|
"#[::bok::package()]: unnamed fields are not supported",
|
2024-11-29 15:59:22 +00:00
|
|
|
)
|
|
|
|
.to_compile_error()
|
|
|
|
.into();
|
|
|
|
};
|
|
|
|
let local_attrs = local.attrs.iter();
|
|
|
|
let ident = local.ident;
|
|
|
|
let vis = local.vis;
|
2024-12-03 16:26:53 +00:00
|
|
|
let local_generics = local.generics;
|
|
|
|
if local_generics.params.len() != 0 {
|
|
|
|
return ::syn::Error::new(
|
|
|
|
local_span,
|
|
|
|
"#[::bok::package()]: a package can not have generics",
|
|
|
|
)
|
|
|
|
.to_compile_error()
|
|
|
|
.into();
|
|
|
|
};
|
|
|
|
let generic_struct: ::syn::ItemStruct = ::syn::parse_quote! {
|
2024-12-03 20:14:12 +00:00
|
|
|
pub struct test<R> where R: ::bok::Repository + 'static {}
|
2024-12-03 16:26:53 +00:00
|
|
|
};
|
|
|
|
let (_, generics, where_clause) = generic_struct.generics.split_for_impl();
|
2024-11-29 15:59:22 +00:00
|
|
|
|
|
|
|
let source_path = parse_macro_input!(__source_path as ::syn::Path);
|
|
|
|
|
|
|
|
let base = parse_macro_input!(attrs as ItemStruct);
|
|
|
|
let Fields::Named(base_fields) = &base.fields else {
|
|
|
|
use ::syn::spanned::Spanned;
|
|
|
|
return ::syn::Error::new(
|
|
|
|
base.fields.span(),
|
2024-12-03 16:26:53 +00:00
|
|
|
"#[::bok::package(..)]: base: unnamed fields are not supported",
|
2024-11-29 15:59:22 +00:00
|
|
|
)
|
|
|
|
.to_compile_error()
|
|
|
|
.into();
|
|
|
|
};
|
|
|
|
// make sure the 'version' field is the first after the user's fields
|
|
|
|
// we will use as a separator from the other imported fields
|
|
|
|
let mut base_fields_extra = Vec::with_capacity(base.fields.len());
|
|
|
|
match base_fields.named.iter().find(|&x| match &x.ident {
|
|
|
|
Some(id_local) => id_local == "version",
|
|
|
|
_ => false,
|
|
|
|
}) {
|
|
|
|
Some(_) => {
|
|
|
|
let pkg_version = ::syn::Field::parse_named
|
|
|
|
.parse2(quote! {
|
|
|
|
version: ::bok::Version
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
let base_marker = ::syn::Field::parse_named
|
|
|
|
.parse2(quote! {
|
|
|
|
_bok_base: ::std::marker::PhantomData<#source_path>
|
|
|
|
})
|
|
|
|
.unwrap();
|
2024-12-03 16:26:53 +00:00
|
|
|
let repo_marker = ::syn::Field::parse_named
|
|
|
|
.parse2(quote! {
|
|
|
|
_bok_repo: ::std::marker::PhantomData<R>
|
|
|
|
})
|
|
|
|
.unwrap();
|
2024-11-29 15:59:22 +00:00
|
|
|
base_fields_extra.push(pkg_version);
|
|
|
|
base_fields_extra.push(base_marker);
|
2024-12-03 16:26:53 +00:00
|
|
|
base_fields_extra.push(repo_marker);
|
2024-11-29 15:59:22 +00:00
|
|
|
}
|
|
|
|
None => {
|
|
|
|
return ::syn::Error::new(
|
|
|
|
local_span,
|
|
|
|
"pkg: \"".to_owned()
|
|
|
|
+ &ident.to_string()
|
|
|
|
+ "\" base package \""
|
|
|
|
+ &base.ident.to_string()
|
|
|
|
+ "\" does not have 'version' field",
|
|
|
|
)
|
|
|
|
.to_compile_error()
|
|
|
|
.into();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for f in base_fields.named.iter() {
|
|
|
|
let Some(id) = &f.ident else { continue };
|
|
|
|
// don't add 'version' again
|
2024-12-03 16:26:53 +00:00
|
|
|
if id.to_string() == "version"
|
|
|
|
|| id.to_string() == "_bok_base"
|
|
|
|
|| id.to_string() == "_bok_repo"
|
|
|
|
{
|
2024-11-29 15:59:22 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// compiler error if you try to extend a package while
|
|
|
|
// reusing its fields names
|
|
|
|
if let Some(repeated) = local_fields.named.iter().find(|&x| {
|
|
|
|
let Some(id_local) = &x.ident else {
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
id_local.to_string() == id.to_string()
|
|
|
|
}) {
|
|
|
|
return ::syn::Error::new(
|
|
|
|
local_span,
|
|
|
|
"pkg: \"".to_owned()
|
|
|
|
+ &ident.to_string()
|
|
|
|
+ "\" field \""
|
|
|
|
+ &repeated.ident.as_ref().unwrap().to_string()
|
|
|
|
+ "\" is already in package "
|
|
|
|
+ &base.ident.to_string()
|
|
|
|
+ "\" or its base",
|
|
|
|
)
|
|
|
|
.to_compile_error()
|
|
|
|
.into();
|
|
|
|
}
|
|
|
|
base_fields_extra.push(f.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut all_fields = Vec::new();
|
|
|
|
all_fields.extend(local_fields.named.iter());
|
|
|
|
all_fields.extend(base_fields_extra.iter());
|
2024-12-03 16:26:53 +00:00
|
|
|
|
2024-11-29 15:59:22 +00:00
|
|
|
quote! {
|
|
|
|
#(#local_attrs)
|
|
|
|
*
|
|
|
|
#[::macro_magic::export_tokens]
|
|
|
|
#[derive(::bok::Package, ::std::fmt::Debug, Clone)]
|
2024-12-03 16:26:53 +00:00
|
|
|
#vis struct #ident #generics #where_clause{
|
2024-11-29 15:59:22 +00:00
|
|
|
#(#all_fields),
|
|
|
|
*
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.into()
|
|
|
|
}
|
|
|
|
|
2024-12-02 11:21:29 +00:00
|
|
|
pub(crate) fn deps_build(
|
|
|
|
attrs: TokenStream,
|
|
|
|
input: TokenStream,
|
|
|
|
) -> TokenStream {
|
2024-12-03 21:40:16 +00:00
|
|
|
let mut packages = parse_macro_input!(attrs as crate::PathList);
|
2024-12-02 11:21:29 +00:00
|
|
|
let local = parse_macro_input!(input as ::syn::ItemStruct);
|
2024-12-03 21:40:16 +00:00
|
|
|
|
|
|
|
// make sure all the packages have generics, and if not add the repo as
|
|
|
|
// generic aka: rewrite
|
|
|
|
// "#[deps_build(my::pkg)]" -->> "#[deps_build(my::pkg<R>)])"
|
|
|
|
let mut rewrite = false;
|
|
|
|
let repo_argument = {
|
|
|
|
let generic_id: ::syn::Path = ::syn::parse_quote!(id<R>);
|
|
|
|
generic_id.segments.last().unwrap().arguments.clone()
|
|
|
|
};
|
|
|
|
for p in packages.0.iter_mut() {
|
|
|
|
let last = p.segments.last_mut().unwrap();
|
|
|
|
if last.arguments.is_empty() {
|
|
|
|
rewrite = true;
|
|
|
|
last.arguments = repo_argument.clone();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if rewrite {
|
|
|
|
let p_list = packages.0.into_iter();
|
|
|
|
return quote! {
|
|
|
|
#[::bok::deps_build(#(#p_list,)*)]
|
|
|
|
#local
|
|
|
|
}
|
|
|
|
.into();
|
|
|
|
}
|
|
|
|
let packages = packages; // remove mut;
|
|
|
|
|
2024-12-02 11:21:29 +00:00
|
|
|
let ::syn::Fields::Named(local_fields) = local.fields else {
|
|
|
|
use ::syn::spanned::Spanned;
|
|
|
|
return ::syn::Error::new(
|
|
|
|
local.fields.span(),
|
|
|
|
"unnamed fields are not supported",
|
|
|
|
)
|
|
|
|
.to_compile_error()
|
|
|
|
.into();
|
|
|
|
};
|
|
|
|
let local_attrs = local.attrs.iter();
|
|
|
|
let local_fields = local_fields.named.iter();
|
2024-12-03 16:26:53 +00:00
|
|
|
let (_, generics, where_clause) = local.generics.split_for_impl();
|
2024-12-02 11:21:29 +00:00
|
|
|
let ident = local.ident;
|
|
|
|
let vis = local.vis;
|
|
|
|
|
|
|
|
let pkg_trait = quote::format_ident!("BokDeps{}", ident);
|
|
|
|
|
2024-12-03 20:30:53 +00:00
|
|
|
use ::convert_case::{Case, Casing};
|
2024-12-02 11:21:29 +00:00
|
|
|
let deps = packages
|
|
|
|
.0
|
|
|
|
.iter()
|
2024-12-03 20:30:53 +00:00
|
|
|
.map(|x| {
|
|
|
|
let ident = x.segments.last().unwrap().ident.clone();
|
|
|
|
quote::format_ident!("{}", ident.to_string().to_case(Case::Snake))
|
|
|
|
})
|
2024-12-02 11:21:29 +00:00
|
|
|
.collect::<Vec<::syn::Ident>>();
|
|
|
|
|
|
|
|
quote! {
|
|
|
|
#(#local_attrs)
|
|
|
|
*
|
2024-12-03 16:26:53 +00:00
|
|
|
#vis struct #ident #generics #where_clause{
|
2024-12-02 11:21:29 +00:00
|
|
|
#(#local_fields),
|
|
|
|
*
|
|
|
|
}
|
|
|
|
#[::macro_magic::export_tokens(#pkg_trait)]
|
2024-12-04 15:38:04 +00:00
|
|
|
pub trait #pkg_trait: ::bok::Repository {
|
2024-12-02 11:21:29 +00:00
|
|
|
#(fn #deps(&self) -> ::std::boxed::Box<dyn ::bok::Pkg>;)
|
|
|
|
*
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.into()
|
|
|
|
}
|
|
|
|
|
2024-11-29 15:59:22 +00:00
|
|
|
// package_impl has 3 stages, and uses macro_magic import for the last two:
|
|
|
|
// * called without parameters => add default ::bok::Pkg functions, then add the
|
|
|
|
// same macro with the package type as argument
|
|
|
|
// * called with the package type as argument => get the base type, then add the
|
|
|
|
// same macro with the base type IMPL as argument
|
|
|
|
// * called with the a package impl as argument, copy the non-implemented
|
|
|
|
// functions from the base
|
|
|
|
pub(crate) fn package_impl(
|
|
|
|
_attrs: TokenStream,
|
|
|
|
input: TokenStream,
|
|
|
|
) -> TokenStream {
|
2024-12-03 21:40:16 +00:00
|
|
|
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
|
|
|
|
//
|
2024-12-04 12:40:44 +00:00
|
|
|
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
|
|
|
|
);
|
2024-12-03 21:40:16 +00:00
|
|
|
let g: ::syn::ItemImpl = ::syn::parse_quote! {
|
2024-12-04 12:40:44 +00:00
|
|
|
impl<R> trait_name for #t_id<R> where R: ::bok::Repository + #trait_deps {}
|
2024-12-03 21:40:16 +00:00
|
|
|
};
|
|
|
|
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
|
2024-11-16 11:52:55 +00:00
|
|
|
|
2024-11-29 15:59:22 +00:00
|
|
|
let name_pkg = match &*ast.self_ty {
|
|
|
|
::syn::Type::Path(tp) => match tp.path.get_ident() {
|
|
|
|
Some(id) => id.clone(),
|
|
|
|
_ => {
|
|
|
|
return ::syn::Error::new(
|
|
|
|
ast.span(),
|
|
|
|
"package_impl: type::Path but no ident",
|
|
|
|
)
|
|
|
|
.to_compile_error()
|
|
|
|
.into()
|
2024-11-16 11:52:55 +00:00
|
|
|
}
|
2024-11-29 15:59:22 +00:00
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
return ::syn::Error::new(
|
|
|
|
ast.span(),
|
|
|
|
"package_impl: not a type::Path",
|
|
|
|
)
|
|
|
|
.to_compile_error()
|
2024-11-16 11:52:55 +00:00
|
|
|
.into()
|
|
|
|
}
|
2024-11-29 15:59:22 +00:00
|
|
|
};
|
|
|
|
let full_path_name = proc_macro2::Ident::new(
|
|
|
|
&("_bok_pkg_".to_owned() + &name_pkg.to_string()),
|
|
|
|
proc_macro2::Span::call_site(),
|
|
|
|
);
|
|
|
|
|
|
|
|
quote! {
|
|
|
|
use ::bok_macro::package_impl_base;
|
|
|
|
#[::bok_macro::package_impl_base(#name_pkg)]
|
|
|
|
#[::macro_magic::export_tokens(#full_path_name)]
|
|
|
|
#ast
|
2024-11-16 11:52:55 +00:00
|
|
|
}
|
2024-11-29 15:59:22 +00:00
|
|
|
.into()
|
2024-11-16 11:52:55 +00:00
|
|
|
}
|
|
|
|
|
2024-11-29 15:59:22 +00:00
|
|
|
// get the actual base type and re-call the macro with the impl symbols
|
|
|
|
pub(crate) fn package_impl_base(
|
|
|
|
attrs: TokenStream,
|
2024-11-16 11:52:55 +00:00
|
|
|
input: TokenStream,
|
|
|
|
) -> TokenStream {
|
2024-11-29 15:59:22 +00:00
|
|
|
// This is a two-phase macro
|
|
|
|
// * The first time this is called the argument will be the same path as the
|
|
|
|
// struct that implements ::bok::Pkg
|
|
|
|
// * The macro will be rewritten with the base package name that is extended
|
|
|
|
// by the package
|
|
|
|
// And now that we have the proper base name we can add the missing
|
|
|
|
// methods from the base
|
|
|
|
|
2024-11-16 11:52:55 +00:00
|
|
|
let mut ast = parse_macro_input!(input as ::syn::ItemImpl);
|
2024-11-29 15:59:22 +00:00
|
|
|
let name_pkg = match &*ast.self_ty {
|
|
|
|
::syn::Type::Path(tp) => match tp.path.get_ident() {
|
|
|
|
Some(id) => id.clone(),
|
|
|
|
_ => panic!("::bok::package_impl expected path ident"),
|
|
|
|
},
|
|
|
|
_ => panic!("::bok::package_impl expected path"),
|
|
|
|
};
|
|
|
|
let Ok(syntax_tree) = syn::parse::<::syn::ItemStruct>(attrs.clone()) else {
|
|
|
|
return package_impl_base_add(attrs, &mut ast);
|
|
|
|
};
|
|
|
|
// we were passed an exported struct. first phase.
|
|
|
|
if syntax_tree.ident.to_string() != name_pkg.to_string() {
|
|
|
|
return ::syn::Error::new(
|
|
|
|
ast.span(),
|
|
|
|
"package_impl: called on \"".to_owned()
|
|
|
|
+ &name_pkg.to_string()
|
|
|
|
+ "\" with wrong initial name"
|
|
|
|
+ &syntax_tree.ident.to_string()
|
|
|
|
+ "\" ",
|
|
|
|
)
|
|
|
|
.to_compile_error()
|
|
|
|
.into();
|
|
|
|
}
|
|
|
|
// go through the struct definition, extract the base type
|
|
|
|
// from the `_bok_base` phantom data marker
|
|
|
|
let base_path = match syntax_tree.fields.iter().find(|&x| {
|
|
|
|
let Some(id) = &x.ident else {
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
return id.to_string() == "_bok_base";
|
|
|
|
}) {
|
|
|
|
Some(field) => match &field.ty {
|
|
|
|
::syn::Type::Path(tp) => {
|
|
|
|
if tp.path.segments.len() != 3
|
|
|
|
|| tp.path.segments[0].ident.to_string() != "std"
|
|
|
|
|| tp.path.segments[1].ident.to_string() != "marker"
|
|
|
|
|| tp.path.segments[2].ident.to_string() != "PhantomData"
|
|
|
|
{
|
|
|
|
return ::syn::Error::new(
|
|
|
|
ast.span(),
|
|
|
|
"package_impl: called on \"".to_owned()
|
|
|
|
+ &name_pkg.to_string()
|
|
|
|
+ "\" base is not PhantomData, "
|
|
|
|
+ "what are you doing?",
|
|
|
|
)
|
|
|
|
.to_compile_error()
|
|
|
|
.into();
|
|
|
|
}
|
|
|
|
let ::syn::PathArguments::AngleBracketed(psa) =
|
|
|
|
&tp.path.segments[2].arguments
|
|
|
|
else {
|
|
|
|
return ::syn::Error::new(
|
|
|
|
ast.span(),
|
|
|
|
"package_impl: called on \"".to_owned()
|
|
|
|
+ &name_pkg.to_string()
|
|
|
|
+ "\" base PhantomData has no arguments "
|
|
|
|
+ "what are you doing?",
|
|
|
|
)
|
|
|
|
.to_compile_error()
|
|
|
|
.into();
|
|
|
|
};
|
|
|
|
if psa.args.len() != 1 {
|
|
|
|
return ::syn::Error::new(
|
|
|
|
ast.span(),
|
|
|
|
"package_impl: called on \"".to_owned()
|
|
|
|
+ &name_pkg.to_string()
|
|
|
|
+ "\" base PhantomData has too many arguments "
|
|
|
|
+ "what are you doing?",
|
|
|
|
)
|
|
|
|
.to_compile_error()
|
|
|
|
.into();
|
|
|
|
}
|
|
|
|
let ::syn::GenericArgument::Type(::syn::Type::Path(
|
|
|
|
base_pkg_path,
|
|
|
|
)) = &psa.args[0]
|
|
|
|
else {
|
|
|
|
return ::syn::Error::new(
|
|
|
|
ast.span(),
|
|
|
|
"package_impl: called on \"".to_owned()
|
|
|
|
+ &name_pkg.to_string()
|
|
|
|
+ "\" base PhantomData has wrong argument type "
|
|
|
|
+ "what are you doing?",
|
|
|
|
)
|
|
|
|
.to_compile_error()
|
|
|
|
.into();
|
|
|
|
};
|
|
|
|
base_pkg_path.path.clone()
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return ::syn::Error::new(
|
|
|
|
ast.span(),
|
|
|
|
"package_impl: called on \"".to_owned()
|
|
|
|
+ &name_pkg.to_string()
|
|
|
|
+ "\" base of unknown type",
|
|
|
|
)
|
|
|
|
.to_compile_error()
|
|
|
|
.into();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
None => {
|
|
|
|
return ::syn::Error::new(
|
|
|
|
ast.span(),
|
|
|
|
"package_impl: called on \"".to_owned()
|
|
|
|
+ &name_pkg.to_string()
|
|
|
|
+ "\" can't find base",
|
|
|
|
)
|
|
|
|
.to_compile_error()
|
|
|
|
.into();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// now, `base_path` is an absolute path.
|
|
|
|
// but it does not mean that it is in our crate
|
|
|
|
// we need to maintain the whole path and change only the last ident,
|
|
|
|
// to the one generated by us instead of by macro_magic
|
|
|
|
|
|
|
|
// now `base_type` is the base struct. but what we actually want is
|
|
|
|
// to import the `impl ::bok::Pkg` for `base_type`
|
|
|
|
let mut exported_impl_name = base_path.clone();
|
|
|
|
let last_segment_n = exported_impl_name.segments.len() - 1;
|
|
|
|
let last_segment = &mut exported_impl_name.segments[last_segment_n];
|
|
|
|
last_segment.ident =
|
|
|
|
::quote::format_ident!("_bok_pkg_{}", last_segment.ident);
|
|
|
|
|
|
|
|
return quote! {
|
|
|
|
#[::bok_macro::package_impl_base(#exported_impl_name)]
|
|
|
|
#ast
|
|
|
|
}
|
|
|
|
.into();
|
|
|
|
}
|
2024-11-16 11:52:55 +00:00
|
|
|
|
2024-11-29 15:59:22 +00:00
|
|
|
// implements ::bok::Pkg:
|
|
|
|
// * add base methods
|
|
|
|
// * copy the non-implemented methods from the base
|
|
|
|
pub(crate) fn package_impl_base_add(
|
|
|
|
attrs: TokenStream,
|
|
|
|
ast: &mut ::syn::ItemImpl,
|
|
|
|
) -> TokenStream {
|
2024-11-16 11:52:55 +00:00
|
|
|
let name_pkg = match &*ast.self_ty {
|
|
|
|
::syn::Type::Path(tp) => match tp.path.get_ident() {
|
|
|
|
Some(id) => id.clone(),
|
2024-11-29 15:59:22 +00:00
|
|
|
_ => panic!("::bok::package_impl expected path ident"),
|
2024-11-16 11:52:55 +00:00
|
|
|
},
|
2024-11-29 15:59:22 +00:00
|
|
|
_ => panic!("::bok::package_impl expected path"),
|
2024-11-16 11:52:55 +00:00
|
|
|
};
|
2024-11-29 15:59:22 +00:00
|
|
|
let base_ast = parse_macro_input!(attrs as ::syn::ItemImpl);
|
2024-11-16 11:52:55 +00:00
|
|
|
let name: ::syn::ImplItem = ::syn::parse_quote! {
|
|
|
|
fn name(&self) -> ::bok::PkgName {
|
|
|
|
(module_path!().to_owned() + "::" + ::std::stringify!(#name_pkg)).as_str().into()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let path: ::syn::ImplItem = ::syn::parse_quote! {
|
|
|
|
fn path(&self) -> ::bok::Path<::bok::PkgName> {
|
|
|
|
(module_path!().to_owned() + "::" + ::std::stringify!(#name_pkg)).as_str().into()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let version: ::syn::ImplItem = ::syn::parse_quote! {
|
|
|
|
fn version(&self) -> ::bok::Version {
|
|
|
|
self.version.clone()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let any: ::syn::ImplItem = ::syn::parse_quote! {
|
|
|
|
fn as_any(&self) -> &dyn ::std::any::Any {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let any_mut: ::syn::ImplItem = ::syn::parse_quote! {
|
|
|
|
fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let hash: ::syn::ImplItem = ::syn::parse_quote! {
|
|
|
|
fn hash(&self) -> ::bok::Hash {
|
|
|
|
use ::sha3::{Digest};
|
|
|
|
let mut hasher = ::sha3::Sha3_256::new();
|
|
|
|
let mut pkg_vars = String::new();
|
|
|
|
use ::std::fmt::Write;
|
|
|
|
write!(&mut pkg_vars,
|
|
|
|
"{:?}",
|
|
|
|
self,
|
|
|
|
).ok();
|
|
|
|
hasher.update(&pkg_vars);
|
|
|
|
hasher.update(self.to_string());
|
|
|
|
let hash = hasher.finalize();
|
|
|
|
::bok::Hash::Sha3(hash)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let hash_code: ::syn::ImplItem = ::syn::parse_quote! {
|
|
|
|
fn hash_code(&self) -> ::bok::Hash {
|
|
|
|
use ::sha3::{Digest};
|
|
|
|
let mut hasher = ::sha3::Sha3_256::new();
|
|
|
|
hasher.update(self.to_string());
|
|
|
|
let hash = hasher.finalize();
|
|
|
|
::bok::Hash::Sha3(hash)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
for mut impl_item in ast.items.iter_mut() {
|
|
|
|
if let ::syn::ImplItem::Fn(ref mut fn_impl) = &mut impl_item {
|
2024-11-29 15:59:22 +00:00
|
|
|
if ["prepare", "configure", "build", "check", "install"]
|
|
|
|
.iter()
|
|
|
|
.find(|&f| *f == fn_impl.sig.ident.to_string().as_str())
|
|
|
|
.is_none()
|
|
|
|
{
|
2024-11-16 11:52:55 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let fn_with_code: ::syn::ImplItemFn = ::syn::parse_quote! {
|
|
|
|
#[::bok::pkg_fn_to_code]
|
|
|
|
#fn_impl
|
|
|
|
};
|
|
|
|
*fn_impl = fn_with_code;
|
|
|
|
} else {
|
|
|
|
panic!("wtf");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-04 12:40:44 +00:00
|
|
|
let trait_deps = ::quote::format_ident!("BokDeps{}", name_pkg);
|
2024-12-03 16:26:53 +00:00
|
|
|
let generic_struct: ::syn::ItemImpl = ::syn::parse_quote! {
|
2024-12-04 12:40:44 +00:00
|
|
|
impl<R> test for #name_pkg <R>
|
2024-12-03 16:26:53 +00:00
|
|
|
where
|
2024-12-04 12:40:44 +00:00
|
|
|
R: ::bok::Repository + 'static + #trait_deps,
|
2024-12-03 16:26:53 +00:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
ast.generics = generic_struct.generics;
|
|
|
|
ast.self_ty = generic_struct.self_ty;
|
|
|
|
|
2024-11-16 11:52:55 +00:00
|
|
|
ast.items.push(name);
|
|
|
|
ast.items.push(path);
|
|
|
|
ast.items.push(version);
|
|
|
|
ast.items.push(any);
|
|
|
|
ast.items.push(any_mut);
|
|
|
|
ast.items.push(hash);
|
|
|
|
ast.items.push(hash_code);
|
|
|
|
|
2024-11-29 15:59:22 +00:00
|
|
|
fn maybe_add_fn(
|
|
|
|
name_pkg: &str,
|
|
|
|
ast: &mut ::syn::ItemImpl,
|
|
|
|
base_ast: &::syn::ItemImpl,
|
|
|
|
name: &str,
|
|
|
|
) -> Result<(), TokenStream> {
|
|
|
|
let Some(base_fn) = base_ast.items.iter().find(|&f| {
|
|
|
|
let ::syn::ImplItem::Fn(func) = f else {
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
return func.sig.ident.to_string() == name;
|
|
|
|
}) else {
|
|
|
|
return Err(::syn::Error::new(
|
|
|
|
ast.span(),
|
|
|
|
"package_impl: called on \"".to_owned()
|
|
|
|
+ name_pkg
|
|
|
|
+ "\" maybe_add_fn can't find "
|
|
|
|
+ name
|
|
|
|
+ " on base",
|
|
|
|
)
|
|
|
|
.to_compile_error()
|
|
|
|
.into());
|
|
|
|
};
|
|
|
|
let ::syn::ImplItem::Fn(mut base_fn_no_code) = base_fn.clone() else {
|
|
|
|
return Err(::syn::Error::new(
|
|
|
|
ast.span(),
|
|
|
|
"package_impl: called on \"".to_owned()
|
|
|
|
+ name_pkg
|
|
|
|
+ "\" what was Fn is not anymore",
|
|
|
|
)
|
|
|
|
.to_compile_error()
|
|
|
|
.into());
|
|
|
|
};
|
|
|
|
base_fn_no_code.attrs.retain_mut(|a| {
|
|
|
|
let ::syn::Meta::Path(mp) = &a.meta else {
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
if mp.segments.len() == 2
|
|
|
|
&& mp.segments[0].ident.to_string() == "bok"
|
|
|
|
&& mp.segments[1].ident.to_string() == "pkg_fn_to_code"
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
if ast
|
|
|
|
.items
|
|
|
|
.iter()
|
|
|
|
.find(|&f| {
|
|
|
|
let ::syn::ImplItem::Fn(func) = f else {
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
return func.sig.ident.to_string() == name;
|
|
|
|
})
|
|
|
|
.is_none()
|
|
|
|
{
|
|
|
|
let new_fn: ::syn::ImplItem = parse_quote!(
|
|
|
|
#[::bok::pkg_fn_to_code]
|
|
|
|
#base_fn_no_code
|
|
|
|
);
|
|
|
|
ast.items.push(new_fn);
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
for name in
|
|
|
|
["prepare", "configure", "build", "check", "install"].into_iter()
|
|
|
|
{
|
|
|
|
let Err(e) = maybe_add_fn(&name_pkg.to_string(), ast, &base_ast, name)
|
|
|
|
else {
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
return e;
|
|
|
|
}
|
2024-11-16 11:52:55 +00:00
|
|
|
quote! {
|
|
|
|
#ast
|
|
|
|
}
|
|
|
|
.into()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn derive_package(input: TokenStream) -> TokenStream {
|
2024-11-29 15:59:22 +00:00
|
|
|
let mut input = parse_macro_input!(input as DeriveInput);
|
2024-11-16 11:52:55 +00:00
|
|
|
|
|
|
|
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();
|
2024-11-29 15:59:22 +00:00
|
|
|
let elements = match &input.data {
|
|
|
|
::syn::Data::Struct(s) => match &s.fields {
|
|
|
|
syn::Fields::Named(n) => n.named.clone(),
|
2024-11-16 11:52:55 +00:00
|
|
|
_ => panic!("only named supported"),
|
|
|
|
},
|
|
|
|
_ => panic!("only struct allowed"),
|
|
|
|
};
|
|
|
|
let all_fields = elements.iter().filter_map(|field| {
|
|
|
|
if let Some(id) = field.ident.clone() {
|
2024-12-03 16:26:53 +00:00
|
|
|
if id.to_string() != "version"
|
|
|
|
&& id.to_string() != "_bok_base"
|
|
|
|
&& id.to_string() != "_bok_repo"
|
|
|
|
{
|
2024-11-16 11:52:55 +00:00
|
|
|
return Some(&field.ident);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
});
|
|
|
|
let all_fields2 = all_fields.clone();
|
|
|
|
let all_fields3 = all_fields.clone();
|
|
|
|
let all_fields_mut = elements.iter().filter_map(|field| {
|
|
|
|
if let Some(id) = field.ident.clone() {
|
2024-12-03 16:26:53 +00:00
|
|
|
if id.to_string() != "version"
|
|
|
|
&& id.to_string() != "_bok_base"
|
|
|
|
&& id.to_string() != "_bok_repo"
|
|
|
|
{
|
2024-11-16 11:52:55 +00:00
|
|
|
return Some(quote::format_ident!("{}_mut", id.to_string()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
});
|
|
|
|
let all_types = elements.iter().filter_map(|field| {
|
|
|
|
if let Some(id) = field.ident.clone() {
|
2024-12-03 16:26:53 +00:00
|
|
|
if id.to_string() != "version"
|
|
|
|
&& id.to_string() != "_bok_base"
|
|
|
|
&& id.to_string() != "_bok_repo"
|
|
|
|
{
|
2024-11-16 11:52:55 +00:00
|
|
|
return Some(&field.ty);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
});
|
|
|
|
let all_types2 = all_types.clone();
|
|
|
|
let non_opt_fields = elements.iter().filter_map(|field| {
|
|
|
|
if let Some(id) = field.ident.clone() {
|
2024-12-03 16:26:53 +00:00
|
|
|
if id.to_string() == "version"
|
|
|
|
|| id.to_string() == "_bok_base"
|
|
|
|
|| id.to_string() == "_bok_repo"
|
|
|
|
{
|
2024-11-16 11:52:55 +00:00
|
|
|
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() {
|
2024-12-03 16:26:53 +00:00
|
|
|
if id.to_string() == "version"
|
|
|
|
|| id.to_string() == "_bok_base"
|
|
|
|
|| id.to_string() == "_bok_repo"
|
|
|
|
{
|
2024-11-16 11:52:55 +00:00
|
|
|
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() {
|
2024-12-03 16:26:53 +00:00
|
|
|
if id.to_string() == "version"
|
|
|
|
|| id.to_string() == "_bok_base"
|
|
|
|
|| id.to_string() == "_bok_repo"
|
|
|
|
{
|
2024-11-16 11:52:55 +00:00
|
|
|
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() {
|
2024-12-03 16:26:53 +00:00
|
|
|
if id.to_string() == "version"
|
|
|
|
|| id.to_string() == "_bok_base"
|
|
|
|
|| id.to_string() == "_bok_repo"
|
|
|
|
{
|
2024-11-16 11:52:55 +00:00
|
|
|
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();
|
|
|
|
|
|
|
|
{
|
2024-11-29 15:59:22 +00:00
|
|
|
let ::syn::Data::Struct(ref mut ds) = input.data else {
|
2024-11-16 11:52:55 +00:00
|
|
|
panic!("::bok::package expected a struct");
|
|
|
|
};
|
2024-11-29 15:59:22 +00:00
|
|
|
let ::syn::Fields::Named(ref mut _old_nf) = ds.fields else {
|
2024-11-16 11:52:55 +00:00
|
|
|
panic!("::bok::package expected a named field");
|
|
|
|
};
|
|
|
|
}
|
|
|
|
let pkg_struct = quote! {
|
2024-11-29 15:59:22 +00:00
|
|
|
#input
|
2024-11-16 11:52:55 +00:00
|
|
|
};
|
|
|
|
let mut pkg_struct_str = String::new();
|
|
|
|
{
|
|
|
|
use ::core::fmt::Write;
|
|
|
|
write!(&mut pkg_struct_str, "{}", pkg_struct)
|
|
|
|
.expect("can't write package struct into string");
|
|
|
|
}
|
|
|
|
|
2024-12-04 12:40:44 +00:00
|
|
|
let trait_deps = ::quote::format_ident!("BokDeps{}", name);
|
|
|
|
|
2024-11-16 11:52:55 +00:00
|
|
|
let expanded = quote! {
|
2024-12-03 16:26:53 +00:00
|
|
|
impl<R> ::core::fmt::Display for #name<R>
|
|
|
|
where
|
2024-12-04 12:40:44 +00:00
|
|
|
R: ::bok::Repository + #trait_deps,
|
2024-12-03 16:26:53 +00:00
|
|
|
{
|
2024-11-16 11:52:55 +00:00
|
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>)
|
|
|
|
->::core::fmt::Result
|
|
|
|
{
|
2024-12-03 16:26:53 +00:00
|
|
|
<#name<R> as ::bok::PkgCode>::fmt(self, f)
|
2024-11-16 11:52:55 +00:00
|
|
|
}
|
|
|
|
}
|
2024-12-03 16:26:53 +00:00
|
|
|
impl<R> ::bok::PkgCode for #name<R>
|
|
|
|
where
|
2024-12-04 12:40:44 +00:00
|
|
|
R: ::bok::Repository + 'static + #trait_deps,
|
2024-12-03 16:26:53 +00:00
|
|
|
{
|
2024-11-16 11:52:55 +00:00
|
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>)
|
|
|
|
->::core::fmt::Result
|
|
|
|
{
|
|
|
|
// FIXME: this sounds convoluted.
|
|
|
|
// first we turn everything into a string, then we
|
|
|
|
// parse the string into tokenstream again to format it
|
|
|
|
// reason: the `prettyplease` works on `::syn::File`
|
|
|
|
// and I can't find an easy translation
|
|
|
|
// `TokenStream2 -> TokenStream`
|
|
|
|
use ::bok::Pkg;
|
|
|
|
|
|
|
|
// don't add a method if it is just the base Trait implementation
|
|
|
|
fn maybe_add_fn(out: &mut String, name: &str, fn_str: &str) -> ::core::fmt::Result {
|
|
|
|
|
2024-11-29 15:59:22 +00:00
|
|
|
//FIXME: check with the code exported from macro_magic base
|
2024-11-16 11:52:55 +00:00
|
|
|
let str_base = "self . base () . unwrap () . ".to_owned() + name + " ()";
|
|
|
|
|
|
|
|
if fn_str.trim() != str_base {
|
|
|
|
use ::core::fmt::Write;
|
|
|
|
|
|
|
|
write!(out,
|
|
|
|
"\
|
|
|
|
fn {}(&self) -> Result<(), ()> {{\n\
|
|
|
|
{}\n\
|
|
|
|
}}\n",
|
|
|
|
name,
|
|
|
|
fn_str,
|
|
|
|
)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
::core::fmt::Result::Ok(())
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
use ::core::fmt::Write;
|
|
|
|
|
|
|
|
let pkg_empty = ::bok::PkgEmpty::default();
|
|
|
|
let mut pkg_string = String::new();
|
2024-11-29 15:59:22 +00:00
|
|
|
// FIXME: filter out the things added from base pkg
|
2024-11-16 11:52:55 +00:00
|
|
|
write!(&mut pkg_string,
|
2024-11-29 15:59:22 +00:00
|
|
|
"#[::bok::package(::bok::PkgEmpty)]\n\
|
2024-11-16 11:52:55 +00:00
|
|
|
{}",
|
|
|
|
#pkg_struct_str)?;
|
|
|
|
write!(&mut pkg_string,
|
2024-11-29 15:59:22 +00:00
|
|
|
"#[::bok::package_impl]\n\
|
2024-11-16 11:52:55 +00:00
|
|
|
impl ::bok::Pkg for {} {{\n",
|
|
|
|
::std::stringify!(#name)
|
|
|
|
)?;
|
|
|
|
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, "install", &self.install_code().to_string())?;
|
|
|
|
write!(&mut pkg_string, "}}\n")?;
|
|
|
|
let re_parsed = ::syn::parse_file(&pkg_string).unwrap();
|
|
|
|
let formatted = prettyplease::unparse(&re_parsed);
|
|
|
|
f.write_str(&formatted)
|
|
|
|
}
|
|
|
|
}
|
2024-12-03 16:26:53 +00:00
|
|
|
impl<R> #name<R>
|
|
|
|
where
|
2024-12-04 12:40:44 +00:00
|
|
|
R: ::bok::Repository + 'static + #trait_deps + ::core::default::Default,
|
2024-12-03 16:26:53 +00:00
|
|
|
{
|
2024-11-16 11:52:55 +00:00
|
|
|
pub fn as_pkg(&self) -> &dyn ::bok::Pkg {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
pub fn as_pkg_mut(&mut self) -> &mut dyn ::bok::Pkg {
|
|
|
|
self
|
|
|
|
}
|
2024-12-03 16:26:53 +00:00
|
|
|
pub fn builder() -> #name_builder<R> {
|
|
|
|
#name_builder::<R>::default()
|
2024-11-16 11:52:55 +00:00
|
|
|
}
|
|
|
|
#(pub fn #all_fields2(&self) -> &#all_types {
|
|
|
|
&self.#all_fields2
|
|
|
|
})*
|
|
|
|
#(pub fn #all_fields_mut(&mut self) -> &#all_types2 {
|
|
|
|
&mut self.#all_fields3
|
|
|
|
})*
|
|
|
|
}
|
|
|
|
#[derive(::std::default::Default, ::std::fmt::Debug)]
|
2024-12-03 16:26:53 +00:00
|
|
|
pub struct #name_builder<R>
|
|
|
|
where
|
2024-12-04 12:40:44 +00:00
|
|
|
R: ::bok::Repository + 'static + #trait_deps,
|
2024-12-03 16:26:53 +00:00
|
|
|
{
|
|
|
|
_bok_repo: ::std::marker::PhantomData<R>,
|
2024-11-16 11:52:55 +00:00
|
|
|
#(#non_opt_fields: ::std::option::Option<#non_opt_types>,)*
|
|
|
|
#(#opt_fields: ::std::option::Option<#opt_types>,)*
|
|
|
|
}
|
2024-12-03 16:26:53 +00:00
|
|
|
impl<R> ::bok::PkgBuilder for #name_builder2<R>
|
|
|
|
where
|
2024-12-04 12:40:44 +00:00
|
|
|
R: ::bok::Repository + 'static + #trait_deps,
|
2024-12-03 16:26:53 +00:00
|
|
|
{
|
2024-11-16 11:52:55 +00:00
|
|
|
fn name(&self) -> ::bok::PkgName {
|
|
|
|
use ::bok::Pkg;
|
2024-12-03 16:26:53 +00:00
|
|
|
#name::<R>::default().name()
|
2024-11-16 11:52:55 +00:00
|
|
|
}
|
|
|
|
fn path(&self) -> ::bok::Path<::bok::PkgName> {
|
|
|
|
use ::bok::Pkg;
|
2024-12-03 16:26:53 +00:00
|
|
|
#name::<R>::default().path()
|
2024-11-16 11:52:55 +00:00
|
|
|
}
|
|
|
|
fn version(&self) -> ::bok::Version {
|
|
|
|
use ::bok::Pkg;
|
2024-12-03 16:26:53 +00:00
|
|
|
#name::<R>::default().version()
|
2024-11-16 11:52:55 +00:00
|
|
|
}
|
|
|
|
fn default_unused(&mut self) -> &mut dyn ::bok::PkgBuilder {
|
2024-12-03 16:26:53 +00:00
|
|
|
let def = #name::<R>::default();
|
2024-11-16 11:52:55 +00:00
|
|
|
#(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(
|
2024-12-03 16:26:53 +00:00
|
|
|
Box::new(#name::<R> {
|
2024-11-29 15:59:22 +00:00
|
|
|
_bok_base: ::std::marker::PhantomData::default(),
|
2024-12-03 16:26:53 +00:00
|
|
|
_bok_repo: ::std::marker::PhantomData::default(),
|
|
|
|
version: #name::<R>::default().version,
|
2024-11-16 11:52:55 +00:00
|
|
|
#(#non_opt_fields3 : self.#non_opt_fields3.clone().unwrap(),)*
|
|
|
|
#(#opt_fields2 : self.#opt_fields2.clone(),)*
|
|
|
|
})
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2024-12-03 16:26:53 +00:00
|
|
|
impl<R> #name_builder3<R>
|
|
|
|
where
|
2024-12-04 12:40:44 +00:00
|
|
|
R: ::bok::Repository +'static + #trait_deps,
|
2024-12-03 16:26:53 +00:00
|
|
|
{
|
2024-11-16 11:52:55 +00:00
|
|
|
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)
|
|
|
|
}
|