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