macro-compose is a library trying to simplify and organize proc-macros.
It offers traits (Lint
, Expand
) to split up the macro expansion into multiple smaller, reusable parts
and structs the collect the results (Collector
, Context
).
The following subsections show examples for different parts of this library.
The examples are taken from the example macro in examples/enum_from_str_macro
which implements a derive macro for FromStr
for an enum.
The Lint
trait is used to lint the macro input. Collector::error
can be used to output errors.
```rust use macro_compose::{Collector, Lint}; use syn::{Data, DeriveInput, Error, Fields};
struct EnsureEnumLint;
impl Lint
The Expand
trait is used to expand the macro.
Once a Lint
or Expand
has reported an error to the collector, the macro will no longer be expanded.
This way Expand
implementations can assume that the data checked by Lint
s is valid.
Returning None
from an Expand
does not automatically report an error.
```rust use macrocompose::{Collector, Expand}; use procmacro2::Ident; use syn::{parse_quote, Arm, Data, DeriveInput, Error, Fields, ItemImpl};
struct ImplFromStrExpand;
impl Expand
fn expand(&self, input: &DeriveInput, _: &mut Collector) -> Option<Self::Output> {
let variants = match &input.data {
Data::Enum(e) => &e.variants,
_ => unreachable!(),
};
let ident = &input.ident;
let arms = variants.iter().map(|v| -> Arm {
let v = &v.ident;
let name = v.to_string();
parse_quote!(
#name => ::core::result::Result::Ok(#ident :: #v)
)
});
let ident = &input.ident;
let error = error_struct_ident(input);
Some(parse_quote!(
impl ::core::str::FromStr for #ident {
type Err = #error;
fn from_str(s: &::core::primitive::str) -> ::core::result::Result<Self, Self::Err> {
match s {
#(#arms,)*
invalid => ::core::result::Result::Err( #error (::std::string::ToString::to_string(invalid))),
}
}
}
))
}
} ```
Context::new_parse
can be used to create a context from a TokenStream
(proc_macro::TokenStream).
This Context can be used to run Lint
s and Expand
s and get the resulting output.
```rust use macrocompose::{Collector, Context}; use procmacro::TokenStream;
pub fn derivefromstr(item: TokenStream) -> TokenStream { let mut collector = Collector::new();
let mut ctx = Context::new_parse(&mut collector, item);
ctx.lint(&EnsureEnumLint);
ctx.expand(&ErrorStructExpand);
ctx.expand(&ImplDebugErrorStructExpand);
ctx.expand(&ImplFromStrExpand);
collector.finish()
} ```