Proc Micro
Small conveniences for high-quality macros.
Use
$ cargo add proc_micro
$ cargo add strum --features=derive
Errors
Normal rust code returns on the first error. Great macros accumulate as many errors as they can and show them all at once.
- [MaybeError]: A container that holds zero or more [syn::Error]-s. When it holds error(s) they can be accumulated into a single [syn::Error] (which is a data structure for holding one or more macro errors).
- [OkMaybe]: An alternative for Result that allows for returning both data and (maybe) errors at the same time. Use Result when an error means the data is unusable (untrustable) or [OkMaybe] when an error means the partial data might still be useful in generating additional error. information. The caller can convert an [OkMaybe] into a [Result], but cannot convert a [Result] into an [OkMaybe].
Enum powered Attribute parsing
These helpers work with attributes defined as enums with this library:
- [WithSpan]: Holds the attributes you parsed and their spans
- [parse_attrs]: Turns a [syn::Attribute] into a [Vec] of your parsed enums
- [unique]: Guarantee each enum attribute is only listed once
- [check_exclusive]: Check if an exclusive attribute is used in conjunction with others.
- [known_attribute]: Convienence function for parsing an enum discriminant
Tutorial
Here's how you define a macro attribute that has a namespace of my_macro and
accepts rename = <string> and ignore attributes using the strum crate:
const NAMESPACE: AttrNamespace =
AttrNamespace;
Each parsed attribute is stored in our enum while the discriminant can be used as a lookup. By representing attributes as an enum, we can be confident our code handles attribute additions or modifications exhaustively.
This also provides a platform for unit testing attribute logic:
let attribute = .unwrap;
assert_eq!;
Then within your macro code you can convert many comma separated attributes into enums while accumulating errors:
let mut errors = new;
let field: Field = parse_quote! ;
let attributes: = parse_attrs.push_unwrap;
assert_eq!;
assert!;
assert!;
Use this result with other helpers to validate your attribute requirements.
For example [unique] requires that attributes are specified at most once i.e.
#[my_macro(ignore, ignore)] is incorrect. And [check_exclusive] is called
for attributes that must be used exclusively, i.e. using "ignore" with any
other attribute is in valid as they would have no effect. And you can use
the returned [WithSpan] information to build your own custom [syn::Error]
errors.
use OkMaybe;
// Make a structure to store your parsed configuration
// Use our building blocks to implement your desired logic
// No problems
let _config = field_config.to_result.unwrap;
// Problem with `check_exclusive`
let result = field_config.to_result;
assert!;
let err = result.err.unwrap;
assert_eq!;
// Problem with `unique`
let result = field_config.to_result;
assert!;
let err = result.err.unwrap;
assert_eq!;
// Multiple problems `unique` and unknown attribute
let result = field_config.to_result;
assert!;
let err = result.err.unwrap;
assert_eq!;