//! Utility macros for utilizing cargo features. #![deny(clippy::all)] #![deny(clippy::pedantic)] #![deny(missing_docs)] use proc_macro::TokenStream; use quote::quote; use syn::{parse2, Item, LitStr}; /// Specifies that a item is only available with a specific feature enabled. This will also /// document that item is only available with the specified feature enabled by using /// the [`doc_cfg`] feature. /// /// All this macro does is to add the following attributes: /// ```text /// #[cfg(feature = {FEATURE})] /// #[cfg_attr(doc_cfg, doc(cfg(feature = {FEATURE})))] /// ``` /// /// With `{FEATURE}` being replaced with the feature argument. /// /// # Arguments /// - The feature. For example: `"parsing"` /// /// # Panics /// If not attributed to a item or if arguments is missing. /// /// # Examples /// ``` /// # use feature_macros::feature_specific; /// # /// #[feature_specific("cool-things")] /// pub fn do_some_cool_thing() { /// // ... /// } /// ``` /// /// [`doc_cfg`]: https://doc.rust-lang.org/unstable-book/language-features/doc-cfg.html #[proc_macro_attribute] pub fn feature_specific(args_stream: TokenStream, item_stream: TokenStream) -> TokenStream { create_feature_specific(args_stream.into(), item_stream.into()) .unwrap() .into() } fn create_feature_specific( args_stream: proc_macro2::TokenStream, item_stream: proc_macro2::TokenStream, ) -> Result> { let item = parse2::(item_stream)?; let feature = parse2::(args_stream).unwrap().value(); Ok(quote! { #[cfg(feature = #feature)] #[cfg_attr(doc_cfg, doc(cfg(feature = #feature)))] #item }) } #[cfg(test)] mod tests { use std::error::Error; use super::*; #[test] fn create_feature_specific_works() -> Result<(), Box> { assert_eq!( create_feature_specific( quote! {"extra-stuff"}, quote! { pub mod extra; }, )? .to_string(), quote! { #[cfg(feature = "extra-stuff")] #[cfg_attr(doc_cfg, doc(cfg(feature = "extra-stuff")))] pub mod extra; } .to_string() ); Ok(()) } }