assert_struct_macros/lib.rs
1//! Procedural macro implementation for assert-struct.
2//!
3//! This crate provides the procedural macro implementation for the `assert-struct` crate.
4//! Users should use the main `assert-struct` crate which re-exports this macro.
5//!
6//! # Architecture Overview
7//!
8//! The macro transformation happens in three phases:
9//!
10//! 1. **Parse** (`parse.rs`): Tokenize the macro input into a Pattern AST
11//! 2. **Expand** (`expand.rs`): Transform patterns into assertion code
12//! 3. **Execute**: Generated code runs the actual assertions
13//!
14//! # Key Design Decisions
15//!
16//! - **Pattern enum**: Unified abstraction for all pattern types (struct, tuple, slice, etc.)
17//! - **Explicit patterns**: Require explicit operators (e.g., `Some(== my_var)` not `Some(my_var)`)
18//! - **Dual-path optimization**: String literal regexes compile at expansion time
19//! - **Native Rust syntax**: Use match expressions for ranges, slices, and enums
20//!
21//! See the main `assert-struct` crate for documentation and examples.
22
23use proc_macro::TokenStream;
24
25mod expand;
26mod parse;
27mod pattern;
28
29use pattern::Pattern;
30
31// Root-level struct that tracks the assertion
32struct AssertStruct {
33 value: syn::Expr,
34 pattern: Pattern,
35}
36
37/// Structural assertion macro for testing complex data structures.
38///
39/// This procedural macro generates efficient runtime assertions that check structural patterns
40/// against actual values, providing detailed error messages when assertions fail. The macro
41/// transforms pattern-based syntax into optimized comparison code at compile time.
42///
43/// See the [crate-level documentation](crate) for comprehensive guides and learning examples.
44/// This documentation serves as a complete specification reference.
45///
46/// # Syntax Specification
47///
48/// ```text
49/// assert_struct!(expression, TypePattern);
50///
51/// TypePattern ::= TypeName '{' FieldPatternList '}'
52/// | '_' '{' FieldPatternList '}' // Wildcard pattern
53/// FieldPatternList ::= (FieldPattern ',')* ('..')?
54/// FieldPattern ::= FieldName ':' Pattern
55/// | FieldName FieldOperation ':' Pattern
56/// FieldOperation ::= ('*')+ | ('.' Identifier '(' ArgumentList? ')')
57/// Pattern ::= Value | ComparisonPattern | RangePattern | RegexPattern
58/// | EnumPattern | TuplePattern | SlicePattern | NestedPattern
59/// ```
60///
61/// # Complete Pattern Reference
62///
63/// ## Basic Value Patterns
64///
65/// | Pattern | Syntax | Description | Constraints |
66/// |---------|--------|-------------|-------------|
67/// | **Exact Value** | `field: value` | Direct equality comparison | Must implement `PartialEq` |
68/// | **String Literal** | `field: "text"` | String comparison (no `.to_string()` needed) | String or &str fields |
69/// | **Explicit Equality** | `field: == value` | Same as exact value but explicit | Must implement `PartialEq` |
70/// | **Inequality** | `field: != value` | Not equal comparison | Must implement `PartialEq` |
71///
72/// ## Comparison Patterns
73///
74/// | Pattern | Syntax | Description | Constraints |
75/// |---------|--------|-------------|-------------|
76/// | **Greater Than** | `field: > value` | Numeric greater than | Must implement `PartialOrd` |
77/// | **Greater Equal** | `field: >= value` | Numeric greater or equal | Must implement `PartialOrd` |
78/// | **Less Than** | `field: < value` | Numeric less than | Must implement `PartialOrd` |
79/// | **Less Equal** | `field: <= value` | Numeric less or equal | Must implement `PartialOrd` |
80///
81/// ## Range Patterns
82///
83/// | Pattern | Syntax | Description | Constraints |
84/// |---------|--------|-------------|-------------|
85/// | **Inclusive Range** | `field: start..=end` | Value in inclusive range | Must implement `PartialOrd` |
86/// | **Exclusive Range** | `field: start..end` | Value in exclusive range | Must implement `PartialOrd` |
87/// | **Range From** | `field: start..` | Value greater or equal to start | Must implement `PartialOrd` |
88/// | **Range To** | `field: ..end` | Value less than end | Must implement `PartialOrd` |
89/// | **Range Full** | `field: ..` | Matches any value | No constraints |
90///
91/// ## String Pattern Matching
92///
93/// | Pattern | Syntax | Description | Constraints |
94/// |---------|--------|-------------|-------------|
95/// | **Regex Literal** | `field: =~ r"pattern"` | Regular expression match | Requires `regex` feature, `String`/`&str` |
96/// | **Like Trait** | `field: =~ expression` | Custom pattern matching | Must implement `Like<T>` |
97///
98/// ## Field Operations
99///
100/// | Operation | Syntax | Description | Constraints |
101/// |-----------|--------|-------------|-------------|
102/// | **Dereference** | `*field: pattern` | Dereference smart pointer | Must implement `Deref` |
103/// | **Multiple Deref** | `**field: pattern` | Multiple dereference | Must implement `Deref` (nested) |
104/// | **Method Call** | `field.method(): pattern` | Call method and match result | Method must exist and return compatible type |
105/// | **Method with Args** | `field.method(args): pattern` | Call method with arguments | Method must exist with compatible signature |
106/// | **Tuple Method** | `(index.method(): pattern, _)` | Method on tuple element | Valid index, method exists |
107///
108/// ## Enum Patterns
109///
110/// | Pattern | Syntax | Description | Constraints |
111/// |---------|--------|-------------|-------------|
112/// | **Option Some** | `field: Some(pattern)` | Match Some variant with inner pattern | `Option<T>` field |
113/// | **Option None** | `field: None` | Match None variant | `Option<T>` field |
114/// | **Result Ok** | `field: Ok(pattern)` | Match Ok variant with inner pattern | `Result<T, E>` field |
115/// | **Result Err** | `field: Err(pattern)` | Match Err variant with inner pattern | `Result<T, E>` field |
116/// | **Unit Variant** | `field: EnumType::Variant` | Match unit enum variant | Enum with unit variant |
117/// | **Tuple Variant** | `field: EnumType::Variant(patterns...)` | Match tuple enum variant | Enum with tuple variant |
118/// | **Struct Variant** | `field: EnumType::Variant { fields... }` | Match struct enum variant | Enum with struct variant |
119///
120/// ## Wildcard Struct Patterns
121///
122/// | Pattern | Syntax | Description | Constraints |
123/// |---------|--------|-------------|-------------|
124/// | **Wildcard Struct** | `value: _ { fields... }` | Match struct without naming type | Must use `..` for partial matching |
125/// | **Nested Wildcard** | `_ { field: _ { ... }, .. }` | Nested anonymous structs | Avoids importing nested types |
126///
127/// ## Collection Patterns
128///
129/// | Pattern | Syntax | Description | Constraints |
130/// |---------|--------|-------------|-------------|
131/// | **Exact Slice** | `field: [pattern, pattern, ...]` | Match exact slice elements | `Vec<T>` or slice |
132/// | **Partial Head** | `field: [pattern, ..]` | Match prefix elements | `Vec<T>` or slice |
133/// | **Partial Tail** | `field: [.., pattern]` | Match suffix elements | `Vec<T>` or slice |
134/// | **Head and Tail** | `field: [pattern, .., pattern]` | Match first and last | `Vec<T>` or slice |
135/// | **Empty Slice** | `field: []` | Match empty collection | `Vec<T>` or slice |
136///
137/// ## Tuple Patterns
138///
139/// | Pattern | Syntax | Description | Constraints |
140/// |---------|--------|-------------|-------------|
141/// | **Exact Tuple** | `field: (pattern, pattern, ...)` | Match all tuple elements | Tuple type |
142/// | **Wildcard Element** | `field: (pattern, _, pattern)` | Ignore specific elements | Tuple type |
143/// | **Indexed Method** | `field: (0.method(): pattern, _)` | Method call on tuple element | Valid index |
144///
145/// # Parameters
146///
147/// - **`expression`**: Any expression that evaluates to a struct instance. The expression is
148/// borrowed, not consumed, so the value remains available after the assertion.
149/// - **`TypeName`**: The struct type name. Must exactly match the runtime type of the expression.
150/// - **`{ fields }`**: Pattern specification for struct fields. Can be partial (with `..`) or exhaustive.
151///
152/// # Runtime Behavior
153///
154/// ## Evaluation Semantics
155///
156/// - **Non-consuming**: The macro borrows the value, leaving it available after the assertion
157/// - **Expression evaluation**: The expression is evaluated exactly once before pattern matching
158/// - **Short-circuit evaluation**: Patterns are evaluated left-to-right, failing fast on first mismatch
159/// - **Field order independence**: Fields can be specified in any order in the pattern
160/// - **Type requirements**: All fields must have types compatible with their patterns
161///
162/// ## Pattern Matching Rules
163///
164/// ### Exhaustive vs Partial Matching
165/// - **Without `..`**: All struct fields must be specified in the pattern (exhaustive)
166/// - **With `..`**: Only specified fields are checked (partial matching)
167/// - **Multiple `..`**: Compilation error - only one rest pattern allowed per struct
168///
169/// ### Field Operation Precedence
170/// Field operations are applied in left-to-right order:
171/// ```text
172/// **field.method().other_method(): pattern
173/// // Equivalent to: ((*(*field)).method()).other_method()
174/// ```
175///
176/// ### String Literal Handling
177/// - String literals (`"text"`) automatically work with `String` and `&str` fields
178/// - No `.to_string()` conversion needed in patterns
179/// - Comparison uses `PartialEq` implementation
180///
181/// # Panics
182///
183/// The macro panics (causing test failure) when:
184///
185/// ## Pattern Mismatches
186/// - **Value mismatch**: Expected value doesn't equal actual value
187/// - **Comparison failure**: Comparison operator condition fails (e.g., `>`, `<`)
188/// - **Range mismatch**: Value outside specified range
189/// - **Enum variant mismatch**: Different enum variant than expected
190/// - **Collection length mismatch**: Slice pattern length differs from actual length
191/// - **None/Some mismatch**: Expected `Some` but got `None`, or vice versa
192/// - **Ok/Err mismatch**: Expected `Ok` but got `Err`, or vice versa
193///
194/// ## Method Call Failures
195/// - **Method panic**: Called method itself panics during execution
196/// - **Argument evaluation panic**: Method arguments panic during evaluation
197///
198/// ## Regex Failures (when `regex` feature enabled)
199/// - **Invalid regex**: Malformed regular expression pattern
200/// - **Regex evaluation panic**: Regex engine encounters error
201///
202/// ## Runtime Type Issues
203/// **Note**: Type mismatches are caught at compile time, not runtime.
204///
205/// # Compilation Errors
206///
207/// ## Field Validation
208/// - **Nonexistent field**: Field doesn't exist on the struct type
209/// - **Missing fields**: Required fields not specified (without `..`)
210/// - **Duplicate fields**: Same field specified multiple times
211/// - **Invalid field operations**: Operations not supported by field type
212///
213/// ## Type Compatibility
214/// - **Type mismatch**: Pattern type incompatible with field type
215/// - **Trait requirements**: Field doesn't implement required traits (`PartialEq`, `PartialOrd`, etc.)
216/// - **Method signatures**: Method doesn't exist or has incompatible signature
217/// - **Deref constraints**: Field type doesn't implement `Deref` for dereference operations
218///
219/// ## Syntax Validation
220/// - **Invalid syntax**: Malformed pattern syntax
221/// - **Invalid operators**: Unsupported operator for field type
222/// - **Invalid ranges**: Malformed range expressions
223/// - **Invalid regex syntax**: Invalid regex literal (when using raw strings)
224/// - **Multiple rest patterns**: More than one `..` in same struct pattern
225///
226/// ## Feature Requirements
227/// - **Missing regex feature**: Using `=~ r"pattern"` without `regex` feature enabled
228/// - **Like trait not implemented**: Using `=~ expr` where `Like` trait not implemented
229///
230/// # Edge Cases and Limitations
231///
232/// ## Method Call Constraints
233/// - **Return type compatibility**: Method return type must be compatible with pattern type
234/// - **Argument evaluation**: Method arguments are evaluated before the method call
235/// - **No generic method inference**: Generic methods may require explicit type annotations
236/// - **Tuple indexing bounds**: Tuple method calls require valid index at compile time
237///
238/// ## Collection Pattern Limitations
239/// - **Fixed length patterns**: Slice patterns without `..` require exact length match
240/// - **Nested pattern complexity**: Deeply nested slice patterns may impact compile time
241/// - **Memory usage**: Large literal slice patterns increase binary size
242///
243/// ## Smart Pointer Behavior
244/// - **Multiple deref levels**: Each `*` adds one deref level, must match pointer nesting
245/// - **Deref coercion**: Standard Rust deref coercion rules apply
246/// - **Ownership semantics**: Dereferencing borrows the pointed-to value
247///
248/// ## Performance Considerations
249/// - **Compile time**: Complex nested patterns increase compilation time
250/// - **Runtime overhead**: Pattern matching is zero-cost for simple patterns
251/// - **Error message generation**: Error formatting only occurs on failure
252///
253/// # Feature Dependencies
254///
255/// ## Regex Feature (`regex`)
256/// - **Default**: Enabled by default
257/// - **Required for**: `=~ r"pattern"` syntax with string literals
258/// - **Disable with**: `default-features = false` in Cargo.toml
259/// - **Alternative**: Use `Like` trait with pre-compiled regex or custom patterns
260///
261/// ## Like Trait Extension
262/// - **No feature required**: Always available
263/// - **Custom implementations**: Implement `Like<T>` for custom pattern matching
264/// - **Regex integration**: Built-in implementations for regex when feature enabled
265///
266/// # Error Message Format
267///
268/// When assertions fail, the macro generates structured error messages with:
269///
270/// ## Error Components
271/// - **Error type**: Specific failure category (value mismatch, comparison failure, etc.)
272/// - **Field path**: Complete path to the failing field (e.g., `response.user.profile.age`)
273/// - **Source location**: File name and line number of the assertion
274/// - **Actual value**: The value that was found
275/// - **Expected pattern**: The pattern that was expected to match
276/// - **Pattern context**: Visual representation showing where the failure occurred
277///
278/// ## Error Types
279/// - **value mismatch**: Direct equality comparison failed
280/// - **comparison mismatch**: Comparison operator condition failed (`>`, `<`, etc.)
281/// - **range mismatch**: Value outside specified range
282/// - **regex mismatch**: Regex pattern didn't match
283/// - **enum variant mismatch**: Wrong enum variant
284/// - **slice mismatch**: Collection length or element pattern failure
285/// - **method call error**: Method call or result pattern failure
286///
287/// ## Pattern Context Display
288/// Complex patterns show visual context with failure highlighting:
289/// ```text
290/// assert_struct! failed:
291///
292/// | Response { user: User { profile: Profile {
293/// comparison mismatch:
294/// --> `response.user.profile.age` (tests/api.rs:45)
295/// | age: > 18,
296/// | ^^^^^ actual: 17
297/// | } } }
298/// ```
299///
300/// ## Method Call Errors
301/// Method calls in field paths are clearly indicated:
302/// ```text
303/// comparison mismatch:
304/// --> `data.items.len()` (tests/collections.rs:23)
305/// actual: 3
306/// expected: > 5
307/// ```
308///
309/// # Quick Reference Examples
310///
311/// ```rust
312/// # use assert_struct::assert_struct;
313/// # #[derive(Debug)]
314/// # struct Example { value: i32, name: String, items: Vec<i32> }
315/// # let example = Example { value: 42, name: "test".to_string(), items: vec![1, 2] };
316/// // Basic pattern matching
317/// assert_struct!(example, Example {
318/// value: 42, // Exact equality
319/// name: != "other", // Inequality
320/// items.len(): >= 2, // Method call with comparison
321/// .. // Partial matching
322/// });
323/// ```
324///
325/// # See Also
326///
327/// - **Learning Guide**: See the [crate-level documentation](crate) for comprehensive examples
328/// - **Real-World Examples**: Check the `examples/` directory for practical usage patterns
329/// - **Like Trait**: Implement custom pattern matching with the `Like` trait
330#[proc_macro]
331pub fn assert_struct(input: TokenStream) -> TokenStream {
332 // Parse the input
333 let assert = match syn::parse(input) {
334 Ok(assert) => assert,
335 Err(err) => return TokenStream::from(err.to_compile_error()),
336 };
337
338 // Expand to output code
339 let expanded = expand::expand(&assert);
340
341 TokenStream::from(expanded)
342}