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