diff --git a/bok-macro/src/lib.rs b/bok-macro/src/lib.rs index 06b6f71..b665e2c 100644 --- a/bok-macro/src/lib.rs +++ b/bok-macro/src/lib.rs @@ -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 diff --git a/bok-macro/src/repos.rs b/bok-macro/src/repos.rs index ff8eb80..c99a8d5 100644 --- a/bok-macro/src/repos.rs +++ b/bok-macro/src/repos.rs @@ -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 { + 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), * } } diff --git a/bok-utils/src/pkgs/mod.rs b/bok-utils/src/pkgs/mod.rs index f98efda..7785f3d 100644 --- a/bok-utils/src/pkgs/mod.rs +++ b/bok-utils/src/pkgs/mod.rs @@ -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 {} diff --git a/bok/src/lib.rs b/bok/src/lib.rs index f0f84e7..953cbed 100644 --- a/bok/src/lib.rs +++ b/bok/src/lib.rs @@ -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, } + impl Repository for RepositoryEmpty { fn name(&self) -> RepoName { "RepositoryEmpty".into()