bok/bok-macro/src/collection.rs

121 lines
3.5 KiB
Rust
Raw Normal View History

/*
* 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)])
*
#[derive(::core::fmt::Debug)]
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 {
fn as_any(&self) -> &dyn ::std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any {
self
}
}
}
.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()
}