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}