serde_tokenstream
This Rust crate is intended for use with macros that need bespoke configuration.
It's implemented as a serde::Deserializer
that operates on a
proc_macro2::TokenSteam
(easily converted from the standard
proc_macro::TokenStream
).
Say we're building a custom proc macro that you want consumers to use like this:
```rust
name = "SNPP",
owner = "Canary M Burns",
details = {
kind = Fission,
year_of_opening = 1968,
}
}] fn some_func() { ... } ```
The function that implements the proc macro must have two parameters (both of
type proc_macro::TokenStream
): attributes (the tokens with the braces that
follow the name of the macro), and the item (the function, type, etc. to
which the macro is applied):
```rust
pub fn MyMacro( attr: procmacro::TokenStream, item: procmacro::TokenStream, ) -> proc_macro::TokenStream { ... } ```
We'll first define the struct
type that represents the configuration and
derive
a serde::Deserialize
:
```rust
struct Config { name: String, owner: String, details: ConfigDetails, }
struct ConfigDetails { kind: ConfigDetailsType, yearofopening: usize, }
enum ConfigDetailsType { Coal, Fission, Hydroelectric, } ```
Now we can parse attr
into the Config
struct with serde_tokenstream::from_tokenstream
:
```rust use procmacro2::TokenStream; use serdetokenstream::from_tokenstream;
pub fn MyMacro(
attr: procmacro::TokenStream,
item: procmacro::TokenStream,
) -> procmacro::TokenStream {
let config = match fromtokenstream::
...
} ```
See the serde
documentation for the full range of controls that can be
applied to types and their members.
In some cases, it's useful to pass TokenStream values as parameters to a macro.
In this case we can use the TokenStreamWrapper
which is a wrapper around
TokenStream
that implements Deserialize
or ParseWrapper
which is a
wrapper around syn::Parse
that implements Deserialize
. The latter is useful
for passing in, for example, a syn::Path
, or other specific entities from the
syn
crate.
Note that errors will highlight the problematic portion of consuming code:
```rust
name = "Rocinante",
owner = "Rocicorp",
details = {
kind = Fusion,
year_of_opening = 2347
}
}] fn deploy() { ... } ```
error: unknown variant `Fusion`, expected one of `Coal`, `Fission`, `Hydroelectric`
--> tests/test_err1.rs:7:16
|
7 | kind = Fusion,
| ^^^^^^