waddling_errors_macros/lib.rs
1//! Procedural macros for waddling-errors
2//!
3//! This crate provides convenient macros for defining error code components,
4//! primaries, and sequences with rich metadata support.
5//!
6//! # Macros
7//!
8//! - `component!` - Define component enums with automatic ComponentId impl
9//! - `primary!` - Define primary enums with automatic PrimaryId impl
10//! - `sequence!` - Define sequence constants with metadata
11//!
12//! # Examples
13//!
14//! ```ignore
15//! use waddling_errors_macros::{component, primary, sequence};
16//!
17//! component! {
18//! Auth,
19//! Parser {
20//! value: "PARSER",
21//! docs: "Parsing subsystem",
22//! tags: ["frontend"],
23//! },
24//! }
25//!
26//! primary! {
27//! Token,
28//! Syntax {
29//! docs: "Syntax errors",
30//! },
31//! }
32//!
33//! sequence! {
34//! MISSING(001) {
35//! description: "Item not found",
36//! hints: ["Check if item exists"],
37//! },
38//! }
39//! ```
40
41use proc_macro::TokenStream;
42
43mod component;
44mod diag;
45mod doc_gen_attr;
46mod doc_gen_macro;
47mod in_component;
48mod primary;
49mod sequence;
50
51/// Define component enums with automatic ComponentId trait implementation.
52///
53/// Supports all 6 metadata fields in any order:
54/// - `value` - Custom string (default: identifier as-is)
55/// - `description` - Documentation string
56/// - `introduced` - Version introduced
57/// - `deprecated` - Deprecation notice
58/// - `examples` - Array of example error codes
59/// - `tags` - Array of category tags
60///
61/// # Examples
62///
63/// ```ignore
64/// component! {
65/// Auth, // Simple: "Auth"
66/// Parser { // With metadata
67/// value: "PARSER",
68/// description: "Parsing subsystem",
69/// introduced: "1.0.0",
70/// tags: ["frontend", "compile-time"],
71/// },
72/// }
73///
74/// // Generated code implements ComponentIdDocumented trait:
75/// let desc = Component::Parser.description(); // Trait method
76/// let tags = Component::Parser.tags();
77/// ```
78#[proc_macro]
79pub fn component(input: TokenStream) -> TokenStream {
80 component::expand(input)
81}
82
83/// Define primary enums with automatic PrimaryId trait implementation.
84///
85/// Supports all 6 metadata fields in any order:
86/// - `value` - Custom string (default: identifier as-is)
87/// - `description` - Documentation string
88/// - `introduced` - Version introduced
89/// - `deprecated` - Deprecation notice
90/// - `examples` - Array of example error codes
91/// - `related` - Array of related primary codes
92///
93/// # Examples
94///
95/// ```ignore
96/// primary! {
97/// Token, // Simple: "Token"
98/// Syntax { // With metadata
99/// description: "Syntax errors",
100/// related: ["Parse", "Lex"],
101/// },
102/// }
103///
104/// // Generated code implements PrimaryIdDocumented trait:
105/// let desc = Primary::Syntax.description(); // Trait method
106/// let related = Primary::Syntax.related();
107/// ```
108#[proc_macro]
109pub fn primary(input: TokenStream) -> TokenStream {
110 primary::expand(input)
111}
112
113/// Define sequence constants with rich metadata.
114///
115/// Supports all 5 metadata fields in any order:
116/// - `description` - Human-readable description
117/// - `typical_severity` - Typical severity level
118/// - `hints` - Array of helpful hints
119/// - `related` - Array of related sequences
120/// - `introduced` - Version introduced
121///
122/// # Examples
123///
124/// ```ignore
125/// sequence! {
126/// MISSING(001), // Simple
127/// CUSTOM(042) { // With metadata
128/// description: "Custom logic error",
129/// hints: ["Check business rules", "Verify input"],
130/// related: ["043", "044"],
131/// introduced: "1.2.0",
132/// },
133/// }
134/// ```
135#[proc_macro]
136pub fn sequence(input: TokenStream) -> TokenStream {
137 sequence::expand(input)
138}
139
140/// Define complete diagnostic metadata with full 4-part error codes.
141///
142/// **This macro generates THREE constants**:
143/// 1. `E_COMPONENT_PRIMARY_SEQUENCE` - Runtime metadata (always present)
144/// 2. `E_COMPONENT_PRIMARY_SEQUENCE_DOCS` - Compile-time docs (with `metadata` feature)
145/// 3. `E_COMPONENT_PRIMARY_SEQUENCE_COMPLETE` - Combined (with `metadata` feature)
146///
147/// # 4-Part Code Format
148///
149/// `Severity.Component.Primary.Sequence`
150///
151/// # Visibility Markers
152///
153/// Fields can have visibility markers to control where they're included:
154/// - `'C` - Compile-time only (documentation generation, not in binary)
155/// - `'R` - Runtime only (in production binary, not in docs)
156/// - `'RC` or `'CR` - Both (default for most fields)
157///
158/// **Mnemonic**: `'CR` = Compile + Runtime (both contexts)
159///
160/// # Supported Fields (in any order)
161///
162/// **Core fields (always present):**
163/// - `message` (required) - Message template with `{field}` placeholders
164/// - `fields` (optional) - Array of field names used in message
165/// - `severity` (optional) - Override the severity from the code
166///
167/// **Fields with visibility markers:**
168/// - `description ['C|'R|'RC|'CR]` - Human-readable description
169/// - `hints ['C|'R|'RC|'CR]` - Array of helpful hints
170/// - `role ['R|'RC|'CR]` - Role-based visibility
171/// - `tags ['R]` - Categorization tags (runtime only)
172/// - `related_codes ['R]` - Related error codes (runtime only)
173/// - `code_snippet ['C]` - Code examples (compile-time only)
174/// - `docs_url ['C]` - Documentation URL (compile-time only)
175/// - `introduced ['C]` - Version introduced (compile-time only)
176/// - `deprecated ['C]` - Deprecation notice (compile-time only)
177///
178/// # Examples
179///
180/// **Basic usage (defaults to 'RC - both contexts):**
181/// ```ignore
182/// diag! {
183/// Error.Auth.Token.MISSING: {
184/// message: "Token '{token}' not found",
185/// fields: [token],
186/// description: "API token missing", // Available in both
187/// hints: ["Check your credentials"], // Available in both
188/// },
189/// }
190/// ```
191///
192/// **Advanced usage with visibility markers:**
193/// ```ignore
194/// diag! {
195/// Error.Auth.Token.MISSING: {
196/// message: "Token '{token}' not found",
197/// fields: [token],
198///
199/// // Verbose docs for compile-time
200/// description 'C: "Detailed explanation for documentation...",
201///
202/// // Short message for runtime
203/// description 'R: "Token missing",
204///
205/// // Different hints for different audiences
206/// hints 'C: ["For developers: Check auth flow", "See RFC 6750"],
207/// hints 'R: ["Include Authorization header", "Verify API key"],
208/// hints 'CR: ["Contact support if needed"], // 'RC also works
209///
210/// // Runtime categorization
211/// role 'R: "Public",
212/// tags: ["auth", "security"],
213///
214/// // Compile-time documentation
215/// code_snippet: {
216/// wrong: "fetch(url)",
217/// correct: "fetch(url, { headers: { 'Authorization': 'Bearer <token>' } })",
218/// },
219/// docs_url: "https://docs.example.com/auth",
220/// introduced: "1.0.0",
221/// },
222/// }
223/// ```
224///
225/// # Generated Code
226///
227/// This generates THREE constants:
228///
229/// ```ignore
230/// // Always generated (no feature flags)
231/// pub const E_AUTH_TOKEN_MISSING: DiagnosticRuntime = /* runtime metadata */;
232///
233/// // Only with metadata feature
234/// #[cfg(feature = "metadata")]
235/// pub const E_AUTH_TOKEN_MISSING_DOCS: DiagnosticDocs = /* compile-time docs */;
236///
237/// #[cfg(feature = "metadata")]
238/// pub const E_AUTH_TOKEN_MISSING_COMPLETE: DiagnosticComplete = /* both */;
239/// ```
240///
241/// Frameworks can use the runtime constant for error handling, and the complete
242/// constant for documentation generation.
243#[proc_macro]
244pub fn diag(input: TokenStream) -> TokenStream {
245 diag::expand(input)
246}
247
248/// Attribute macro for automatic documentation generation
249///
250/// Wraps `diag!` and generates registration code for specified formats.
251///
252/// # Examples
253///
254/// ```ignore
255/// #[doc_gen("json", "html")]
256/// diag! {
257/// E.Auth.Token.MISSING: {
258/// message: "Token not found",
259/// // ...
260/// },
261/// }
262/// ```
263///
264/// This generates a `__doc_gen_registry` module with format tracking.
265#[proc_macro_attribute]
266pub fn doc_gen(attr: TokenStream, item: TokenStream) -> TokenStream {
267 doc_gen_attr::expand(attr, item)
268}
269
270/// Generates documentation registration function for a diagnostic.
271///
272/// This macro creates a registration function that can be called to register
273/// a diagnostic with a DocRegistry for documentation generation.
274///
275/// # Example
276/// ```rust,ignore
277/// // Define diagnostic
278/// diag! {
279/// E.Auth.Token.EXPIRED: {
280/// message: "Token expired",
281/// // ...
282/// },
283/// }
284///
285/// // Generate registration code
286/// doc_gen_register! {
287/// E.Auth.Token.EXPIRED => ["json", "html"]
288/// }
289///
290/// // Later, use the generated function:
291/// let mut registry = DocRegistry::new("myapp", "1.0.0");
292/// register_e_auth_token_expired_complete_for_doc_gen(&mut registry);
293/// ```
294#[proc_macro]
295pub fn doc_gen_register(input: TokenStream) -> TokenStream {
296 doc_gen_macro::expand(input)
297}
298
299/// Mark modules or files as belonging to a specific component.
300///
301/// This attribute enables:
302/// - **Discovery**: Find all files in a component via grep or IDE search
303/// - **Navigation**: IDE integration for "jump to component" features
304/// - **Documentation**: Auto-group errors by component in generated docs
305/// - **Organization**: Track scattered components across different folders
306///
307/// # Syntax
308///
309/// ```ignore
310/// #[in_component(ComponentName)]
311/// ```
312///
313/// # Examples
314///
315/// **Marking a folder** (via mod.rs):
316/// ```ignore
317/// // src/auth/mod.rs
318/// #[in_component(Auth)]
319///
320/// mod token;
321/// mod session;
322/// ```
323///
324/// **Marking scattered files**:
325/// ```ignore
326/// // src/api/middleware.rs
327/// #[in_component(Auth)] // Auth logic here too!
328///
329/// use waddling_errors::diag;
330/// // ... Auth-related errors ...
331/// ```
332///
333/// **Finding all Auth components**:
334/// ```bash
335/// grep -r "#\[in_component(Auth)\]" src/
336/// ```
337///
338/// # Metadata Generated
339///
340/// The macro creates a hidden module with:
341/// - Component name constant
342/// - Module path (for IDE integration)
343/// - File path (for tooling)
344/// - Marker trait (for discovery)
345#[proc_macro_attribute]
346pub fn in_component(attr: TokenStream, item: TokenStream) -> TokenStream {
347 in_component::expand(attr, item)
348}