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//! - **Disambiguation**: `check_for_special_syntax` solves `Some(> 30)` vs `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;
24use std::fmt;
25use syn::{Expr, Token, punctuated::Punctuated};
26
27mod expand;
28mod parse;
29
30// Root-level struct that tracks the assertion
31struct AssertStruct {
32 value: Expr,
33 pattern: Pattern,
34}
35
36// Unified pattern type that can represent any pattern
37#[derive(Debug, Clone)]
38pub(crate) enum Pattern {
39 // Simple value: 42, \"hello\", true
40 Simple {
41 node_id: usize,
42 expr: Expr,
43 },
44 // Struct pattern: User { name: \"Alice\", age: 30, .. }
45 // When path is None, it's a wildcard pattern: _ { name: \"Alice\", .. }
46 Struct {
47 node_id: usize,
48 path: Option<syn::Path>, // None for wildcard patterns
49 fields: Punctuated<FieldAssertion, Token![,]>,
50 rest: bool,
51 },
52 // Tuple pattern: (10, 20) or Some(42) or None
53 // Now supports mixed positional and indexed elements
54 Tuple {
55 node_id: usize,
56 path: Option<syn::Path>,
57 elements: Vec<TupleElement>,
58 },
59 // Slice pattern: [1, 2, 3] or [1, .., 5]
60 Slice {
61 node_id: usize,
62 elements: Vec<Pattern>,
63 },
64 // Comparison: > 30, <= 100
65 Comparison {
66 node_id: usize,
67 op: ComparisonOp,
68 expr: Expr,
69 },
70 // Range: 10..20, 0..=100
71 Range {
72 node_id: usize,
73 expr: Expr,
74 },
75 // Regex: =~ "pattern" - string literal optimized at compile time
76 #[cfg(feature = "regex")]
77 Regex {
78 node_id: usize,
79 pattern: String, // String literal regex pattern (performance optimization)
80 span: proc_macro2::Span, // Store span for accurate error reporting
81 },
82 // Like pattern: =~ expr - arbitrary expression using Like trait
83 #[cfg(feature = "regex")]
84 Like {
85 node_id: usize,
86 expr: Expr,
87 },
88 // Rest pattern: .. for partial matching
89 Rest {
90 node_id: usize,
91 },
92 // Wildcard pattern: _ for ignoring a value while asserting it exists
93 Wildcard {
94 node_id: usize,
95 },
96 // Closure pattern: |x| expr for custom validation (escape hatch)
97 Closure {
98 node_id: usize,
99 closure: syn::ExprClosure,
100 },
101 // Map pattern: #{ "key": pattern, .. } for map-like structures
102 Map {
103 node_id: usize,
104 entries: Vec<(syn::Expr, Pattern)>, // key-value pairs
105 rest: bool, // partial matching with ..
106 },
107}
108
109// Helper function to format syn expressions as strings
110fn expr_to_string(expr: &Expr) -> String {
111 // This is a simplified version - in production we'd want more complete handling
112 match expr {
113 Expr::Lit(lit) => {
114 // Handle literals
115 quote::quote! { #lit }.to_string()
116 }
117 Expr::Path(path) => {
118 // Handle paths
119 quote::quote! { #path }.to_string()
120 }
121 Expr::Range(range) => {
122 // Handle ranges
123 quote::quote! { #range }.to_string()
124 }
125 _ => {
126 // Fallback - use quote for other expressions
127 quote::quote! { #expr }.to_string()
128 }
129 }
130}
131
132fn path_to_string(path: &syn::Path) -> String {
133 quote::quote! { #path }.to_string()
134}
135
136impl fmt::Display for Pattern {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 match self {
139 Pattern::Simple { expr, .. } => {
140 write!(f, "{}", expr_to_string(expr))
141 }
142 Pattern::Struct {
143 path, fields, rest, ..
144 } => {
145 if let Some(p) = path {
146 write!(f, "{} {{ ", path_to_string(p))?;
147 } else {
148 write!(f, "_ {{ ")?;
149 }
150 for (i, field) in fields.iter().enumerate() {
151 if i > 0 {
152 write!(f, ", ")?;
153 }
154 write!(f, "{}: {}", field.field_name, field.pattern)?;
155 }
156 if *rest {
157 if !fields.is_empty() {
158 write!(f, ", ")?;
159 }
160 write!(f, "..")?;
161 }
162 write!(f, " }}")
163 }
164 Pattern::Tuple { path, elements, .. } => {
165 if let Some(p) = path {
166 write!(f, "{}", path_to_string(p))?;
167 }
168 write!(f, "(")?;
169 for (i, elem) in elements.iter().enumerate() {
170 if i > 0 {
171 write!(f, ", ")?;
172 }
173 write!(f, "{}", elem)?;
174 }
175 write!(f, ")")
176 }
177 Pattern::Slice { elements, .. } => {
178 write!(f, "[")?;
179 for (i, elem) in elements.iter().enumerate() {
180 if i > 0 {
181 write!(f, ", ")?;
182 }
183 write!(f, "{}", elem)?;
184 }
185 write!(f, "]")
186 }
187 Pattern::Comparison { op, expr, .. } => {
188 write!(f, "{} {}", op, expr_to_string(expr))
189 }
190 Pattern::Range { expr, .. } => {
191 write!(f, "{}", expr_to_string(expr))
192 }
193 #[cfg(feature = "regex")]
194 Pattern::Regex { pattern, .. } => {
195 write!(f, r#"=~ r"{}""#, pattern)
196 }
197 #[cfg(feature = "regex")]
198 Pattern::Like { expr, .. } => {
199 write!(f, "=~ {}", expr_to_string(expr))
200 }
201 Pattern::Rest { .. } => {
202 write!(f, "..")
203 }
204 Pattern::Wildcard { .. } => {
205 write!(f, "_")
206 }
207 Pattern::Closure { closure, .. } => {
208 write!(f, "{}", quote::quote! { #closure })
209 }
210 Pattern::Map { entries, rest, .. } => {
211 write!(f, "#{{ ")?;
212 for (i, (key, value)) in entries.iter().enumerate() {
213 if i > 0 {
214 write!(f, ", ")?;
215 }
216 write!(f, "{}: {}", expr_to_string(key), value)?;
217 }
218 if *rest {
219 if !entries.is_empty() {
220 write!(f, ", ")?;
221 }
222 write!(f, "..")?;
223 }
224 write!(f, " }}")
225 }
226 }
227 }
228}
229
230struct Expected {
231 fields: Punctuated<FieldAssertion, Token![,]>,
232 rest: bool, // true if ".." was present
233}
234
235/// Represents an operation to be performed on a field before pattern matching
236#[derive(Debug, Clone)]
237#[allow(dead_code)]
238enum FieldOperation {
239 /// Dereference operation: *field, **field, etc.
240 /// The count indicates how many dereferences to perform
241 Deref {
242 count: usize,
243 span: proc_macro2::Span,
244 },
245
246 /// Method call: field.method(), field.len(), etc.
247 /// Stores the method name and arguments (if any)
248 Method {
249 name: syn::Ident,
250 args: Vec<syn::Expr>,
251 span: proc_macro2::Span,
252 },
253
254 /// Await operation: field.await
255 /// For async futures that need to be awaited
256 Await { span: proc_macro2::Span },
257
258 /// Nested field access: field.nested, field.inner.value, etc.
259 /// Stores the chain of field names to access
260 Nested {
261 fields: Vec<syn::Ident>,
262 span: proc_macro2::Span,
263 },
264
265 /// Index operation: field\[0\], field\[index\], etc.
266 /// Stores the index expression to use
267 Index {
268 index: syn::Expr,
269 span: proc_macro2::Span,
270 },
271
272 /// Combined operation: dereferencing followed by method/nested/index access
273 /// Example: *field.method(), **field.inner, *field\[0\], etc.
274 Combined {
275 deref_count: usize,
276 operation: Box<FieldOperation>,
277 span: proc_macro2::Span,
278 },
279
280 /// Chained operations: nested field followed by index or method
281 /// Example: field.nested\[0\], field.inner.method(), field.sub\[1\].len()
282 Chained {
283 operations: Vec<FieldOperation>,
284 span: proc_macro2::Span,
285 },
286}
287
288// Field assertion - a field name paired with its expected pattern
289// Now supports operations like dereferencing, method calls, and nested access
290#[derive(Debug, Clone)]
291struct FieldAssertion {
292 field_name: syn::Ident,
293 operations: Option<FieldOperation>,
294 pattern: Pattern,
295}
296
297/// Represents an element in a tuple pattern, supporting both positional and indexed syntax
298#[derive(Debug, Clone)]
299enum TupleElement {
300 /// Positional element: just a pattern in sequence
301 /// Example: "foo", > 10, Some(42)
302 Positional { pattern: Pattern },
303
304 /// Indexed element: explicit index with optional operations
305 /// Example: 0: "foo", *1: "bar", 2.len(): 5
306 Indexed {
307 index: usize,
308 operations: Option<FieldOperation>,
309 pattern: Pattern,
310 },
311}
312impl fmt::Display for TupleElement {
313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314 match self {
315 TupleElement::Positional { pattern } => {
316 write!(f, "{}", pattern)
317 }
318 TupleElement::Indexed {
319 index,
320 operations,
321 pattern,
322 } => {
323 if let Some(ops) = operations {
324 match ops {
325 FieldOperation::Deref { count, .. } => {
326 // Show deref operations before the index: *0:
327 for _ in 0..*count {
328 write!(f, "*")?;
329 }
330 write!(f, "{}: {}", index, pattern)
331 }
332 FieldOperation::Method { name, .. } => {
333 // Show method calls after the index: 0.len():
334 write!(f, "{}.{}(): {}", index, name, pattern)
335 }
336 FieldOperation::Await { .. } => {
337 // Show await after the index: 0.await:
338 write!(f, "{}.await: {}", index, pattern)
339 }
340 FieldOperation::Nested { fields, .. } => {
341 // Show nested access after the index: 0.field:
342 write!(f, "{}", index)?;
343 for field in fields {
344 write!(f, ".{}", field)?;
345 }
346 write!(f, ": {}", pattern)
347 }
348 FieldOperation::Index { index: idx, .. } => {
349 // Show index access after the tuple index: 0[1]:
350 write!(f, "{}[{}]: {}", index, quote::quote! { #idx }, pattern)
351 }
352 FieldOperation::Chained { operations, .. } => {
353 // Show chained operations after the tuple index: 0.field[1]:
354 write!(f, "{}", index)?;
355 for op in operations {
356 match op {
357 FieldOperation::Nested { fields, .. } => {
358 for field in fields {
359 write!(f, ".{}", field)?;
360 }
361 }
362 FieldOperation::Method { name, .. } => {
363 write!(f, ".{}()", name)?;
364 }
365 FieldOperation::Await { .. } => {
366 write!(f, ".await")?;
367 }
368 FieldOperation::Index { index, .. } => {
369 write!(f, "[{}]", quote::quote! { #index })?;
370 }
371 _ => write!(f, "{}", op)?,
372 }
373 }
374 write!(f, ": {}", pattern)
375 }
376 FieldOperation::Combined {
377 deref_count,
378 operation,
379 ..
380 } => {
381 // Show combined operations: *0.len():
382 for _ in 0..*deref_count {
383 write!(f, "*")?;
384 }
385 match operation.as_ref() {
386 FieldOperation::Method { name, .. } => {
387 write!(f, "{}.{}(): {}", index, name, pattern)
388 }
389 _ => {
390 write!(f, "{}{}: {}", index, operation, pattern)
391 }
392 }
393 }
394 }
395 } else {
396 write!(f, "{}: {}", index, pattern)
397 }
398 }
399 }
400 }
401}
402
403impl fmt::Display for FieldOperation {
404 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405 match self {
406 FieldOperation::Deref { count, .. } => {
407 for _ in 0..*count {
408 write!(f, "*")?;
409 }
410 Ok(())
411 }
412 FieldOperation::Method { name, .. } => {
413 write!(f, ".{}()", name)
414 }
415 FieldOperation::Await { .. } => {
416 write!(f, ".await")
417 }
418 FieldOperation::Nested { fields, .. } => {
419 for field in fields {
420 write!(f, ".{}", field)?;
421 }
422 Ok(())
423 }
424 FieldOperation::Index { index, .. } => {
425 write!(f, "[{}]", quote::quote! { #index })
426 }
427 FieldOperation::Chained { operations, .. } => {
428 for op in operations {
429 write!(f, "{}", op)?;
430 }
431 Ok(())
432 }
433 FieldOperation::Combined {
434 deref_count,
435 operation,
436 ..
437 } => {
438 for _ in 0..*deref_count {
439 write!(f, "*")?;
440 }
441 write!(f, "{}", operation)
442 }
443 }
444 }
445}
446
447#[derive(Debug, Clone, Copy)]
448pub(crate) enum ComparisonOp {
449 Less,
450 LessEqual,
451 Greater,
452 GreaterEqual,
453 Equal,
454 NotEqual,
455}
456
457impl fmt::Display for ComparisonOp {
458 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
459 match self {
460 ComparisonOp::Less => write!(f, "<"),
461 ComparisonOp::LessEqual => write!(f, "<="),
462 ComparisonOp::Greater => write!(f, ">"),
463 ComparisonOp::GreaterEqual => write!(f, ">="),
464 ComparisonOp::Equal => write!(f, "=="),
465 ComparisonOp::NotEqual => write!(f, "!="),
466 }
467 }
468}
469
470/// Structural assertion macro for testing complex data structures.
471///
472/// This procedural macro generates efficient runtime assertions that check structural patterns
473/// against actual values, providing detailed error messages when assertions fail. The macro
474/// transforms pattern-based syntax into optimized comparison code at compile time.
475///
476/// See the [crate-level documentation](crate) for comprehensive guides and learning examples.
477/// This documentation serves as a complete specification reference.
478///
479/// # Syntax Specification
480///
481/// ```text
482/// assert_struct!(expression, TypePattern);
483///
484/// TypePattern ::= TypeName '{' FieldPatternList '}'
485/// | '_' '{' FieldPatternList '}' // Wildcard pattern
486/// FieldPatternList ::= (FieldPattern ',')* ('..')?
487/// FieldPattern ::= FieldName ':' Pattern
488/// | FieldName FieldOperation ':' Pattern
489/// FieldOperation ::= ('*')+ | ('.' Identifier '(' ArgumentList? ')')
490/// Pattern ::= Value | ComparisonPattern | RangePattern | RegexPattern
491/// | EnumPattern | TuplePattern | SlicePattern | NestedPattern
492/// ```
493///
494/// # Complete Pattern Reference
495///
496/// ## Basic Value Patterns
497///
498/// | Pattern | Syntax | Description | Constraints |
499/// |---------|--------|-------------|-------------|
500/// | **Exact Value** | `field: value` | Direct equality comparison | Must implement `PartialEq` |
501/// | **String Literal** | `field: "text"` | String comparison (no `.to_string()` needed) | String or &str fields |
502/// | **Explicit Equality** | `field: == value` | Same as exact value but explicit | Must implement `PartialEq` |
503/// | **Inequality** | `field: != value` | Not equal comparison | Must implement `PartialEq` |
504///
505/// ## Comparison Patterns
506///
507/// | Pattern | Syntax | Description | Constraints |
508/// |---------|--------|-------------|-------------|
509/// | **Greater Than** | `field: > value` | Numeric greater than | Must implement `PartialOrd` |
510/// | **Greater Equal** | `field: >= value` | Numeric greater or equal | Must implement `PartialOrd` |
511/// | **Less Than** | `field: < value` | Numeric less than | Must implement `PartialOrd` |
512/// | **Less Equal** | `field: <= value` | Numeric less or equal | Must implement `PartialOrd` |
513///
514/// ## Range Patterns
515///
516/// | Pattern | Syntax | Description | Constraints |
517/// |---------|--------|-------------|-------------|
518/// | **Inclusive Range** | `field: start..=end` | Value in inclusive range | Must implement `PartialOrd` |
519/// | **Exclusive Range** | `field: start..end` | Value in exclusive range | Must implement `PartialOrd` |
520/// | **Range From** | `field: start..` | Value greater or equal to start | Must implement `PartialOrd` |
521/// | **Range To** | `field: ..end` | Value less than end | Must implement `PartialOrd` |
522/// | **Range Full** | `field: ..` | Matches any value | No constraints |
523///
524/// ## String Pattern Matching
525///
526/// | Pattern | Syntax | Description | Constraints |
527/// |---------|--------|-------------|-------------|
528/// | **Regex Literal** | `field: =~ r"pattern"` | Regular expression match | Requires `regex` feature, `String`/`&str` |
529/// | **Like Trait** | `field: =~ expression` | Custom pattern matching | Must implement `Like<T>` |
530///
531/// ## Field Operations
532///
533/// | Operation | Syntax | Description | Constraints |
534/// |-----------|--------|-------------|-------------|
535/// | **Dereference** | `*field: pattern` | Dereference smart pointer | Must implement `Deref` |
536/// | **Multiple Deref** | `**field: pattern` | Multiple dereference | Must implement `Deref` (nested) |
537/// | **Method Call** | `field.method(): pattern` | Call method and match result | Method must exist and return compatible type |
538/// | **Method with Args** | `field.method(args): pattern` | Call method with arguments | Method must exist with compatible signature |
539/// | **Tuple Method** | `(index.method(): pattern, _)` | Method on tuple element | Valid index, method exists |
540///
541/// ## Enum Patterns
542///
543/// | Pattern | Syntax | Description | Constraints |
544/// |---------|--------|-------------|-------------|
545/// | **Option Some** | `field: Some(pattern)` | Match Some variant with inner pattern | `Option<T>` field |
546/// | **Option None** | `field: None` | Match None variant | `Option<T>` field |
547/// | **Result Ok** | `field: Ok(pattern)` | Match Ok variant with inner pattern | `Result<T, E>` field |
548/// | **Result Err** | `field: Err(pattern)` | Match Err variant with inner pattern | `Result<T, E>` field |
549/// | **Unit Variant** | `field: EnumType::Variant` | Match unit enum variant | Enum with unit variant |
550/// | **Tuple Variant** | `field: EnumType::Variant(patterns...)` | Match tuple enum variant | Enum with tuple variant |
551/// | **Struct Variant** | `field: EnumType::Variant { fields... }` | Match struct enum variant | Enum with struct variant |
552///
553/// ## Wildcard Struct Patterns
554///
555/// | Pattern | Syntax | Description | Constraints |
556/// |---------|--------|-------------|-------------|
557/// | **Wildcard Struct** | `value: _ { fields... }` | Match struct without naming type | Must use `..` for partial matching |
558/// | **Nested Wildcard** | `_ { field: _ { ... }, .. }` | Nested anonymous structs | Avoids importing nested types |
559///
560/// ## Collection Patterns
561///
562/// | Pattern | Syntax | Description | Constraints |
563/// |---------|--------|-------------|-------------|
564/// | **Exact Slice** | `field: [pattern, pattern, ...]` | Match exact slice elements | `Vec<T>` or slice |
565/// | **Partial Head** | `field: [pattern, ..]` | Match prefix elements | `Vec<T>` or slice |
566/// | **Partial Tail** | `field: [.., pattern]` | Match suffix elements | `Vec<T>` or slice |
567/// | **Head and Tail** | `field: [pattern, .., pattern]` | Match first and last | `Vec<T>` or slice |
568/// | **Empty Slice** | `field: []` | Match empty collection | `Vec<T>` or slice |
569///
570/// ## Tuple Patterns
571///
572/// | Pattern | Syntax | Description | Constraints |
573/// |---------|--------|-------------|-------------|
574/// | **Exact Tuple** | `field: (pattern, pattern, ...)` | Match all tuple elements | Tuple type |
575/// | **Wildcard Element** | `field: (pattern, _, pattern)` | Ignore specific elements | Tuple type |
576/// | **Indexed Method** | `field: (0.method(): pattern, _)` | Method call on tuple element | Valid index |
577///
578/// # Parameters
579///
580/// - **`expression`**: Any expression that evaluates to a struct instance. The expression is
581/// borrowed, not consumed, so the value remains available after the assertion.
582/// - **`TypeName`**: The struct type name. Must exactly match the runtime type of the expression.
583/// - **`{ fields }`**: Pattern specification for struct fields. Can be partial (with `..`) or exhaustive.
584///
585/// # Runtime Behavior
586///
587/// ## Evaluation Semantics
588///
589/// - **Non-consuming**: The macro borrows the value, leaving it available after the assertion
590/// - **Expression evaluation**: The expression is evaluated exactly once before pattern matching
591/// - **Short-circuit evaluation**: Patterns are evaluated left-to-right, failing fast on first mismatch
592/// - **Field order independence**: Fields can be specified in any order in the pattern
593/// - **Type requirements**: All fields must have types compatible with their patterns
594///
595/// ## Pattern Matching Rules
596///
597/// ### Exhaustive vs Partial Matching
598/// - **Without `..`**: All struct fields must be specified in the pattern (exhaustive)
599/// - **With `..`**: Only specified fields are checked (partial matching)
600/// - **Multiple `..`**: Compilation error - only one rest pattern allowed per struct
601///
602/// ### Field Operation Precedence
603/// Field operations are applied in left-to-right order:
604/// ```text
605/// **field.method().other_method(): pattern
606/// // Equivalent to: ((*(*field)).method()).other_method()
607/// ```
608///
609/// ### String Literal Handling
610/// - String literals (`"text"`) automatically work with `String` and `&str` fields
611/// - No `.to_string()` conversion needed in patterns
612/// - Comparison uses `PartialEq` implementation
613///
614/// # Panics
615///
616/// The macro panics (causing test failure) when:
617///
618/// ## Pattern Mismatches
619/// - **Value mismatch**: Expected value doesn't equal actual value
620/// - **Comparison failure**: Comparison operator condition fails (e.g., `>`, `<`)
621/// - **Range mismatch**: Value outside specified range
622/// - **Enum variant mismatch**: Different enum variant than expected
623/// - **Collection length mismatch**: Slice pattern length differs from actual length
624/// - **None/Some mismatch**: Expected `Some` but got `None`, or vice versa
625/// - **Ok/Err mismatch**: Expected `Ok` but got `Err`, or vice versa
626///
627/// ## Method Call Failures
628/// - **Method panic**: Called method itself panics during execution
629/// - **Argument evaluation panic**: Method arguments panic during evaluation
630///
631/// ## Regex Failures (when `regex` feature enabled)
632/// - **Invalid regex**: Malformed regular expression pattern
633/// - **Regex evaluation panic**: Regex engine encounters error
634///
635/// ## Runtime Type Issues
636/// **Note**: Type mismatches are caught at compile time, not runtime.
637///
638/// # Compilation Errors
639///
640/// ## Field Validation
641/// - **Nonexistent field**: Field doesn't exist on the struct type
642/// - **Missing fields**: Required fields not specified (without `..`)
643/// - **Duplicate fields**: Same field specified multiple times
644/// - **Invalid field operations**: Operations not supported by field type
645///
646/// ## Type Compatibility
647/// - **Type mismatch**: Pattern type incompatible with field type
648/// - **Trait requirements**: Field doesn't implement required traits (`PartialEq`, `PartialOrd`, etc.)
649/// - **Method signatures**: Method doesn't exist or has incompatible signature
650/// - **Deref constraints**: Field type doesn't implement `Deref` for dereference operations
651///
652/// ## Syntax Validation
653/// - **Invalid syntax**: Malformed pattern syntax
654/// - **Invalid operators**: Unsupported operator for field type
655/// - **Invalid ranges**: Malformed range expressions
656/// - **Invalid regex syntax**: Invalid regex literal (when using raw strings)
657/// - **Multiple rest patterns**: More than one `..` in same struct pattern
658///
659/// ## Feature Requirements
660/// - **Missing regex feature**: Using `=~ r"pattern"` without `regex` feature enabled
661/// - **Like trait not implemented**: Using `=~ expr` where `Like` trait not implemented
662///
663/// # Edge Cases and Limitations
664///
665/// ## Method Call Constraints
666/// - **Return type compatibility**: Method return type must be compatible with pattern type
667/// - **Argument evaluation**: Method arguments are evaluated before the method call
668/// - **No generic method inference**: Generic methods may require explicit type annotations
669/// - **Tuple indexing bounds**: Tuple method calls require valid index at compile time
670///
671/// ## Collection Pattern Limitations
672/// - **Fixed length patterns**: Slice patterns without `..` require exact length match
673/// - **Nested pattern complexity**: Deeply nested slice patterns may impact compile time
674/// - **Memory usage**: Large literal slice patterns increase binary size
675///
676/// ## Smart Pointer Behavior
677/// - **Multiple deref levels**: Each `*` adds one deref level, must match pointer nesting
678/// - **Deref coercion**: Standard Rust deref coercion rules apply
679/// - **Ownership semantics**: Dereferencing borrows the pointed-to value
680///
681/// ## Performance Considerations
682/// - **Compile time**: Complex nested patterns increase compilation time
683/// - **Runtime overhead**: Pattern matching is zero-cost for simple patterns
684/// - **Error message generation**: Error formatting only occurs on failure
685///
686/// # Feature Dependencies
687///
688/// ## Regex Feature (`regex`)
689/// - **Default**: Enabled by default
690/// - **Required for**: `=~ r"pattern"` syntax with string literals
691/// - **Disable with**: `default-features = false` in Cargo.toml
692/// - **Alternative**: Use `Like` trait with pre-compiled regex or custom patterns
693///
694/// ## Like Trait Extension
695/// - **No feature required**: Always available
696/// - **Custom implementations**: Implement `Like<T>` for custom pattern matching
697/// - **Regex integration**: Built-in implementations for regex when feature enabled
698///
699/// # Error Message Format
700///
701/// When assertions fail, the macro generates structured error messages with:
702///
703/// ## Error Components
704/// - **Error type**: Specific failure category (value mismatch, comparison failure, etc.)
705/// - **Field path**: Complete path to the failing field (e.g., `response.user.profile.age`)
706/// - **Source location**: File name and line number of the assertion
707/// - **Actual value**: The value that was found
708/// - **Expected pattern**: The pattern that was expected to match
709/// - **Pattern context**: Visual representation showing where the failure occurred
710///
711/// ## Error Types
712/// - **value mismatch**: Direct equality comparison failed
713/// - **comparison mismatch**: Comparison operator condition failed (`>`, `<`, etc.)
714/// - **range mismatch**: Value outside specified range
715/// - **regex mismatch**: Regex pattern didn't match
716/// - **enum variant mismatch**: Wrong enum variant
717/// - **slice mismatch**: Collection length or element pattern failure
718/// - **method call error**: Method call or result pattern failure
719///
720/// ## Pattern Context Display
721/// Complex patterns show visual context with failure highlighting:
722/// ```text
723/// assert_struct! failed:
724///
725/// | Response { user: User { profile: Profile {
726/// comparison mismatch:
727/// --> `response.user.profile.age` (tests/api.rs:45)
728/// | age: > 18,
729/// | ^^^^^ actual: 17
730/// | } } }
731/// ```
732///
733/// ## Method Call Errors
734/// Method calls in field paths are clearly indicated:
735/// ```text
736/// comparison mismatch:
737/// --> `data.items.len()` (tests/collections.rs:23)
738/// actual: 3
739/// expected: > 5
740/// ```
741///
742/// # Quick Reference Examples
743///
744/// ```rust
745/// # use assert_struct::assert_struct;
746/// # #[derive(Debug)]
747/// # struct Example { value: i32, name: String, items: Vec<i32> }
748/// # let example = Example { value: 42, name: "test".to_string(), items: vec![1, 2] };
749/// // Basic pattern matching
750/// assert_struct!(example, Example {
751/// value: 42, // Exact equality
752/// name: != "other", // Inequality
753/// items.len(): >= 2, // Method call with comparison
754/// .. // Partial matching
755/// });
756/// ```
757///
758/// # See Also
759///
760/// - **Learning Guide**: See the [crate-level documentation](crate) for comprehensive examples
761/// - **Real-World Examples**: Check the `examples/` directory for practical usage patterns
762/// - **Like Trait**: Implement custom pattern matching with the `Like` trait
763#[proc_macro]
764pub fn assert_struct(input: TokenStream) -> TokenStream {
765 // Parse the input
766 let assert = match parse::parse(input) {
767 Ok(assert) => assert,
768 Err(err) => return TokenStream::from(err.to_compile_error()),
769 };
770
771 // Expand to output code
772 let expanded = expand::expand(&assert);
773
774 TokenStream::from(expanded)
775}