repo fields multiple extend

Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
Luca Fulchir 2024-11-16 23:28:54 +01:00
parent 5d3fde9481
commit 9a83bf0dfa
Signed by: luca.fulchir
GPG Key ID: 8F6440603D13A78E
4 changed files with 171 additions and 21 deletions

View File

@ -20,12 +20,17 @@ use ::proc_macro::TokenStream;
mod pkgs;
mod repos;
//
// ####### Repository stuff ##########
//
/// Use as #[::bok::repository(MyBaseRepo)]
/// Will setup a `base` field that is the given base repo
/// and add `#[derive(::bok::Repository)]`
///
/// e.g.: `#[::bok::repository(::bok::RepositoryEmpty)]`
#[::macro_magic::import_tokens_attr]
//#[with_custom_parsing(repos::BaseSet)]
#[proc_macro_attribute]
pub fn repository(attrs: TokenStream, input: TokenStream) -> TokenStream {
crate::repos::repository(attrs, input)
@ -47,6 +52,25 @@ pub fn derive_repository(input: TokenStream) -> TokenStream {
crate::repos::derive_repository(input)
}
/// Add multiple packages to a repo
/// Usage:
/// ```
/// #[::bok::impl_repo(
/// Mypkg1,
/// Mypkg2,
/// )]
/// impl MyRepo{}
/// ```
#[::macro_magic::import_tokens_attr]
#[proc_macro_attribute]
pub fn impl_repository(attrs: TokenStream, input: TokenStream) -> TokenStream {
crate::repos::impl_repo(attrs, input)
}
//
// ####### Package stuff ##########
//
/// Use on a function as `#[::bok::pkg_fn_to_code]`
/// will create a new function, same name with `_code` that
/// returns the ::proc_macro2::TokenStream of the function

View File

@ -17,15 +17,80 @@
use ::proc_macro::TokenStream;
use ::quote::quote;
use ::syn::{parse_macro_input, DeriveInput, Fields, ItemStruct};
use ::syn::{
parse_macro_input, DeriveInput, Fields, Item, ItemImpl, ItemStruct,
};
pub(crate) fn impl_repo(attrs: TokenStream, input: TokenStream) -> TokenStream {
let base = parse_macro_input!(attrs as ItemImpl);
let local = parse_macro_input!(input as ItemImpl);
quote! {}.into()
}
/*
// Either a series of paths, or a single one that we have to expand
pub(crate) enum BaseSet {
Paths(::std::vec::Vec<::syn::Path>),
Single((::syn::Path, ItemStruct)),
}
impl ::syn::parse::Parse for BaseSet {
fn parse(input: ::syn::parse::ParseStream) -> ::syn::parse::Result<Self> {
if let Ok(path) = input.parse() {
if let Ok(item_struct) = ItemStruct::parse(input) {
return Ok(BaseSet::Single((path, item_struct)));
}
}
use ::syn::punctuated::Punctuated;
let vars =
Punctuated::<::syn::Path, ::syn::Token![,]>::parse_separated_nonempty(
input,
)?;
if vars.len() >= 0 {
return Err(::syn::parse::Error::new(input.span(), "GOT TWO"));
}
Ok(BaseSet::Paths(vars.into_iter().collect()))
}
}
impl quote::ToTokens for BaseSet {
fn to_tokens(&self, tokens: &mut ::proc_macro2::TokenStream) {
match self {
BaseSet::Paths(bases) => {
for (i, base) in bases.iter().enumerate() {
if i > 0 {
tokens.extend(
::proc_macro2::Punct::new(
',',
::proc_macro2::Spacing::Joint,
)
.into_token_stream(),
);
tokens.extend(base.to_token_stream());
}
base.to_tokens(tokens);
}
}
BaseSet::Single((_, item_struct)) => item_struct.to_tokens(tokens),
}
}
}
impl ::macro_magic::mm_core::ForeignPath for BaseSet {
fn foreign_path(&self) -> &syn::Path {
match self {
BaseSet::Paths(bases) => &bases[0],
BaseSet::Single((path, _)) => &path,
}
}
}
*/
pub(crate) fn repository(
attrs: TokenStream,
input: TokenStream,
) -> TokenStream {
let base = parse_macro_input!(attrs as ItemStruct);
let local = parse_macro_input!(input as ItemStruct);
let Fields::Named(local_fields) = local.fields else {
use ::syn::spanned::Spanned;
return ::syn::Error::new(
@ -36,7 +101,26 @@ pub(crate) fn repository(
.into();
};
let Fields::Named(base_fields) = base.fields else {
let local_attrs = local.attrs.iter().filter(|&x| {
// don't export the same thing multiple times
if let ::syn::Meta::Path(p) = &x.meta {
if p.segments.len() == 2
&& p.segments[0].ident == "macro_magic"
&& p.segments[1].ident == "export_tokens"
{
return false;
}
return true;
};
true
});
let generics = local.generics;
let ident = local.ident;
let vis = local.vis;
let base = parse_macro_input!(attrs as ItemStruct);
let mut base_fields_extra = Vec::new();
let Fields::Named(base_fields) = &base.fields else {
use ::syn::spanned::Spanned;
return ::syn::Error::new(
base.fields.span(),
@ -45,22 +129,55 @@ pub(crate) fn repository(
.to_compile_error()
.into();
};
let local_fields_it = local_fields.named.iter();
let base_fields_extra = base_fields.named.iter();
let attrs = local.attrs;
let generics = local.generics;
let ident = local.ident;
let vis = local.vis;
// only add the base repo once
let mut base_empty_found = local_fields
.named
.iter()
.find(|&x| {
//
let Some(id) = &x.ident else {
return false;
};
id.to_string() == "bok_repo_empty_marker"
})
.is_some();
for f in base_fields.named.iter() {
let Some(id) = &f.ident else { continue };
if id.to_string() == "bok_repo_empty_marker" {
if base_empty_found {
continue;
} else {
base_empty_found = true;
base_fields_extra.push(f);
continue;
}
}
if local_fields
.named
.iter()
.find(|&x| {
let Some(id_local) = &x.ident else {
return false;
};
id_local.to_string() == id.to_string()
})
.is_some()
{
continue;
}
base_fields_extra.push(f);
}
let mut all_fields = Vec::new();
all_fields.extend(base_fields_extra.iter());
all_fields.extend(local_fields.named.iter());
quote! {
#(#attrs)
#(#local_attrs)
*
#[::macro_magic::export_tokens]
#[derive(::bok::Repository, Debug)]
#vis struct #ident<#generics> {
#(#base_fields_extra),
*
,
#(#local_fields_it),
#(#all_fields),
*
}
}

View File

@ -31,7 +31,9 @@ use ::bok::repository;
/// Base repository with some packages
#[::bok::repository(::bok::RepositoryEmpty)]
#[derive(::std::default::Default)]
pub struct Pkgs1 {}
pub struct Pkgs1 {
r1: i32,
}
// Add some packages to the repository
// all packages will have `::default()` values
@ -46,11 +48,13 @@ bok::repo_packages! {
/// This repository extends and changes Pkgs1
#[::bok::repository(Pkgs1)]
#[derive(::std::default::Default)]
pub struct Pkgs2 {}
pub struct Pkgs2 {
r2: i32,
}
// add a third package with `::default()` values
bok::repo_packages! {
Pkgs1 {
Pkgs2 {
Three,
}
}
@ -68,3 +72,8 @@ impl Pkgs2 {
two.clone()
}
}
/// This repository extends both Pkgs1 and Pkgs2
#[::bok::repository(Pkgs1)]
#[::bok::repository(Pkgs2)]
#[derive(::std::default::Default)]
pub struct Pkgs3 {}

View File

@ -32,8 +32,6 @@ pub use ::bok_macro::{
};
pub use ::semver::{BuildMetadata, Prerelease, Version};
//use ::std::any::Any;
// Package stuff
/// Get a package from its module and export it in the current module
@ -98,8 +96,10 @@ pub trait Repository: ::core::fmt::Debug {
#[::macro_magic::export_tokens]
#[derive(::std::default::Default, Debug)]
pub struct RepositoryEmpty {
test: i32,
// export_tokens needs something to export
bok_repo_empty_marker: ::std::marker::PhantomData<Self>,
}
impl Repository for RepositoryEmpty {
fn name(&self) -> RepoName {
"RepositoryEmpty".into()