feature_scope/lib.rs
1//! # feature-scope Macros
2//!
3//! Procedural macros for the `feature-scope` library that enables workspace crates to
4//! independently control their required features without cross-package interference.
5//!
6//! ## Overview
7//!
8//! This crate provides the `#[feature_scope]` and `#[feature_scope_default]` attribute macros
9//! that allow you to conditionally compile code based on feature flags defined in your `Cargo.toml`.
10//!
11//! ## Configuration
12//!
13//! This library uses a two-step configuration approach:
14//!
15//! 1. **Declare features** in library crates using `package.metadata.feature-scope-decl`:
16//!
17//! ```toml
18//! # In your library crate's Cargo.toml
19//! [package.metadata.feature-scope-decl]
20//! default = ["a"]
21//! a = []
22//! b = []
23//! c = []
24//! ```
25//!
26//! 2. **Configure feature usage** in consumer crates using `package.metadata.feature-scope`:
27//!
28//! ```toml
29//! # In your binary/consumer crate's Cargo.toml
30//! [[package.metadata.feature-scope]]
31//! package = "your-library-name"
32//! features = ["b"]
33//! default-features = false
34//! ```
35//!
36//! ## Usage
37//!
38//! Use the macros in your library code:
39//!
40//! ```rust
41//! use feature_scope::{feature_scope, feature_scope_default};
42//!
43//! #[feature_scope_default(a)]
44//! pub fn feature_a_function() {
45//! println!("This compiles when feature 'a' is enabled or by default");
46//! }
47//!
48//! #[feature_scope(b)]
49//! pub fn feature_b_function() {
50//! println!("This only compiles when feature 'b' is enabled");
51//! }
52//!
53//! #[feature_scope_default]
54//! pub fn default_function() {
55//! println!("This compiles by default");
56//! }
57//! ```
58//!
59//! ## Build Commands
60//!
61//! Use `cargo feature-scope` commands instead of regular `cargo` commands to build your project:
62//!
63//! ```bash
64//! cargo feature-scope build
65//! cargo feature-scope run
66//! cargo feature-scope test
67//! ```
68
69mod parser;
70
71use proc_macro::TokenStream;
72use quote::quote;
73use syn::parse_macro_input;
74
75#[proc_macro_attribute]
76pub fn feature_scope(attr: TokenStream, input: TokenStream) -> TokenStream {
77 let input = proc_macro2::TokenStream::from(input);
78 let attr = parse_macro_input!(attr as parser::FeatureScope);
79
80 let parser::FeatureScope { ident } = attr;
81 quote! {
82 #[allow(unexpected_cfgs)]
83 #[cfg(#ident)]
84 #input
85 }
86 .into()
87}
88
89#[proc_macro_attribute]
90pub fn feature_scope_default(_attr: TokenStream, input: TokenStream) -> TokenStream {
91 let input = proc_macro2::TokenStream::from(input);
92 let attr = parse_macro_input!(_attr as parser::FeatureScopeDefault);
93
94 if let Some(ident) = attr.ident {
95 quote! {
96 #[allow(unexpected_cfgs)]
97 #[cfg(any(__scope_default, #ident))]
98 #input
99 }
100 .into()
101 } else {
102 quote! {
103 #[allow(unexpected_cfgs)]
104 #[cfg(__scope_default)]
105 #input
106 }
107 .into()
108 }
109}