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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
//! Derive associated functions for enum variants that are familiar from `std::option::Option` & `std::result::Result` such as `unwrap_or` or `and_then`. //! # Example //! ```ignore, no_run //! #[derive(Variantly)] //! enum Color { //! RGB(u8, u8, u8), //! HSV(u8, u8, u8), //! Grey(u8), //! FromOutOfSpace, //! #[variantly(rename = "darkness")] //! Black, //! } //! //! fn example() { //! let color = Color::HSV(123, 45, 67); //! //! // boolean helper function for determining variant: //! assert!(color.is_hsv()); //! assert!(!color.is_rgb()); //! //! // Get inner values: //! let (h, s, v) = color.unwrap_hsv(); //! assert_eq!((h, s, v), (123, 45, 67)); //! //! // Single values don't require tuple destructuring: //! let color = Color::Grey(128); //! let value = color.unwrap_grey(); //! assert_eq!(value, 128); //! //! // Alter inner value, only if hsv: //! let color = Color::HSV(111, 22, 33); //! let color = color.and_then_hsv(|(h, s, _)| (h, s, 100)); //! assert_eq!(color.unwrap_hsv(), (111, 22, 100)); //! //! // Safely unwrap with a fallback: //! let color = Color::RGB(255, 255, 0); //! let (r, g, b) = color.unwrap_or_rgb((0, 0, 0)); //! assert_eq!((r, g, b), (255, 255, 0)); //! // Since color is of the HSV variant, the default is not used. //! //! // Safely unwrap using the fallback //! let color = Color::FromOutOfSpace; //! let (r, g, b) = color.unwrap_or_rgb((0, 0, 0)); //! assert_eq!((r, g, b), (0, 0, 0)); //! //! // Convert into an Option //! let color = Color::RGB(0, 255, 255); //! let optional_rgb = color.ok_rgb(); //! assert_eq!(Some((0, 255, 255)), optional_rgb); //! //! // Convert into a Result //! let color = Color::RGB(255, 0, 255); //! let result_rgb = color.ok_or_rgb("Error: This is not an RGB variant!"); //! assert_eq!(Ok((255, 0, 255)), result_rgb); //! //! // Operations like this can also use their familiar `_else` versions: //! let color = Color::FromOutOfSpace; //! let result_rgb = color.ok_or_else_rgb(|| Some("This is a computationally expensive error!")); //! assert!(result_rgb.is_err()); //! //! // The `#[variantly(rename = "darkness")]` attribute renames associated functions: //! let color = Color::Black; //! assert!(color.is_darkness()) //! } //! ``` //! # Derived functions //! The following are supported function types that are derived for all enum variants: //! 1. `is` //! 1. `is_not` //! //! The following are supported function types that are derived for tuple-like structs. This includes structs that hold one or many tuple style values. //! 1. `and` //! 1. `and_then` //! 1. `expect` //! 1. `ok` //! 1. `ok_or` //! 1. `ok_or_else` //! 1. `or` //! 1. `unwrap` //! 1. `unwrap_or` //! 1. `unwrap_or_else` //! //! # Derived function naming //! Derived functions are named by parsing and combining the enum variant they correspond with and the name of the operation they perform. //! Simplified, this looks like: //! ``` //! use inflector::cases::snakecase::to_snake_case; //! //! fn name_fn(operation: String, variant_name: String) -> String { //! let snake_case_variant = to_snake_case(&variant_name); //! format!("{}_{}", snake_case_variant, operation) //! } //! //! #[test] //!fn it_makes_a_name() { //! assert_eq!( //! name_fn("unwrap","VariantA"), //! "unwrap_variant_a".into() //! ) //! } //! ``` //! //! # Renaming associated functions //! The `varianty` attribute may be placed on a variant in order to customize the resulting associated function names. //! ```ignore, no_run //! #[derive(Variantly)] //! enum SomeEnum { //! #[variantly(rename = "variant_a")] //! SomeVariantWithALongName(String), //! VariantB, //! } //! ``` //! Functions associated with `SomeVariantWithALongName` will now be accessible only with the `variant_a` //! suffix, such as `.unwrap_or_else_variant_a()`. This can help control overly verbose fn names. //! Note that the input to `rename` is used as is and is not coerced into snake_case. //! //! //! The above is also relevant when two variant names would expand to create conflicting function names: //! ```ignore, no_run //! #[derive(Variantly)] //! enum SomeEnum { //! #[variantly(rename = "capital")] //! ABC, //! #[variantly(rename = "lower")] //! abc, //! } //! ``` //! Without the `rename` attribute in the above, both variants would create conflicting functions such as `.is_abc()` due to the coercion to snake_case. //! This is avoided by using the rename input to create meaningful and unique fn names. //! #[macro_use] extern crate darling; extern crate proc_macro; #[macro_use] mod idents; mod derive; mod error; mod input; use derive::derive_variantly_fns; use proc_macro::TokenStream; use syn::{parse_macro_input, ItemEnum}; /// The `Variantly` derive macro. See [the module level documentation](self) for more. #[proc_macro_derive(Variantly, attributes(variantly))] pub fn variantly(input: TokenStream) -> TokenStream { let item_enum = parse_macro_input!(input as ItemEnum); derive_variantly_fns(item_enum).unwrap_or_else(|err| err.to_compile_error()) }