From 3383faeaf8342cf4637b6d9a9dfba30b1684edca Mon Sep 17 00:00:00 2001 From: HampusM Date: Sun, 31 Jul 2022 13:26:41 +0200 Subject: feat: add hide impl of Injectable from documentation This will make it so that by default the impl of Injectable is hidden from user code documentation. This commit also includes a flag for the injectable macro to disable the aforementioned feature --- macros/src/injectable_impl.rs | 11 ++++- macros/src/injectable_macro_args.rs | 85 ++++++++++++++++++++++++++++++++++--- macros/src/lib.rs | 21 ++++++--- macros/src/util/iterator_ext.rs | 28 ++++++++++++ macros/src/util/mod.rs | 1 + 5 files changed, 134 insertions(+), 12 deletions(-) create mode 100644 macros/src/util/iterator_ext.rs create mode 100644 macros/src/util/mod.rs (limited to 'macros/src') diff --git a/macros/src/injectable_impl.rs b/macros/src/injectable_impl.rs index f510407..227a8c6 100644 --- a/macros/src/injectable_impl.rs +++ b/macros/src/injectable_impl.rs @@ -38,7 +38,7 @@ impl Parse for InjectableImpl impl InjectableImpl { - pub fn expand(&self) -> proc_macro2::TokenStream + pub fn expand(&self, no_doc_hidden: bool) -> proc_macro2::TokenStream { let Self { dependency_types, @@ -51,9 +51,18 @@ impl InjectableImpl let get_dependencies = Self::_create_get_dependencies(dependency_types); + let maybe_doc_hidden = if no_doc_hidden { + quote! {} + } else { + quote! { + #[doc(hidden)] + } + }; + quote! { #original_impl + #maybe_doc_hidden impl #generics syrette::interfaces::injectable::Injectable for #self_type { fn resolve( #di_container_var: &syrette::DIContainer diff --git a/macros/src/injectable_macro_args.rs b/macros/src/injectable_macro_args.rs index 4ef4389..43f8e11 100644 --- a/macros/src/injectable_macro_args.rs +++ b/macros/src/injectable_macro_args.rs @@ -1,17 +1,92 @@ use syn::parse::{Parse, ParseStream}; -use syn::TypePath; +use syn::punctuated::Punctuated; +use syn::{braced, Ident, LitBool, Token, TypePath}; + +use crate::util::iterator_ext::IteratorExt; + +pub const INJECTABLE_MACRO_FLAGS: &[&str] = &["no_doc_hidden"]; + +pub struct InjectableMacroFlag +{ + pub flag: Ident, + pub is_on: LitBool, +} + +impl Parse for InjectableMacroFlag +{ + fn parse(input: ParseStream) -> syn::Result + { + let input_forked = input.fork(); + + let flag: Ident = input_forked.parse()?; + + let flag_str = flag.to_string(); + + if !INJECTABLE_MACRO_FLAGS.contains(&flag_str.as_str()) { + return Err(input.error(format!( + "Unknown flag '{}'. Expected one of [ {} ]", + flag_str, + INJECTABLE_MACRO_FLAGS.join(",") + ))); + } + + input.parse::()?; + + input.parse::()?; + + let is_on: LitBool = input.parse()?; + + Ok(Self { flag, is_on }) + } +} pub struct InjectableMacroArgs { - pub interface: TypePath, + pub interface: Option, + pub flags: Punctuated, } impl Parse for InjectableMacroArgs { fn parse(input: ParseStream) -> syn::Result { - Ok(Self { - interface: input.parse()?, - }) + let interface = input.parse::().ok(); + + if interface.is_some() { + let comma_input_lookahead = input.lookahead1(); + + if !comma_input_lookahead.peek(Token![,]) { + return Ok(Self { + interface, + flags: Punctuated::new(), + }); + } + + input.parse::()?; + } + + if input.is_empty() { + return Ok(Self { + interface, + flags: Punctuated::new(), + }); + } + + let braced_content; + + braced!(braced_content in input); + + let flags = braced_content.parse_terminated(InjectableMacroFlag::parse)?; + + let flag_names = flags + .iter() + .map(|flag| flag.flag.to_string()) + .collect::>(); + + if let Some(dupe_flag_name) = flag_names.iter().find_duplicate() { + return Err(input.error(format!("Duplicate flag '{}'", dupe_flag_name))); + } + + Ok(Self { interface, flags }) } } diff --git a/macros/src/lib.rs b/macros/src/lib.rs index aca4007..7dba7d1 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -14,16 +14,21 @@ mod factory_type_alias; mod injectable_impl; mod injectable_macro_args; mod libs; +mod util; use declare_interface_args::DeclareInterfaceArgs; use injectable_impl::InjectableImpl; use injectable_macro_args::InjectableMacroArgs; use libs::intertrait_macros::gen_caster::generate_caster; -/// Makes a struct injectable. Thereby usable with `DIContainer`. +/// Makes a struct injectable. Thereby usable with [`DIContainer`]. /// /// # Arguments /// * (Optional) A interface trait the struct implements. +/// * (Zero or more) Flags wrapped in curly braces. Like `{ a = true, b = false }` +/// +/// # Flags +/// - `no_doc_hidden` - Don't hide the impl of the [`Injectable`] trait from documentation. /// /// # Panics /// If the attributed item is not a impl. @@ -53,15 +58,19 @@ use libs::intertrait_macros::gen_caster::generate_caster; #[proc_macro_attribute] pub fn injectable(args_stream: TokenStream, impl_stream: TokenStream) -> TokenStream { - let should_declare_interface = !args_stream.is_empty(); + let InjectableMacroArgs { interface, flags } = parse_macro_input!(args_stream); - let injectable_impl: InjectableImpl = parse(impl_stream).unwrap(); + let mut flags_iter = flags.iter(); - let expanded_injectable_impl = injectable_impl.expand(); + let no_doc_hidden = flags_iter + .find(|flag| flag.flag.to_string().as_str() == "no_doc_hidden") + .map_or(false, |flag| flag.is_on.value); + + let injectable_impl: InjectableImpl = parse(impl_stream).unwrap(); - let maybe_decl_interface = if should_declare_interface { - let InjectableMacroArgs { interface } = parse_macro_input!(args_stream); + let expanded_injectable_impl = injectable_impl.expand(no_doc_hidden); + let maybe_decl_interface = if interface.is_some() { let self_type = &injectable_impl.self_type; quote! { diff --git a/macros/src/util/iterator_ext.rs b/macros/src/util/iterator_ext.rs new file mode 100644 index 0000000..86db6cb --- /dev/null +++ b/macros/src/util/iterator_ext.rs @@ -0,0 +1,28 @@ +use std::collections::HashMap; +use std::hash::Hash; + +pub trait IteratorExt +{ + fn find_duplicate(&mut self) -> Option; +} + +impl IteratorExt for Iter +where + Iter: Iterator, + Iter::Item: Eq + Hash + Copy, +{ + fn find_duplicate(&mut self) -> Option + { + let mut iterated_item_map = HashMap::::new(); + + for item in self { + if iterated_item_map.contains_key(&item) { + return Some(item); + } + + iterated_item_map.insert(item, ()); + } + + None + } +} diff --git a/macros/src/util/mod.rs b/macros/src/util/mod.rs new file mode 100644 index 0000000..fe2fbbc --- /dev/null +++ b/macros/src/util/mod.rs @@ -0,0 +1 @@ +pub mod iterator_ext; -- cgit v1.2.3-18-g5258