parse_variants/
lib.rs

1//! This crate allows us to parse tokens as one of the variants given by an `enum`.
2//! This is achieved by deriving the `parse_variants::Parse` trait on the enumeration which will in turn
3//! derive a [`syn::Parse`](https://docs.rs/syn/1.0/syn/parse/trait.Parse.html) implementation with
4//! the desired logic.
5//!
6//! # Motivation
7//! For a project, I was trying to parse tokens that could either be an integer literal or an identifier
8//! from a [ParseBuffer](https://docs.rs/syn/1.0/syn/parse/struct.ParseBuffer.html). This inspired me
9//! to write a custom derive macro for these kinds of use cases. We can now write
10//! ```
11//! #[derive(parse_variants::Parse)]
12//! enum Number {
13//!     Identifier(syn::Ident),
14//!     Literal(syn::LitInt),
15//! }
16//! ```
17//! and then use this type to parse either variant from a parse buffer like so:
18//! ```
19//! # #[derive(parse_variants::Parse)]
20//! # enum Number {
21//! #    Identifier(syn::Ident),
22//! #    Literal(syn::LitInt),
23//! # }
24//! # use syn::parse::ParseBuffer;
25//! # fn parse_number(input : &ParseBuffer) -> Result<(), syn::Error>{
26//! // input : &ParseBuffer
27//! let num : Number = input.parse()?;
28//! # Ok(())
29//! # }
30//! ```
31//! Parsing will return the first variant that can be parsed from the contents of the parse buffer.
32//! If none of the variants can be parsed, a compile error is returned. We can use this in any context
33//! where we wish to parse this type. The custom derive macro can also be used on
34//! much more general `enum` types, enabling pretty powerful parsing of variant types.
35//!
36//! [See the macro documentation for more use cases and some caveats](self::Parse).
37
38/// A derive macro that allows us to parse a variant of an enumeration.
39///
40/// # Usage
41///
42/// **Attention** This crate requires that you have the [syn crate](https://crates.io/crates/syn) as a dependency
43/// and that you have not renamed it.
44///
45/// ## General
46/// * The custom derive can be applied to *enumerations*, which may contain struct like or
47///   tuple like variants. Each variant may contain one or multiple fields.
48/// * Every contained field must implement the [`syn::parse::Parse`](https://docs.rs/syn/1.0.73/syn/parse/trait.Parse.html) trait.
49/// * Member fields for each variants are parsed in order of declaration.
50/// * The first variant (in order of declaration) that is successfully parsed from the input will
51///   be returned. The input `ParseBuffer` is advanced accordingly.
52/// * If no variant can be successfully parsed from the given input, a descriptive compile error
53///   is returned.
54///
55/// ## Caveats
56/// The enum variants are speculatively parsed in order or declaration, i.e. the first variant that can successfully
57/// parsed is be returned. Accordingly, the order matters if one variant includes other variants
58/// as in the following example
59/// ```
60/// // WRONG: this can never return the Identifier variant
61/// #[derive(parse_variants::Parse)]
62/// enum Number {
63///     Expression(syn::Expr),
64///     Identifier(syn::Ident)
65/// }
66/// ```
67/// Since identifiers can be parsed as expressions, the `Expression` variant will always be chosen,
68/// even if the given tokens could also have been parsed as an identifier.
69///
70/// ```
71/// // CORRECT: the most general pattern comes last
72/// #[derive(parse_variants::Parse)]
73/// enum Number {
74///     Identifier(syn::Ident),
75///     Expression(syn::Expr)
76/// }
77/// ```
78/// This is why we have to pay attention to ordering the variants from least general to most general.
79///
80/// ## Restrictions
81/// The enumeration cannot contain unit variants (i.e. without member fields), because there is no
82/// useful way to parse them.
83///
84/// ## Example
85/// It is possible to write pretty complex parsers for variants. See this very silly example:
86///
87/// ```
88/// use syn::Ident;
89/// use syn::Expr;
90/// # use assert2::let_assert;
91///
92/// mod kw {
93///     syn::custom_keyword!(meters);
94/// }
95///
96/// #[derive(parse_variants::Parse)]
97/// enum SillyEnum {
98///     ExpressionInMeters {
99///         first: syn::Expr,
100///         _meters: kw::meters,
101///     },
102///     IdentPlusPlus(Ident, syn::Token![+], syn::Token![+]),
103/// }
104/// # let_assert!(Ok(SillyEnum::ExpressionInMeters{..}) = syn::parse_str::<SillyEnum>("16 + 12*length meters"));
105/// # let_assert!(Ok(SillyEnum::IdentPlusPlus(_,_,_)) = syn::parse_str::<SillyEnum>("C++"));
106/// ```
107/// This parses the tokens `16 + 12*length meters` as the first and `C++` as the second variant.
108pub use parse_variants_derive::Parse;
109
110#[cfg(test)]
111#[allow(clippy::large_enum_variant)]
112mod test;