├── .github └── workflows │ └── rust.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── src ├── derive.rs ├── error.rs ├── idents.rs ├── input.rs └── lib.rs └── tests ├── derived_and.rs ├── derived_and_then.rs ├── derived_expect.rs ├── derived_is_and_is_not.rs ├── derived_mut.rs ├── derived_ok.rs ├── derived_ok_mut_or.rs ├── derived_ok_mut_or_else.rs ├── derived_ok_or.rs ├── derived_ok_or_else.rs ├── derived_ok_ref_or.rs ├── derived_ok_ref_or_else.rs ├── derived_or.rs ├── derived_or_else.rs ├── derived_ref.rs ├── derived_unwrap.rs ├── derived_unwrap_or.rs ├── derived_unwrap_or_else.rs ├── helper.rs └── std_imports.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ main, develop ] 6 | pull_request: 7 | branches: [ main, develop ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | src/variantly_derive/target/ 5 | scratch/* 6 | 7 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 8 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 9 | Cargo.lock 10 | 11 | # These are backup files generated by rustfmt 12 | **/*.rs.bk 13 | 14 | 15 | # Added by cargo 16 | 17 | /target 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [0.4.0] - 2023-11-27 10 | ### Added 11 | - Derived methods for obtaining mutable references to inner values: 12 | - `.{variant_name}_mut()` 13 | - `.{variant_name}_mut_or()` 14 | - `.{variant_name}_mut_or_else()` 15 | 16 | ### Fixed 17 | - All items from `std::prelude` now use fully qualified syntax so as to avoid conflicting with other items of the same name that are in scope. This will allow the use of the derive macro in situations where a custom Result or similar is defined. 18 | 19 | ### Breaking 20 | - Newly derived methods could potentially conflict with manually derived implementations of the same name. 21 | - Example: 22 | - Given a Color enum with an `HSV` variant and a manually implemented method named `hsv_mut`,the newly derived `.{variant_name}_mut()` will conflict with the manual implementation causing a compilation error. 23 | - Resolution: 24 | - If the manually derived methods provide the same functionality as the derived one, you can remove the manual implementation. Otherwise, consider [renaming](https://docs.rs/variantly/0.3.0/variantly/#renaming-methods) the derived methods. 25 | 26 | 27 | ## [0.3.0] - 2023-11-07 28 | ### Added 29 | - Derived methods for obtaining references to inner values: 30 | - `.{variant_name}_ref()` 31 | - `.{variant_name}_ref_or()` 32 | - `.{variant_name}_ref_or_else()` 33 | 34 | ### Breaking 35 | - Newly derived methods could potentially conflict with manually derived implementations of the same name. 36 | - Example: 37 | - Given a Color enum with an `HSV` variant and a manually implemented method named `hsv_ref`,the newly derived `.{variant_name}_ref()` will conflict with the manual implementation causing a compilation error. 38 | - Resolution: 39 | - If the manually derived methods provide the same functionality as the derived one, you can remove the manual implementation. Otherwise, consider [renaming](https://docs.rs/variantly/0.2.0/variantly/#renaming-methods) the derived methods. 40 | 41 | ## [0.2.0] - 2021-01-12 42 | ### Added 43 | - Derived methods for producing Options & Results: 44 | - `.{variant_name}()` 45 | - `.{variant_name}_or()` 46 | - `.{variant_name}_or_else()` 47 | - CHANGELOG.md 48 | ### Changed 49 | - Improved documentation in README.md & base lib.rs 50 | ### Deprecated 51 | - Derived methods that begin with `.ok` including: 52 | - `.ok_{variant_name}()` 53 | - `.ok_or_{variant_name}()` 54 | - `.ok_or_else{variant_name}()` 55 | 56 | ## [0.1.0] - 2020-12-29 57 | ### Added 58 | - Initial Implementation, License & README.md 59 | 60 | [Unreleased]: https://github.com/luker-os/variantly/compare/v0.4.0...HEAD 61 | [0.4.0]: https://github.com/luker-os/variantly/compare/v0.3.0...v0.4.0 62 | [0.3.0]: https://github.com/luker-os/variantly/compare/v0.2.0...v0.3.0 63 | [0.2.0]: https://github.com/luker-os/variantly/compare/v0.1.0...v0.2.0 64 | [0.1.0]: https://github.com/luker-os/variantly/releases/tag/v0.1.0 -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Luke Roberts "] 3 | description = """ 4 | Derive helper methods for enum variants that are familiar from `std::option::Option` & `std::result::Result` such as `unwrap_or` or `and_then`. 5 | """ 6 | edition = "2018" 7 | license = "MIT" 8 | name = "variantly" 9 | version = "0.4.0" 10 | repository = "https://github.com/luker-os/variantly" 11 | 12 | [dependencies] 13 | Inflector = "^0.11.4" 14 | darling = "^0.11.0" 15 | proc-macro2 = "1.0" 16 | quote = "1.0" 17 | syn = { features = ["full"], version = "1.0" } 18 | uuid = { features = ["v4"], version = "^0.8.0" } 19 | 20 | [lib] 21 | proc-macro = true 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Luke Roberts 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Variantly 2 | Derive helper methods for enum variants that are familiar from `Option` & `Result` such as `unwrap_or` or `and_then`. 3 | # Example 4 | ```rust 5 | #[derive(variantly::Variantly)] 6 | enum Color { 7 | RGB(u8, u8, u8), 8 | HSV(u8, u8, u8), 9 | Grey(u8), 10 | FromOutOfSpace, 11 | #[variantly(rename = "darkness")] 12 | Black, 13 | } 14 | 15 | fn example() { 16 | let color = Color::HSV(123, 45, 67); 17 | 18 | // boolean helper method for determining variant: 19 | assert!(color.is_hsv()); 20 | assert!(!color.is_rgb()); 21 | 22 | // Get inner values: 23 | let (h, s, v) = color.unwrap_hsv(); 24 | assert_eq!((h, s, v), (123, 45, 67)); 25 | 26 | // Single values don't require tuple destructuring: 27 | let color = Color::Grey(128); 28 | let value = color.unwrap_grey(); 29 | assert_eq!(value, 128); 30 | 31 | // Alter inner value, only if hsv: 32 | let color = Color::HSV(111, 22, 33); 33 | let color = color.and_then_hsv(|(h, s, _)| (h, s, 100)); 34 | assert_eq!(color.unwrap_hsv(), (111, 22, 100)); 35 | 36 | // Safely unwrap with a fallback: 37 | let color = Color::RGB(255, 255, 0); 38 | let (r, g, b) = color.unwrap_or_rgb((0, 0, 0)); 39 | assert_eq!((r, g, b), (255, 255, 0)); 40 | // Since color is of the HSV variant, the default is not used. 41 | 42 | // Safely unwrap using the fallback 43 | let color = Color::FromOutOfSpace; 44 | let (r, g, b) = color.unwrap_or_rgb((0, 0, 0)); 45 | assert_eq!((r, g, b), (0, 0, 0)); 46 | 47 | // Convert into an Option 48 | let color = Color::RGB(0, 255, 255); 49 | let optional_rgb = color.rgb(); 50 | assert_eq!(Some((0, 255, 255)), optional_rgb); 51 | 52 | // Convert into a Result 53 | let color = Color::RGB(255, 0, 255); 54 | let result_rgb = color.rgb_or("Error: This is not an RGB variant!"); 55 | assert_eq!(Ok((255, 0, 255)), result_rgb); 56 | 57 | // Operations like this can also use their familiar `_else` versions: 58 | let color = Color::FromOutOfSpace; 59 | let result_rgb = color.rgb_or_else(|| Some("This is a computationally expensive error!")); 60 | assert!(result_rgb.is_err()); 61 | 62 | // The `#[variantly(rename = "darkness")]` attribute renames derived methods: 63 | let color = Color::Black; 64 | assert!(color.is_darkness()) 65 | } 66 | ``` 67 | # Derived Methods 68 | In the naming of all methods described here, replace the `{variant_name}` with the snake_case formatted name of the given variant. 69 | 70 | ## Option & Result Conversion 71 | Use the below methods to convert the enum into either an option or result: 72 | 73 | ### `pub fn {variant_name}(self) -> Option(...)` 74 | If the enum is of the given variant, returns a `Some` containing the inner variant value. Otherwise, return None. 75 | 76 | #### Example 77 | ```rust 78 | let color = Color::HSV(1,2,3); 79 | 80 | let option = color.hsv(); 81 | assert_eq!(Some((1, 2, 3)), option); 82 | 83 | let color = Color::FromOutOfSpace; 84 | assert_eq!(None, color.rgb()); 85 | ``` 86 | 87 | *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 88 | 89 | ### `pub fn {variant_name}_ref(&self) -> Option(&...)` 90 | If the enum is of the given variant, returns a `Some` containing a ref to the inner variant value. Otherwise, return None. 91 | 92 | #### Example 93 | ```rust 94 | let color = Color::HSV(1,2,3); 95 | 96 | let option = color.hsv_ref(); 97 | assert_eq!(Some((&1, &2, &3)), option); 98 | 99 | let color = Color::FromOutOfSpace; 100 | assert_eq!(None, color.rgb_ref()); 101 | ``` 102 | 103 | *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 104 | 105 | ### `pub fn {variant_name}_mut(&mut self) -> Option(&mut...)` 106 | If the enum is of the given variant, returns a `Some` containing a mutable ref to the inner variant value. Otherwise, return None. 107 | 108 | #### Example 109 | ```rust 110 | let mut color = Color::HSV(1,2,3); 111 | 112 | let option = color.hsv_mut(); 113 | assert_eq!(Some((&mut 1, &mut 2, &mut 3)), option); 114 | 115 | let mut color = Color::FromOutOfSpace; 116 | assert_eq!(None, color.rgb_mut()); 117 | ``` 118 | 119 | *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 120 | 121 | ### `pub fn {variant_name}_or(self, err: E) -> Result<(...), E>` 122 | If the enum is of the given variant, returns a `Result::Ok` containing the inner value. Otherwise, return `Result::Err` containing `err`. 123 | 124 | #### Example 125 | ```rust 126 | let color = Color::HSV(1,2,3); 127 | 128 | let result = color.hsv_or("Error: Not an HSV!"); 129 | assert_eq!(Ok((1, 2, 3)), result); 130 | 131 | let color = Color::FromOutOfSpace; 132 | let result = color.hsv_or("Error: Not an HSV!"); 133 | assert_eq!(Err("Error: Not an HSV!"), result); 134 | ``` 135 | 136 | *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 137 | 138 | ### `pub fn {variant_name}_ref_or(&self, err: E) -> Result<(&...), E>` 139 | If the enum is of the given variant, returns a `Result::Ok` containing a ref to the inner value. Otherwise, return `Result::Err` containing `err`. 140 | 141 | #### Example 142 | ```rust 143 | let color = Color::HSV(1,2,3); 144 | 145 | let result = color.hsv_ref_or("Error: Not an HSV!"); 146 | assert_eq!(Ok((&1, &2, &3)), result); 147 | 148 | let color = Color::FromOutOfSpace; 149 | let result = color.hsv_ref_or("Error: Not an HSV!"); 150 | assert_eq!(Err("Error: Not an HSV!"), result); 151 | ``` 152 | 153 | *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 154 | 155 | ### `pub fn {variant_name}_mut_or(&mut self, err: E) -> Result<(&mut...), E>` 156 | If the enum is of the given variant, returns a `Result::Ok` containing a mutable ref to the inner value. Otherwise, return `Result::Err` containing `err`. 157 | 158 | #### Example 159 | ```rust 160 | let mut color = Color::HSV(1,2,3); 161 | 162 | let result = color.hsv_mut_or("Error: Not an HSV!"); 163 | assert_eq!(Ok((&mut 1, &mut 2, &mut 3)), result); 164 | 165 | let mut color = Color::FromOutOfSpace; 166 | let result = color.hsv_mut_or("Error: Not an HSV!"); 167 | assert_eq!(Err("Error: Not an HSV!"), result); 168 | ``` 169 | 170 | *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 171 | 172 | ### `pub fn {variant_name}_or_else E>(self, f: F) -> Result<(...), E>` 173 | If the enum is of the given variant, returns a `Result::Ok` containing the inner variant value. Otherwise, calls `f` to calculate a `Result::Err`. 174 | 175 | #### Example 176 | ```rust 177 | let color = Color::HSV(1,2,3); 178 | 179 | let result = color.hsv_or_else(|| "This is an expensive error to create."); 180 | assert_eq!(Ok((1, 2, 3)), result); 181 | 182 | let color = Color::FromOutOfSpace; 183 | let result = color.hsv_or_else(|| "This is an expensive error to create."); 184 | assert_eq!(Err("This is an expensive error to create."), result); 185 | ``` 186 | 187 | *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 188 | 189 | ### `pub fn {variant_name}_ref_or_else E>(&self, f: F) -> Result<(&...), E>` 190 | If the enum is of the given variant, returns a `Result::Ok` containing a ref to the inner variant value. Otherwise, calls `f` to calculate a `Result::Err`. 191 | 192 | #### Example 193 | ```rust 194 | let color = Color::HSV(1,2,3); 195 | 196 | let result = color.hsv_ref_or_else(|| "This is an expensive error to create."); 197 | assert_eq!(Ok((&1, &2, &3)), result); 198 | 199 | let color = Color::FromOutOfSpace; 200 | let result = color.hsv_ref_or_else(|| "This is an expensive error to create."); 201 | assert_eq!(Err("This is an expensive error to create."), result); 202 | ``` 203 | 204 | *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 205 | 206 | ### `pub fn {variant_name}_mut_or_else E>(&mut self, f: F) -> Result<(&mut...), E>` 207 | If the enum is of the given variant, returns a `Result::Ok` containing a mut ref to the inner variant value. Otherwise, calls `f` to calculate a `Result::Err`. 208 | 209 | #### Example 210 | ```rust 211 | let mut color = Color::HSV(1,2,3); 212 | 213 | let result = color.hsv_mut_or_else(|| "This is an expensive error to create."); 214 | assert_eq!(Ok((&mut 1, &mut 2, &mut 3)), result); 215 | 216 | let mut color = Color::FromOutOfSpace; 217 | let result = color.hsv_mut_or_else(|| "This is an expensive error to create."); 218 | assert_eq!(Err("This is an expensive error to create."), result); 219 | ``` 220 | 221 | *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 222 | 223 | ## Accessing Inner Values 224 | Use the below methods to easily access the inner value of a given variant. 225 | 226 | ## `pub fn expect_{variant_name}(self, msg: &str) -> (...)` 227 | Returns the contained value. 228 | 229 | ### Panics 230 | Panics if the enum is not of the given variant with the custom message `msg`. 231 | 232 | ### Example 233 | ```rust 234 | #[derive(variantly::Variantly)] 235 | enum Color { 236 | HSV(u8, u8, u8), 237 | Grey(u8), 238 | } 239 | 240 | let color_a = Color::HSV(1,2,3); 241 | let color_b = Color::Grey(10); 242 | 243 | let (h, s, v) = color_a.expect_hsv("This should be an hsv"); 244 | assert_eq!((h, s, v), (1, 2, 3)); 245 | 246 | let grey = color_b.expect_grey("This should be grey"); 247 | assert_eq!(grey, 10); 248 | ``` 249 | 250 | *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 251 | 252 | ## `pub fn unwrap_{variant_name}(self) -> (...)` 253 | Returns the contained value. 254 | 255 | ### Panics 256 | Panics if the enum is not of the given variant. 257 | 258 | ### Example 259 | ```rust 260 | let color_a = Color::HSV(1,2,3); 261 | let color_b = Color::Grey(10); 262 | 263 | let (h, s, v) = color_a.unwrap_hsv(); 264 | assert_eq!((h, s, v), (1, 2, 3)); 265 | 266 | let grey = color_b.unwrap_grey(); 267 | assert_eq!(grey, 10); 268 | ``` 269 | 270 | *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 271 | 272 | ## `pub fn unwrap_or_{variant_name}(self, fallback: (...)) -> (...)` 273 | Returns the contained value if the enum is of the given variant, otherwise returns the provided `fallback`. 274 | 275 | ### Example 276 | ```rust 277 | let color_a = Color::HSV(1,2,3); 278 | let color_b = Color::Grey(10); 279 | 280 | let (h, s, v) = color_a.unwrap_or_hsv((4, 5, 6)); 281 | assert_eq!((h, s, v), (1, 2, 3)); 282 | 283 | let color = color_b.unwrap_or_rgb((4, 5, 6)); 284 | assert_eq!(color, (4, 5, 6)); 285 | ``` 286 | 287 | *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 288 | 289 | ## `pub fn unwrap_or_else_{variant_name} (...)>(self, f: F) -> (...)` 290 | Returns the contained value if the enum is of the given variant, otherwise computes a fallback from `f`. 291 | 292 | ### Example 293 | ```rust 294 | let color_a = Color::HSV(1,2,3); 295 | let color_b = Color::Grey(10); 296 | 297 | let (h, s, v) = color_a.unwrap_or_else_hsv(|| (4,5,6)); 298 | assert_eq!((h, s, v), (1, 2, 3)); 299 | 300 | let (h, s, v) = color_b.unwrap_or_else_hsv(|| (4,5,6)); 301 | assert_eq!((h, s, v), (4, 5, 6)); 302 | ``` 303 | 304 | *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 305 | 306 | ## Testing Variant Type 307 | Use the below methods to test whether a variant is of the given type. 308 | 309 | ### `pub fn is_{variant_name}(self) -> bool` 310 | Returns `true` if the enum is of the given variant. 311 | 312 | #### Example 313 | ```rust 314 | let color = Color::FromOutOfSpace; 315 | assert!(color.is_from_out_of_space()); 316 | ``` 317 | 318 | *Note: Available for all variant types* 319 | 320 | ### `pub fn is_not_{variant_name}(self) -> bool` 321 | Returns `true` if the enum is *not* of the given variant. 322 | 323 | #### Example 324 | ```rust 325 | let color = Color::HSV(1,2,3); 326 | assert!(color.is_not_rgb()); 327 | ``` 328 | 329 | *Note: Available for all variant types* 330 | 331 | ## Compare & Process Specific Variant 332 | Use the below to process and compare a specific enum variant. 333 | 334 | ### `pub fn and_{variant_name}(self, enum_b: GivenEnum) -> GivenEnum` 335 | Returns `enum_b` if both self and `enum_b` are of the given variant. Otherwise returns `self`. 336 | 337 | #### Example 338 | ```rust 339 | let color_a = Color::HSV(1,2,3); 340 | let color_b = Color::HSV(4,5,6); 341 | let and = color_a.and_hsv(color_b); 342 | assert_eq!( 343 | and, 344 | Color::HSV(4,5,6), 345 | ); 346 | ``` 347 | 348 | *Available for all variant types* 349 | 350 | ### `pub fn and_then_{variant_name} (...)>(self, f: F) -> Self` 351 | Returns the enum as is if it is not of the given variant, otherwise calls `f` with the wrapped value and returns the result. 352 | 353 | #### Example 354 | ```rust 355 | let color_a = Color::HSV(1,2,3); 356 | 357 | let and = color_a.and_then_hsv(|(h, s, _)| (h, s, 4)); 358 | assert_eq!( 359 | and, 360 | Color::HSV(1, 2, 4), 361 | ); 362 | ``` 363 | 364 | *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 365 | 366 | ### `pub fn or_{variant_name}(self, enum_b: GivenEnum) -> GivenEnum` 367 | Returns `self` if it is of the given variant, otherwise returns `enum_b`. 368 | 369 | #### Example 370 | ```rust 371 | let color_a = Color::HSV(1,2,3); 372 | let color_b = Color::RGB(4,5,6); 373 | let or = color_a.or_rgb(color_b); 374 | assert_eq!( 375 | or, 376 | Color::RGB(4,5,6), 377 | ); 378 | ``` 379 | 380 | *Available for all variant types* 381 | 382 | ### `pub fn or_else_{variant_name} (...)>(self, f: F) -> Self {` 383 | Returns `self` if it is of the given variant, otherwise calls `f` and returns the result. 384 | 385 | #### Example 386 | ```rust 387 | let color = Color::HSV(1,2,3); 388 | let color = color.or_else_rgb(|| (4,5,6)); 389 | assert_eq!( 390 | color, 391 | Color::RGB(4,5,6), 392 | ); 393 | ``` 394 | 395 | *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 396 | 397 | # Renaming Methods 398 | The `variantly` attribute may be placed on a variant in order to customize the resulting method names. The value set against `rename` inside the attribute will be used in place of the snake_cased variant name when constructing derived method names. 399 | ```rust 400 | #[derive(variantly::Variantly)] 401 | enum SomeEnum { 402 | #[variantly(rename = "variant_a")] 403 | SomeVariantWithALongName(String), 404 | VariantB, 405 | } 406 | 407 | let variant = SomeEnum::SomeVariantWithALongName(String::from("Hello")); 408 | assert!(variant.is_variant_a()); 409 | ``` 410 | Methods associated with `SomeVariantWithALongName` will now be accessible only with the `variant_a` 411 | suffix, such as `.unwrap_or_else_variant_a()`. This can help control overly verbose fn names. 412 | Note that the input to `rename` is used as is and is not coerced into snake_case. 413 | 414 | The above is also relevant when two variant names would expand to create conflicting method names: 415 | ```rust 416 | #[derive(variantly::Variantly)] 417 | enum SomeEnum { 418 | #[variantly(rename = "capital")] 419 | ABC, 420 | #[variantly(rename = "lower")] 421 | abc, 422 | } 423 | ``` 424 | Without the `rename` attribute in the above, both variants would create conflicting functions such as `.is_abc()` due to the coercion to snake_case. 425 | This is avoided by using the `rename` input to create meaningful and unique fn names. 426 | 427 | #### License 428 | 429 | 430 | Licensed under MIT license. 431 | 432 | 433 |
434 | 435 | 436 | Unless you explicitly state otherwise, any contribution intentionally submitted 437 | for inclusion in this crate shall be licensed as above, without any additional terms or conditions. 438 | -------------------------------------------------------------------------------- /src/derive.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::Result, 3 | idents::generate_idents, 4 | input::{compare_used_names, try_parse_variants, validate_compare, VariantParsed}, 5 | }; 6 | 7 | use darling::ast::Style::{Struct, Tuple, Unit}; 8 | use proc_macro::TokenStream; 9 | use proc_macro2::TokenStream as TokenStream2; 10 | use quote::{format_ident, quote}; 11 | use syn::{Ident, ItemEnum, Type}; 12 | 13 | pub fn derive_variantly_fns(item_enum: ItemEnum) -> Result { 14 | let enum_name = &item_enum.ident; 15 | 16 | // For collecting impl functions 17 | let mut functions = vec![]; 18 | 19 | let variants = try_parse_variants(&item_enum)?; 20 | 21 | validate_compare(&variants, vec![compare_used_names])?; 22 | 23 | variants.iter().for_each(|variant| { 24 | // This will be initialized with a tokenstream representing how to match & ignore any variables held by a variant. 25 | 26 | let ident = &variant.ident; 27 | let ignore = match &variant.fields.style { 28 | Tuple => { 29 | handle_tuple(variant, &mut functions, enum_name); 30 | quote!((..)) 31 | } 32 | Struct => quote!({ .. }), 33 | Unit => quote!(), 34 | }; 35 | 36 | // include any impl functions that are common to all variant types. 37 | identify!(variant.used_name, [is, is_not, and, or]); 38 | functions.push(quote! { 39 | pub fn #is(&self) -> bool { 40 | match self { 41 | #enum_name::#ident#ignore => true, 42 | _ => false 43 | } 44 | } 45 | 46 | pub fn #is_not(&self) -> bool { 47 | !self.#is() 48 | } 49 | 50 | pub fn #and(self, and: Self) -> Self { 51 | match (&self, &and) { 52 | (#enum_name::#ident#ignore, #enum_name::#ident#ignore) => and, 53 | _ => self 54 | } 55 | } 56 | 57 | pub fn #or(self, or: Self) -> Self { 58 | match &self { 59 | #enum_name::#ident#ignore => self, 60 | _ => or 61 | } 62 | } 63 | 64 | }); 65 | }); 66 | 67 | let generics = &item_enum.generics; 68 | let where_clause = &generics.where_clause; 69 | 70 | // Declare the actual impl block & iterate over all fns. 71 | let output: TokenStream = quote! { 72 | impl#generics #enum_name#generics #where_clause { 73 | #(#functions)* 74 | } 75 | } 76 | .into(); 77 | 78 | Ok(output) 79 | } 80 | 81 | /// Construct all impl functions related to variants with tuple style internal variables and add them to the functions vec. 82 | fn handle_tuple(variant: &VariantParsed, functions: &mut Vec, enum_name: &Ident) { 83 | // parse necessary information from variant & fields. 84 | let ident = &variant.ident; 85 | let types: Vec<&Type> = variant 86 | .fields 87 | .fields 88 | .iter() 89 | .map(|field| &field.ty) 90 | .collect(); 91 | 92 | // Generate a unique ident per type used in the variant 93 | let vars = generate_idents(types.len()); 94 | let vars = quote! { (#( #vars ),*)}; 95 | 96 | let ref_types = quote! {(#( & #types ),*)}; 97 | let mut_types = quote! {(#( &mut #types ),*)}; 98 | let types = quote! { (#( #types ),*)}; 99 | 100 | // declare ident variables with helper macro. 101 | identify!( 102 | variant.used_name, 103 | [ 104 | and_then, 105 | expect, 106 | ok_or_else, 107 | ok_or, 108 | ok, 109 | or_else, 110 | unwrap_or_else, 111 | unwrap_or, 112 | unwrap 113 | ] 114 | ); 115 | 116 | // used for both pattern matching and constructing variants: 117 | // EX: var_pattern = SomeEnum::SomeVariant(some_variable_1, some_variable_2) 118 | let var_pattern = quote! { #enum_name::#ident#vars }; 119 | 120 | // Helper for deprecating methods 121 | let deprecate = |alternate| { 122 | let note = format!( 123 | "Please use the derived `{}::{}` method instead. This method will be removed in 1.0.0 or next pre-stable minor bump.", 124 | &enum_name, alternate 125 | ); 126 | quote! { 127 | #[deprecated( 128 | since = "0.2.0", 129 | note = #note 130 | )] 131 | } 132 | }; 133 | 134 | let var_fn = &variant.used_name; 135 | let var_or_fn = format_ident!("{}_or", var_fn); 136 | let var_or_else_fn = format_ident!("{}_or_else", var_fn); 137 | 138 | let var_ref_fn = format_ident!("{}_ref", var_fn); 139 | let var_ref_or_fn = format_ident!("{}_ref_or", var_fn); 140 | let var_ref_or_else_fn = format_ident!("{}_ref_or_else", var_fn); 141 | 142 | let var_mut_fn = format_ident!("{}_mut", var_fn); 143 | let var_mut_or_fn = format_ident!("{}_mut_or", var_fn); 144 | let var_mut_or_else_fn = format_ident!("{}_mut_or_else", var_fn); 145 | 146 | let ok_deprecation = deprecate(var_fn); 147 | let ok_or_deprecation = deprecate(&var_or_fn); 148 | let ok_or_else_deprecation = deprecate(&var_or_else_fn); 149 | 150 | // Create and push actual impl functions 151 | functions.push(quote! { 152 | pub fn #var_fn(self) -> std::option::Option<(#types)> { 153 | match self { 154 | #var_pattern => std::option::Option::Some((#vars)), 155 | _ => std::option::Option::None, 156 | } 157 | } 158 | 159 | pub fn #var_ref_fn(&self) -> std::option::Option<(#ref_types)> { 160 | match self { 161 | #var_pattern => std::option::Option::Some((#vars)), 162 | _ => std::option::Option::None, 163 | } 164 | } 165 | 166 | pub fn #var_mut_fn(&mut self) -> std::option::Option<(#mut_types)> { 167 | match self { 168 | #var_pattern => std::option::Option::Some((#vars)), 169 | _ => std::option::Option::None, 170 | } 171 | } 172 | 173 | pub fn #var_or_fn(self, or: E) -> std::result::Result<(#types), E> { 174 | self.#var_or_else_fn(|| or) 175 | } 176 | 177 | pub fn #var_or_else_fn E>(self, or_else: F) -> std::result::Result<(#types), E> { 178 | match self { 179 | #var_pattern => std::result::Result::Ok((#vars)), 180 | _ => std::result::Result::Err(or_else()) 181 | } 182 | } 183 | 184 | pub fn #var_ref_or_fn(&self, or: E) -> std::result::Result<(#ref_types), E> { 185 | self.#var_ref_or_else_fn(|| or) 186 | } 187 | 188 | pub fn #var_mut_or_fn(&mut self, or: E) -> std::result::Result<(#mut_types), E> { 189 | self.#var_mut_or_else_fn(|| or) 190 | } 191 | 192 | pub fn #var_ref_or_else_fn E>(&self, or_else: F) -> std::result::Result<(#ref_types), E> { 193 | match self { 194 | #var_pattern => std::result::Result::Ok((#vars)), 195 | _ => std::result::Result::Err(or_else()) 196 | } 197 | } 198 | 199 | pub fn #var_mut_or_else_fn E>(&mut self, or_else: F) -> std::result::Result<(#mut_types), E> { 200 | match self { 201 | #var_pattern => std::result::Result::Ok((#vars)), 202 | _ => std::result::Result::Err(or_else()) 203 | } 204 | } 205 | 206 | pub fn #and_then (#types)>(self, and_then: F) -> Self { 207 | match self { 208 | #var_pattern => { 209 | let #vars = and_then(#vars); 210 | #var_pattern 211 | }, 212 | _ => self 213 | } 214 | } 215 | 216 | pub fn #expect(self, msg: &str) -> (#types) { 217 | self.#unwrap_or_else(|| std::panic!("{}", msg)) 218 | } 219 | 220 | #ok_deprecation 221 | pub fn #ok(self) -> std::option::Option<(#types)> { 222 | self.#var_fn() 223 | } 224 | 225 | #ok_or_deprecation 226 | pub fn #ok_or(self, or: E) -> std::result::Result<(#types), E> { 227 | self.#var_or_fn(or) 228 | } 229 | 230 | #ok_or_else_deprecation 231 | pub fn #ok_or_else E>(self, or_else: F) -> std::result::Result<(#types), E> { 232 | self.#var_or_else_fn(or_else) 233 | } 234 | 235 | pub fn #or_else (#types)>(self, or_else: F) -> Self { 236 | match self { 237 | #var_pattern => #var_pattern, 238 | _ => { 239 | let #vars = or_else(); 240 | #var_pattern 241 | } 242 | } 243 | } 244 | 245 | pub fn #unwrap(self) -> (#types) { 246 | self.#unwrap_or_else(|| std::panic!()) 247 | } 248 | 249 | pub fn #unwrap_or(self, or: (#types)) -> (#types) { 250 | self.#unwrap_or_else(|| or) 251 | } 252 | 253 | pub fn #unwrap_or_else (#types)>(self, or_else: F) -> (#types) { 254 | match self { 255 | #var_pattern => (#vars), 256 | _ => or_else() 257 | } 258 | } 259 | }); 260 | } 261 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use darling::Error as DarlingError; 2 | use proc_macro::TokenStream; 3 | use syn::Error as SynError; 4 | 5 | pub type Result = std::result::Result; 6 | 7 | /// Custom error type for wrapping syn & darling errors as well as any other custom errors that may become necessary. 8 | pub enum Error { 9 | Syn(SynError), 10 | Darling(DarlingError), 11 | } 12 | 13 | impl Error { 14 | pub fn into_compile_error(self) -> TokenStream { 15 | match self { 16 | Error::Syn(err) => err.to_compile_error().into(), 17 | Error::Darling(err) => err.write_errors().into(), 18 | } 19 | } 20 | } 21 | 22 | impl From for Error { 23 | fn from(err: DarlingError) -> Self { 24 | Error::Darling(err) 25 | } 26 | } 27 | 28 | impl From for Error { 29 | fn from(err: SynError) -> Self { 30 | Error::Syn(err) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/idents.rs: -------------------------------------------------------------------------------- 1 | //! Parse or generate idents. 2 | use quote::format_ident; 3 | use syn::Ident; 4 | use uuid::Uuid; 5 | 6 | /// Declare a series of vars named by `operation` that contain an ident created 7 | /// by concatenating the stringified `operation`, and the passed in `ident`. 8 | /// # Examples 9 | /// ```ignore 10 | /// # use quote::format_ident; 11 | /// let foo = format_ident!("{}", "foo"); 12 | /// identify!(foo, [get, and]); 13 | /// // Expands to: 14 | /// let get = format_ident!("{}_{}", stringify!(get), to_snake_case(&foo.to_string())); 15 | /// let and = format_ident!("{}_{}", stringify!(and), to_snake_case(&foo.to_string())); 16 | /// // Which results in: 17 | /// assert_eq!(get.to_string(), "get_foo"); 18 | /// assert_eq!(and.to_string(), "and_foo"); 19 | /// ``` 20 | macro_rules! identify { 21 | ($ident:expr, [$($operation:ident$(,)*)*]) => { 22 | $( 23 | let $operation = format_ident!( 24 | "{}_{}", 25 | stringify!($operation), 26 | $ident 27 | ); 28 | )* 29 | }; 30 | } 31 | 32 | /// Generate the given number of unique and random idents and collect them into a vec. 33 | pub fn generate_idents(count: usize) -> Vec { 34 | let mut idents: Vec = vec![]; 35 | for _ in 0..count { 36 | idents.push(unique_ident()) 37 | } 38 | idents 39 | } 40 | 41 | /// Generate a valid, unique and random ident. 42 | pub fn unique_ident() -> Ident { 43 | format_ident!("ident_{}", Uuid::new_v4().to_simple().to_string()) 44 | } 45 | -------------------------------------------------------------------------------- /src/input.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Result; 2 | use darling::{ast::Fields, FromVariant}; 3 | use inflector::cases::snakecase::to_snake_case; 4 | use quote::format_ident; 5 | use syn::{Attribute, Ident, ItemEnum, Type, Visibility}; 6 | 7 | /// Struct for parsing relevant input to each variant of a variantly derived enum. 8 | #[derive(FromVariant, Debug)] 9 | #[darling(attributes(variantly))] 10 | pub struct VariantInput { 11 | pub ident: Ident, 12 | #[darling(default)] 13 | pub rename: Option, 14 | pub fields: Fields, 15 | } 16 | 17 | /// Struct for parsing relevant information from a variant field 18 | #[derive(FromField, Debug)] 19 | #[darling(forward_attrs)] 20 | pub struct FieldParsed { 21 | pub ident: Option, 22 | pub ty: Type, 23 | pub attrs: Vec, 24 | pub vis: Visibility, 25 | } 26 | 27 | /// Parsed input to each variant of a variantly derived enum. 28 | #[derive(Debug)] 29 | pub struct VariantParsed { 30 | pub ident: Ident, 31 | pub used_name: Ident, 32 | pub fields: Fields, 33 | } 34 | 35 | impl From for VariantParsed { 36 | fn from(variant: VariantInput) -> Self { 37 | let ident = &variant.ident; 38 | VariantParsed { 39 | used_name: format_ident!( 40 | "{}", 41 | to_snake_case(&variant.rename.unwrap_or_else(|| ident.clone()).to_string()) 42 | ), 43 | ident: variant.ident, 44 | fields: variant.fields, 45 | } 46 | } 47 | } 48 | 49 | /// Attempt to parse an ItemEnum into a vec of parsed variants. 50 | pub fn try_parse_variants(item_enum: &ItemEnum) -> Result> { 51 | item_enum 52 | .variants 53 | .iter() 54 | .map(|variant| { 55 | VariantInput::from_variant(variant) 56 | .map(VariantInput::into) 57 | .map_err(darling::Error::into) 58 | }) 59 | .collect() 60 | } 61 | 62 | /// Helper function for validation that requires comparing each variant with each other variant. 63 | /// Visits each pair only once and early returns on the first failure. 64 | pub fn validate_compare Result<()>>( 65 | variants: &Vec, 66 | validations: Vec, 67 | ) -> Result<()> { 68 | // Enumerate over the entire set. 69 | variants 70 | .as_slice() 71 | .iter() 72 | .enumerate() 73 | .try_for_each(|(index, variant_a)| -> Result<()> { 74 | // Iterate over variants not visited already by the primary iterator. 75 | variants[(index + 1)..variants.len()] 76 | .iter() 77 | .try_for_each(|variant_b| { 78 | // Run the current pair against all validation fns 79 | validations 80 | .iter() 81 | .try_for_each(|validation| validation(variant_a, variant_b)) 82 | }) 83 | }) 84 | } 85 | 86 | /// Validate that the used names for each variant will not cause naming conflicts. 87 | pub fn compare_used_names(a: &VariantParsed, b: &VariantParsed) -> Result<()> { 88 | if a.used_name == b.used_name { 89 | let message = format!("`{}` cannot be coerced into a unique & idiomatic snake_case function name as it would collide with the `{}` variant of the same Enum. \ 90 | use the following attribute on this or the conflicting variant to resolve: `#[variantly(rename = \"some_other_name\")]`", 91 | &a.ident, &b.ident); 92 | Err(syn::Error::new(a.ident.span(), message).into()) 93 | } else { 94 | Ok(()) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Debug helper methods for enum variants that are familiar from [`Option`] & [`Result`] such as [`Option::unwrap_or`] or [`Result::and_then`]. 3 | //! # Example 4 | //! ``` 5 | //! #[derive(variantly::Variantly)] 6 | //! enum Color { 7 | //! RGB(u8, u8, u8), 8 | //! HSV(u8, u8, u8), 9 | //! Grey(u8), 10 | //! FromOutOfSpace, 11 | //! #[variantly(rename = "darkness")] 12 | //! Black, 13 | //! } 14 | //! 15 | //! fn example() { 16 | //! let color = Color::HSV(123, 45, 67); 17 | //! 18 | //! // boolean helper method for determining variant: 19 | //! assert!(color.is_hsv()); 20 | //! assert!(!color.is_rgb()); 21 | //! 22 | //! // Get inner values: 23 | //! let (h, s, v) = color.unwrap_hsv(); 24 | //! assert_eq!((h, s, v), (123, 45, 67)); 25 | //! 26 | //! // Single values don't require tuple destructuring: 27 | //! let color = Color::Grey(128); 28 | //! let value = color.unwrap_grey(); 29 | //! assert_eq!(value, 128); 30 | //! 31 | //! // Alter inner value, only if hsv: 32 | //! let color = Color::HSV(111, 22, 33); 33 | //! let color = color.and_then_hsv(|(h, s, _)| (h, s, 100)); 34 | //! assert_eq!(color.unwrap_hsv(), (111, 22, 100)); 35 | //! 36 | //! // Safely unwrap with a fallback: 37 | //! let color = Color::RGB(255, 255, 0); 38 | //! let (r, g, b) = color.unwrap_or_rgb((0, 0, 0)); 39 | //! assert_eq!((r, g, b), (255, 255, 0)); 40 | //! // Since color is of the HSV variant, the default is not used. 41 | //! 42 | //! // Safely unwrap using the fallback 43 | //! let color = Color::FromOutOfSpace; 44 | //! let (r, g, b) = color.unwrap_or_rgb((0, 0, 0)); 45 | //! assert_eq!((r, g, b), (0, 0, 0)); 46 | //! 47 | //! // Convert into an Option 48 | //! let color = Color::RGB(0, 255, 255); 49 | //! let optional_rgb = color.rgb(); 50 | //! assert_eq!(Some((0, 255, 255)), optional_rgb); 51 | //! 52 | //! // Convert into a Result 53 | //! let color = Color::RGB(255, 0, 255); 54 | //! let result_rgb = color.rgb_or("Error: This is not an RGB variant!"); 55 | //! assert_eq!(Ok((255, 0, 255)), result_rgb); 56 | //! 57 | //! // Operations like this can also use their familiar `_else` versions: 58 | //! let color = Color::FromOutOfSpace; 59 | //! let result_rgb = color.rgb_or_else(|| Some("This is a computationally expensive error!")); 60 | //! assert!(result_rgb.is_err()); 61 | //! 62 | //! // The `#[variantly(rename = "darkness")]` attribute renames derived methods: 63 | //! let color = Color::Black; 64 | //! assert!(color.is_darkness()) 65 | //! } 66 | //! ``` 67 | //! # Derived Methods 68 | //! In the naming of all methods described here, replace the `{variant_name}` with the snake_case formatted name of the given variant. 69 | //! 70 | //! ## Option & Result Conversion 71 | //! Use the below methods to convert the enum into either an option or result: 72 | //! 73 | //! ### `pub fn {variant_name}(self) -> Option(...)` 74 | //! If the enum is of the given variant, returns a [`Some`] containing the inner variant value. Otherwise, return [`None`]. 75 | //! 76 | //! #### Example 77 | //! ``` 78 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 79 | //! # enum Color { 80 | //! # RGB(u8, u8, u8), 81 | //! # HSV(u8, u8, u8), 82 | //! # Grey(u8), 83 | //! # FromOutOfSpace, 84 | //! # #[variantly(rename = "darkness")] 85 | //! # Black, 86 | //! # } 87 | //! let color = Color::HSV(1,2,3); 88 | //! 89 | //! let option = color.hsv(); 90 | //! assert_eq!(Some((1, 2, 3)), option); 91 | //! 92 | //! let color = Color::FromOutOfSpace; 93 | //! assert_eq!(None, color.rgb()); 94 | //! ``` 95 | //! 96 | //! *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 97 | //! 98 | //! ### `pub fn {variant_name}_ref(&self) -> Option(&...)` 99 | //! If the enum is of the given variant, returns a `Some` containing a ref to the inner variant value. Otherwise, return None. 100 | //! 101 | //! #### Example 102 | //! ``` 103 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 104 | //! # enum Color { 105 | //! # RGB(u8, u8, u8), 106 | //! # HSV(u8, u8, u8), 107 | //! # Grey(u8), 108 | //! # FromOutOfSpace, 109 | //! # #[variantly(rename = "darkness")] 110 | //! # Black, 111 | //! # } 112 | //! 113 | //! let color = Color::HSV(1,2,3); 114 | //! 115 | //! let option = color.hsv_ref(); 116 | //! assert_eq!(Some((&1, &2, &3)), option); 117 | //! 118 | //! let color = Color::FromOutOfSpace; 119 | //! assert_eq!(None, color.rgb_ref()); 120 | //! ``` 121 | //! 122 | //! *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 123 | //! 124 | //! ### `pub fn {variant_name}_mut(&mut self) -> Option(&mut...)` 125 | //! If the enum is of the given variant, returns a `Some` containing a mutable ref to the inner variant value. Otherwise, return None. 126 | //! 127 | //! #### Example 128 | //! ``` 129 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 130 | //! # enum Color { 131 | //! # RGB(u8, u8, u8), 132 | //! # HSV(u8, u8, u8), 133 | //! # Grey(u8), 134 | //! # FromOutOfSpace, 135 | //! # #[variantly(rename = "darkness")] 136 | //! # Black, 137 | //! # } 138 | //! let mut color = Color::HSV(1,2,3); 139 | //! 140 | //! let option = color.hsv_mut(); 141 | //! assert_eq!(Some((&mut 1, &mut 2, &mut 3)), option); 142 | //! 143 | //! let mut color = Color::FromOutOfSpace; 144 | //! assert_eq!(None, color.rgb_mut()); 145 | //! ``` 146 | //! 147 | //! *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 148 | //! 149 | //! ### `pub fn {variant_name}_or(self, err: E) -> Result<(...), E>` 150 | //! If the enum is of the given variant, returns a [`Result::Ok`] containing the inner value. Otherwise, return [`Result::Err`] containing `err`. 151 | //! 152 | //! #### Example 153 | //! ``` 154 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 155 | //! # enum Color { 156 | //! # RGB(u8, u8, u8), 157 | //! # HSV(u8, u8, u8), 158 | //! # Grey(u8), 159 | //! # FromOutOfSpace, 160 | //! # #[variantly(rename = "darkness")] 161 | //! # Black, 162 | //! # } 163 | //! let color = Color::HSV(1,2,3); 164 | //! 165 | //! let result = color.hsv_or("Error: Not an HSV!"); 166 | //! assert_eq!(Ok((1, 2, 3)), result); 167 | //! 168 | //! let color = Color::FromOutOfSpace; 169 | //! let result = color.hsv_or("Error: Not an HSV!"); 170 | //! assert_eq!(Err("Error: Not an HSV!"), result); 171 | //! ``` 172 | //! 173 | //! *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 174 | //! 175 | //! ### `pub fn {variant_name}_ref_or(&self, err: E) -> Result<(&...), E>` 176 | //! If the enum is of the given variant, returns a `Result::Ok` containing a ref to the inner value. Otherwise, return `Result::Err` containing `err`. 177 | //! 178 | //! #### Example 179 | //! ``` 180 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 181 | //! # enum Color { 182 | //! # RGB(u8, u8, u8), 183 | //! # HSV(u8, u8, u8), 184 | //! # Grey(u8), 185 | //! # FromOutOfSpace, 186 | //! # #[variantly(rename = "darkness")] 187 | //! # Black, 188 | //! # } 189 | //! let color = Color::HSV(1,2,3); 190 | //! 191 | //! let result = color.hsv_ref_or("Error: Not an HSV!"); 192 | //! assert_eq!(Ok((&1, &2, &3)), result); 193 | //! 194 | //! let color = Color::FromOutOfSpace; 195 | //! let result = color.hsv_ref_or("Error: Not an HSV!"); 196 | //! assert_eq!(Err("Error: Not an HSV!"), result); 197 | //! ``` 198 | //! 199 | //! *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 200 | //! 201 | //! ### `pub fn {variant_name}_mut_or(&mut self, err: E) -> Result<(&mut...), E>` 202 | //! If the enum is of the given variant, returns a `Result::Ok` containing a mutable ref to the inner value. Otherwise, return `Result::Err` containing `err`. 203 | //! 204 | //! #### Example 205 | //! ``` 206 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 207 | //! # enum Color { 208 | //! # RGB(u8, u8, u8), 209 | //! # HSV(u8, u8, u8), 210 | //! # Grey(u8), 211 | //! # FromOutOfSpace, 212 | //! # #[variantly(rename = "darkness")] 213 | //! # Black, 214 | //! # } 215 | //! let mut color = Color::HSV(1,2,3); 216 | //! 217 | //! let result = color.hsv_mut_or("Error: Not an HSV!"); 218 | //! assert_eq!(Ok((&mut 1, &mut 2, &mut 3)), result); 219 | //! 220 | //! let mut color = Color::FromOutOfSpace; 221 | //! let result = color.hsv_mut_or("Error: Not an HSV!"); 222 | //! assert_eq!(Err("Error: Not an HSV!"), result); 223 | //! ``` 224 | //! 225 | //! *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 226 | //! 227 | //! ### `pub fn {variant_name}_or_else E>(self, f: F) -> Result<(...), E>` 228 | //! If the enum is of the given variant, returns a [`Result::Ok`] containing the inner variant value. Otherwise, calls `f` to calculate a [`Result::Err`]. 229 | //! 230 | //! #### Example 231 | //! ``` 232 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 233 | //! # enum Color { 234 | //! # RGB(u8, u8, u8), 235 | //! # HSV(u8, u8, u8), 236 | //! # Grey(u8), 237 | //! # FromOutOfSpace, 238 | //! # #[variantly(rename = "darkness")] 239 | //! # Black, 240 | //! # } 241 | //! let color = Color::HSV(1,2,3); 242 | //! 243 | //! let result = color.hsv_or_else(|| "This is an expensive error to create."); 244 | //! assert_eq!(Ok((1, 2, 3)), result); 245 | //! 246 | //! let color = Color::FromOutOfSpace; 247 | //! let result = color.hsv_or_else(|| "This is an expensive error to create."); 248 | //! assert_eq!(Err("This is an expensive error to create."), result); 249 | //! ``` 250 | //! 251 | //! *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 252 | //! 253 | //! ### `pub fn {variant_name}_ref_or_else E>(&self, f: F) -> Result<(&...), E>` 254 | //! If the enum is of the given variant, returns a `Result::Ok` containing a ref to the inner variant value. Otherwise, calls `f` to calculate a `Result::Err`. 255 | //! 256 | //! #### Example 257 | //! ``` 258 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 259 | //! # enum Color { 260 | //! # RGB(u8, u8, u8), 261 | //! # HSV(u8, u8, u8), 262 | //! # Grey(u8), 263 | //! # FromOutOfSpace, 264 | //! # #[variantly(rename = "darkness")] 265 | //! # Black, 266 | //! # } 267 | //! let color = Color::HSV(1,2,3); 268 | //! 269 | //! let result = color.hsv_ref_or_else(|| "This is an expensive error to create."); 270 | //! assert_eq!(Ok((&1, &2, &3)), result); 271 | //! 272 | //! let color = Color::FromOutOfSpace; 273 | //! let result = color.hsv_ref_or_else(|| "This is an expensive error to create."); 274 | //! assert_eq!(Err("This is an expensive error to create."), result); 275 | //! ``` 276 | //! 277 | //! *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 278 | //! 279 | //! ### `pub fn {variant_name}_mut_or_else E>(&mut self, f: F) -> Result<(&mut...), E>` 280 | //! If the enum is of the given variant, returns a `Result::Ok` containing a mut ref to the inner variant value. Otherwise, calls `f` to calculate a `Result::Err`. 281 | //! 282 | //! #### Example 283 | //! ``` 284 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 285 | //! # enum Color { 286 | //! # RGB(u8, u8, u8), 287 | //! # HSV(u8, u8, u8), 288 | //! # Grey(u8), 289 | //! # FromOutOfSpace, 290 | //! # #[variantly(rename = "darkness")] 291 | //! # Black, 292 | //! # } 293 | //! let mut color = Color::HSV(1,2,3); 294 | //! 295 | //! let result = color.hsv_mut_or_else(|| "This is an expensive error to create."); 296 | //! assert_eq!(Ok((&mut 1, &mut 2, &mut 3)), result); 297 | //! 298 | //! let mut color = Color::FromOutOfSpace; 299 | //! let result = color.hsv_mut_or_else(|| "This is an expensive error to create."); 300 | //! assert_eq!(Err("This is an expensive error to create."), result); 301 | //! ``` 302 | //! 303 | //! *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 304 | //! 305 | //! ## Accessing Inner Values 306 | //! Use the below methods to easily access the inner value of a given variant. 307 | //! 308 | //! ### `pub fn expect_{variant_name}(self, msg: &str) -> (...)` 309 | //! Returns the contained value. 310 | //! 311 | //! #### Panics 312 | //! Panics if the enum is not of the given variant with the custom message `msg`. 313 | //! 314 | //! #### Example 315 | //! ``` 316 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 317 | //! # enum Color { 318 | //! # RGB(u8, u8, u8), 319 | //! # HSV(u8, u8, u8), 320 | //! # Grey(u8), 321 | //! # FromOutOfSpace, 322 | //! # #[variantly(rename = "darkness")] 323 | //! # Black, 324 | //! # } 325 | //! let color_a = Color::HSV(1,2,3); 326 | //! let color_b = Color::Grey(10); 327 | //! 328 | //! let (h, s, v) = color_a.expect_hsv("This should be an hsv"); 329 | //! assert_eq!((h, s, v), (1, 2, 3)); 330 | //! 331 | //! let grey = color_b.expect_grey("This should be grey"); 332 | //! assert_eq!(grey, 10); 333 | //! ``` 334 | //! 335 | //! *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 336 | //! 337 | //! ### `pub fn unwrap_{variant_name}(self) -> (...)` 338 | //! Returns the contained value. 339 | //! 340 | //! #### Panics 341 | //! Panics if the enum is not of the given variant. 342 | //! 343 | //! #### Example 344 | //! ``` 345 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 346 | //! # enum Color { 347 | //! # RGB(u8, u8, u8), 348 | //! # HSV(u8, u8, u8), 349 | //! # Grey(u8), 350 | //! # FromOutOfSpace, 351 | //! # #[variantly(rename = "darkness")] 352 | //! # Black, 353 | //! # } 354 | //! let color_a = Color::HSV(1,2,3); 355 | //! let color_b = Color::Grey(10); 356 | //! 357 | //! let (h, s, v) = color_a.unwrap_hsv(); 358 | //! assert_eq!((h, s, v), (1, 2, 3)); 359 | //! 360 | //! let grey = color_b.unwrap_grey(); 361 | //! assert_eq!(grey, 10); 362 | //! ``` 363 | //! 364 | //! *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 365 | //! 366 | //! ### `pub fn unwrap_or_{variant_name}(self, fallback: (...)) -> (...)` 367 | //! Returns the contained value if the enum is of the given variant, otherwise returns the provided `fallback`. 368 | //! 369 | //! #### Example 370 | //! ``` 371 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 372 | //! # enum Color { 373 | //! # RGB(u8, u8, u8), 374 | //! # HSV(u8, u8, u8), 375 | //! # Grey(u8), 376 | //! # FromOutOfSpace, 377 | //! # #[variantly(rename = "darkness")] 378 | //! # Black, 379 | //! # } 380 | //! let color_a = Color::HSV(1,2,3); 381 | //! let color_b = Color::Grey(10); 382 | //! 383 | //! let (h, s, v) = color_a.unwrap_or_hsv((4, 5, 6)); 384 | //! assert_eq!((h, s, v), (1, 2, 3)); 385 | //! 386 | //! let color = color_b.unwrap_or_rgb((4, 5, 6)); 387 | //! assert_eq!(color, (4, 5, 6)); 388 | //! ``` 389 | //! 390 | //! *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 391 | //! 392 | //! ### `pub fn unwrap_or_else_{variant_name} (...)>(self, f: F) -> (...)` 393 | //! Returns the contained value if the enum is of the given variant, otherwise computes a fallback from `f`. 394 | //! 395 | //! #### Example 396 | //! ``` 397 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 398 | //! # enum Color { 399 | //! # RGB(u8, u8, u8), 400 | //! # HSV(u8, u8, u8), 401 | //! # Grey(u8), 402 | //! # FromOutOfSpace, 403 | //! # #[variantly(rename = "darkness")] 404 | //! # Black, 405 | //! # } 406 | //! let color_a = Color::HSV(1,2,3); 407 | //! let color_b = Color::Grey(10); 408 | //! 409 | //! let (h, s, v) = color_a.unwrap_or_else_hsv(|| (4,5,6)); 410 | //! assert_eq!((h, s, v), (1, 2, 3)); 411 | //! 412 | //! let (h, s, v) = color_b.unwrap_or_else_hsv(|| (4,5,6)); 413 | //! assert_eq!((h, s, v), (4, 5, 6)); 414 | //! ``` 415 | //! 416 | //! *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 417 | //! 418 | //! ## Testing Variant Type 419 | //! Use the below methods to test whether a variant is of the given type. 420 | //! 421 | //! ### `pub fn is_{variant_name}(self) -> bool` 422 | //! Returns `true` if the enum is of the given variant. 423 | //! 424 | //! #### Example 425 | //! ``` 426 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 427 | //! # enum Color { 428 | //! # RGB(u8, u8, u8), 429 | //! # HSV(u8, u8, u8), 430 | //! # Grey(u8), 431 | //! # FromOutOfSpace, 432 | //! # #[variantly(rename = "darkness")] 433 | //! # Black, 434 | //! # } 435 | //! let color = Color::FromOutOfSpace; 436 | //! assert!(color.is_from_out_of_space()); 437 | //! ``` 438 | //! 439 | //! *Note: Available for all variant types* 440 | //! 441 | //! ### `pub fn is_not_{variant_name}(self) -> bool` 442 | //! Returns `true` if the enum is *not* of the given variant. 443 | //! 444 | //! #### Example 445 | //! ``` 446 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 447 | //! # enum Color { 448 | //! # RGB(u8, u8, u8), 449 | //! # HSV(u8, u8, u8), 450 | //! # Grey(u8), 451 | //! # FromOutOfSpace, 452 | //! # #[variantly(rename = "darkness")] 453 | //! # Black, 454 | //! # } 455 | //! let color = Color::HSV(1,2,3); 456 | //! assert!(color.is_not_rgb()); 457 | //! ``` 458 | //! 459 | //! *Note: Available for all variant types* 460 | //! 461 | //! ## Compare & Process Specific Variant 462 | //! Use the below to process and compare a specific enum variant. 463 | //! 464 | //! ### `pub fn and_{variant_name}(self, enum_b: GivenEnum) -> GivenEnum` 465 | //! Returns `enum_b` if both self and `enum_b` are of the given variant. Otherwise returns `self`. 466 | //! 467 | //! #### Example 468 | //! ``` 469 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 470 | //! # enum Color { 471 | //! # RGB(u8, u8, u8), 472 | //! # HSV(u8, u8, u8), 473 | //! # Grey(u8), 474 | //! # FromOutOfSpace, 475 | //! # #[variantly(rename = "darkness")] 476 | //! # Black, 477 | //! # } 478 | //! let color_a = Color::HSV(1,2,3); 479 | //! let color_b = Color::HSV(4,5,6); 480 | //! let and = color_a.and_hsv(color_b); 481 | //! assert_eq!( 482 | //! and, 483 | //! Color::HSV(4,5,6), 484 | //! ); 485 | //! ``` 486 | //! 487 | //! *Available for all variant types* 488 | //! 489 | //! ### `pub fn and_then_{variant_name} (...)>(self, f: F) -> Self` 490 | //! Returns the enum as is if it is not of the given variant, otherwise calls `f` with the wrapped value and returns the result. 491 | //! 492 | //! #### Example 493 | //! ``` 494 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 495 | //! # enum Color { 496 | //! # RGB(u8, u8, u8), 497 | //! # HSV(u8, u8, u8), 498 | //! # Grey(u8), 499 | //! # FromOutOfSpace, 500 | //! # #[variantly(rename = "darkness")] 501 | //! # Black, 502 | //! # } 503 | //! let color_a = Color::HSV(1,2,3); 504 | //! 505 | //! let and = color_a.and_then_hsv(|(h, s, _)| (h, s, 4)); 506 | //! assert_eq!( 507 | //! and, 508 | //! Color::HSV(1, 2, 4), 509 | //! ); 510 | //! ``` 511 | //! 512 | //! *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 513 | //! 514 | //! ### `pub fn or_{variant_name}(self, enum_b: GivenEnum) -> GivenEnum` 515 | //! Returns `self` if it is of the given variant, otherwise returns `enum_b`. 516 | //! 517 | //! #### Example 518 | //! ``` 519 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 520 | //! # enum Color { 521 | //! # RGB(u8, u8, u8), 522 | //! # HSV(u8, u8, u8), 523 | //! # Grey(u8), 524 | //! # FromOutOfSpace, 525 | //! # #[variantly(rename = "darkness")] 526 | //! # Black, 527 | //! # } 528 | //! let color_a = Color::HSV(1,2,3); 529 | //! let color_b = Color::RGB(4,5,6); 530 | //! let or = color_a.or_rgb(color_b); 531 | //! assert_eq!( 532 | //! or, 533 | //! Color::RGB(4,5,6), 534 | //! ); 535 | //! ``` 536 | //! 537 | //! *Available for all variant types* 538 | //! 539 | //! ### `pub fn or_else_{variant_name} (...)>(self, f: F) -> Self {` 540 | //! Returns `self` if it is of the given variant, otherwise calls `f` and returns the result. 541 | //! 542 | //! #### Example 543 | //! ``` 544 | //! # #[derive(variantly::Variantly, Debug, PartialEq)] 545 | //! # enum Color { 546 | //! # RGB(u8, u8, u8), 547 | //! # HSV(u8, u8, u8), 548 | //! # Grey(u8), 549 | //! # FromOutOfSpace, 550 | //! # #[variantly(rename = "darkness")] 551 | //! # Black, 552 | //! # } 553 | //! let color = Color::HSV(1,2,3); 554 | //! let color = color.or_else_rgb(|| (4,5,6)); 555 | //! assert_eq!( 556 | //! color, 557 | //! Color::RGB(4,5,6), 558 | //! ); 559 | //! ``` 560 | //! 561 | //! *Note: Available only for tuple-style variants such as Color::RGB(200, 40, 180), or Color::Grey(10)* 562 | //! 563 | //! # Renaming Methods 564 | //! The `variantly` attribute may be placed on a variant in order to customize the resulting method names. The value set against `rename` inside the attribute will be used in place of the snake_cased variant name when constructing derived method names. 565 | //! ``` 566 | //! #[derive(variantly::Variantly)] 567 | //! enum SomeEnum { 568 | //! #[variantly(rename = "variant_a")] 569 | //! SomeVariantWithALongName(String), 570 | //! VariantB, 571 | //! } 572 | //! 573 | //! let variant = SomeEnum::SomeVariantWithALongName(String::from("Hello")); 574 | //! assert!(variant.is_variant_a()); 575 | //! ``` 576 | //! Methods associated with `SomeVariantWithALongName` will now be accessible only with the `variant_a` 577 | //! suffix, such as `.unwrap_or_else_variant_a()`. This can help control overly verbose fn names. 578 | //! Note that the input to `rename` is used as is and is not coerced into snake_case. 579 | //! 580 | //! The above is also relevant when two variant names would expand to create conflicting method names: 581 | //! ``` 582 | //! #[derive(variantly::Variantly)] 583 | //! enum SomeEnum { 584 | //! #[variantly(rename = "capital")] 585 | //! ABC, 586 | //! #[variantly(rename = "lower")] 587 | //! abc, 588 | //! } 589 | //! ``` 590 | //! Without the `rename` attribute in the above, both variants would create conflicting methods such as `.is_abc()` due to the coercion to snake_case. 591 | //! This is avoided by using the rename input to create meaningful and unique fn names. 592 | //! 593 | //! #### License 594 | //! 595 | //! 596 | //! Licensed under MIT license. 597 | //! 598 | //! 599 | //!
600 | //! 601 | //! 602 | //! Unless you explicitly state otherwise, any contribution intentionally submitted 603 | //! for inclusion in this crate shall be licensed as above, without any additional terms or conditions. 604 | //! 605 | 606 | #[macro_use] 607 | extern crate darling; 608 | extern crate proc_macro; 609 | 610 | #[macro_use] 611 | mod idents; 612 | 613 | mod derive; 614 | mod error; 615 | mod input; 616 | 617 | use derive::derive_variantly_fns; 618 | use proc_macro::TokenStream; 619 | use syn::{parse_macro_input, ItemEnum}; 620 | 621 | /// The `Variantly` derive macro. See [the module level documentation](self) for more information. 622 | #[proc_macro_derive(Variantly, attributes(variantly))] 623 | pub fn variantly(input: TokenStream) -> TokenStream { 624 | let item_enum = parse_macro_input!(input as ItemEnum); 625 | derive_variantly_fns(item_enum).unwrap_or_else(|err| err.into_compile_error()) 626 | } 627 | -------------------------------------------------------------------------------- /tests/derived_and.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, OtherUnit, Tuple, Unit}, 5 | }; 6 | 7 | #[test] 8 | fn single_value_tuple() { 9 | // True True 10 | assert_eq!(Int(123).and_int(Int(456)).unwrap_int(), 456); 11 | 12 | // True False 13 | assert_eq!(Int(123).and_int(Unit).unwrap_int(), 123); 14 | 15 | // False True 16 | assert!(Unit.and_int(Int(123)).is_unit()); 17 | 18 | // False False 19 | assert!(Unit.and_int(OtherUnit).is_unit()); 20 | } 21 | 22 | fn tuple(num: u128) -> TestEnum { 23 | Tuple(num.to_string(), num) 24 | } 25 | #[test] 26 | fn multi_value_tuple() { 27 | // True True 28 | assert_eq!( 29 | tuple(123).and_tuple(tuple(456)).unwrap_tuple(), 30 | ("456".into(), 456) 31 | ); 32 | 33 | // True False 34 | assert_eq!( 35 | tuple(123).and_tuple(Unit).unwrap_tuple(), 36 | ("123".into(), 123) 37 | ); 38 | 39 | // False True 40 | assert!(Unit.and_tuple(tuple(123)).is_unit()); 41 | 42 | // False False 43 | assert!(Unit.and_tuple(Unit).is_unit()); 44 | } 45 | -------------------------------------------------------------------------------- /tests/derived_and_then.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, Unit}, 5 | }; 6 | 7 | #[test] 8 | fn single_value_tuple() { 9 | let and_then = |val| val + 100; 10 | 11 | // Match 12 | assert_eq!(Int(123).and_then_int(and_then).unwrap_int(), 223); 13 | 14 | // Non-Match 15 | assert!(Unit.and_then_int(and_then).is_unit()); 16 | } 17 | 18 | #[test] 19 | fn multi_value_tuple() { 20 | let and_then = |(word, num)| (format!("{}{}", word, word), num + num); 21 | 22 | // Match 23 | assert_eq!( 24 | TestEnum::new_tuple(123) 25 | .and_then_tuple(and_then) 26 | .unwrap_tuple(), 27 | ("123123".into(), 246) 28 | ); 29 | 30 | // Non-Match 31 | assert!(Unit.and_then_tuple(and_then).is_unit()); 32 | } 33 | -------------------------------------------------------------------------------- /tests/derived_expect.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, Unit}, 5 | }; 6 | 7 | #[test] 8 | fn single_value_tuple() { 9 | assert_eq!(Int(123).expect_int("This should have been an int"), 123); 10 | } 11 | 12 | #[test] 13 | #[should_panic(expected = "This should have been an int")] 14 | fn single_value_tuple_panic() { 15 | Unit.expect_int("This should have been an int"); 16 | } 17 | 18 | #[test] 19 | fn multi_value_tuple() { 20 | assert_eq!( 21 | TestEnum::new_tuple(123).expect_tuple("This should have been a tuple"), 22 | ("123".into(), 123) 23 | ); 24 | } 25 | 26 | #[test] 27 | #[should_panic(expected = "This should have been a tuple")] 28 | fn multi_value_tuple_panic() { 29 | Unit.expect_tuple("This should have been a tuple"); 30 | } 31 | -------------------------------------------------------------------------------- /tests/derived_is_and_is_not.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{StructLike, Tuple, Unit}, 5 | }; 6 | 7 | #[test] 8 | fn unit() { 9 | let enm = Unit; 10 | assert!(enm.is_unit()); 11 | assert!(!enm.is_not_unit()); 12 | 13 | assert!(enm.is_not_other_unit()); 14 | assert!(!enm.is_other_unit()); 15 | 16 | assert!(enm.is_not_string()); 17 | assert!(!enm.is_string()); 18 | 19 | assert!(enm.is_not_tuple()); 20 | assert!(!enm.is_tuple()); 21 | 22 | assert!(enm.is_not_struct_like()); 23 | assert!(!enm.is_struct_like()); 24 | } 25 | 26 | #[test] 27 | fn tuple() { 28 | let enm = Tuple("Test".into(), 123); 29 | assert!(enm.is_tuple()); 30 | assert!(!enm.is_not_tuple()); 31 | 32 | assert!(enm.is_not_unit()); 33 | assert!(!enm.is_unit()); 34 | 35 | assert!(enm.is_not_other_unit()); 36 | assert!(!enm.is_other_unit()); 37 | 38 | assert!(enm.is_not_string()); 39 | assert!(!enm.is_string()); 40 | 41 | assert!(enm.is_not_struct_like()); 42 | assert!(!enm.is_struct_like()); 43 | } 44 | 45 | #[test] 46 | fn struct_like() { 47 | let enm: TestEnum = StructLike { value: 123 }; 48 | assert!(enm.is_struct_like()); 49 | assert!(!enm.is_not_struct_like()); 50 | 51 | assert!(enm.is_not_unit()); 52 | assert!(!enm.is_unit()); 53 | 54 | assert!(enm.is_not_other_unit()); 55 | assert!(!enm.is_other_unit()); 56 | 57 | assert!(enm.is_not_string()); 58 | assert!(!enm.is_string()); 59 | 60 | assert!(enm.is_not_tuple()); 61 | assert!(!enm.is_tuple()); 62 | } 63 | -------------------------------------------------------------------------------- /tests/derived_mut.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, Unit}, 5 | }; 6 | 7 | #[test] 8 | fn single_value_tuple() { 9 | // Match 10 | assert_eq!(Int(123).int_mut(), Some(&mut 123)); 11 | 12 | // Non-Match 13 | assert_eq!(Unit.int_mut(), None); 14 | } 15 | 16 | #[test] 17 | fn multi_value_tuple() { 18 | // Match 19 | assert_eq!( 20 | TestEnum::new_tuple(123).tuple_mut(), 21 | Some((&mut "123".into(), &mut 123)) 22 | ); 23 | 24 | // Non-Match 25 | assert_eq!(Unit.tuple_mut(), None); 26 | } 27 | -------------------------------------------------------------------------------- /tests/derived_ok.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, Unit}, 5 | }; 6 | 7 | #[test] 8 | #[allow(deprecated)] // Test deprecated functions for back-compat. Remove in 1.0.0 or next pre-stable minor bump. 9 | fn single_value_tuple_deprecated() { 10 | // Match 11 | assert_eq!(Int(123).ok_int(), Some(123)); 12 | 13 | // Non-Match 14 | assert_eq!(Unit.ok_int(), None); 15 | 16 | // Match 17 | assert_eq!(Int(123).int(), Some(123)); 18 | 19 | // Non-Match 20 | assert_eq!(Unit.int(), None); 21 | } 22 | 23 | #[test] 24 | #[allow(deprecated)] // Test deprecated functions for back-compat. Remove in 1.0.0 or next pre-stable minor bump. 25 | fn multi_value_tuple_deprecated() { 26 | // Match 27 | assert_eq!( 28 | TestEnum::new_tuple(123).ok_tuple(), 29 | Some(("123".into(), 123)) 30 | ); 31 | 32 | // Non-Match 33 | assert_eq!(Unit.ok_tuple(), None); 34 | } 35 | 36 | #[test] 37 | fn single_value_tuple() { 38 | // Match 39 | assert_eq!(Int(123).int(), Some(123)); 40 | 41 | // Non-Match 42 | assert_eq!(Unit.int(), None); 43 | 44 | // Match 45 | assert_eq!(Int(123).int(), Some(123)); 46 | 47 | // Non-Match 48 | assert_eq!(Unit.int(), None); 49 | } 50 | 51 | #[test] 52 | fn multi_value_tuple() { 53 | // Match 54 | assert_eq!(TestEnum::new_tuple(123).tuple(), Some(("123".into(), 123))); 55 | 56 | // Non-Match 57 | assert_eq!(Unit.tuple(), None); 58 | } 59 | -------------------------------------------------------------------------------- /tests/derived_ok_mut_or.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, Unit}, 5 | }; 6 | #[test] 7 | fn single_value_tuple() { 8 | // Match 9 | assert_eq!(Int(123).int_mut_or("ERR").unwrap(), &mut 123); 10 | 11 | // Non-Match 12 | assert_eq!(Unit.int_mut_or("ERR").unwrap_err(), "ERR"); 13 | } 14 | 15 | #[test] 16 | fn multi_value_tuple() { 17 | // Match 18 | assert_eq!( 19 | TestEnum::new_tuple(123).tuple_mut_or("ERR").unwrap(), 20 | (&mut "123".into(), &mut 123) 21 | ); 22 | 23 | // Non-Match 24 | assert_eq!(Unit.tuple_mut_or("ERR").unwrap_err(), "ERR"); 25 | } 26 | -------------------------------------------------------------------------------- /tests/derived_ok_mut_or_else.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, Unit}, 5 | }; 6 | 7 | #[test] 8 | fn single_value_tuple() { 9 | // Match 10 | assert_eq!(Int(123).int_mut_or_else(|| "ERR").unwrap(), &mut 123); 11 | 12 | // Non-Match 13 | assert_eq!(Unit.int_mut_or_else(|| "ERR").unwrap_err(), "ERR"); 14 | } 15 | 16 | #[test] 17 | fn multi_value_tuple() { 18 | // Match 19 | assert_eq!( 20 | TestEnum::new_tuple(123) 21 | .tuple_mut_or_else(|| "ERR") 22 | .unwrap(), 23 | (&mut "123".into(), &mut 123) 24 | ); 25 | 26 | // Non-Match 27 | assert_eq!(Unit.tuple_mut_or_else(|| "ERR").unwrap_err(), "ERR"); 28 | } 29 | -------------------------------------------------------------------------------- /tests/derived_ok_or.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, Unit}, 5 | }; 6 | 7 | #[test] 8 | #[allow(deprecated)] // Test deprecated functions for back-compat. Remove in 1.0.0 or next pre-stable minor bump. 9 | fn single_value_tuple_deprecated() { 10 | // Match 11 | assert_eq!(Int(123).ok_or_int("ERR").unwrap(), 123); 12 | 13 | // Non-Match 14 | assert_eq!(Unit.ok_or_int("ERR").unwrap_err(), "ERR"); 15 | } 16 | 17 | #[test] 18 | #[allow(deprecated)] // Test deprecated functions for back-compat. Remove in 1.0.0 or next pre-stable minor bump. 19 | fn multi_value_tuple_deprecated() { 20 | // Match 21 | assert_eq!( 22 | TestEnum::new_tuple(123).ok_or_tuple("ERR").unwrap(), 23 | ("123".into(), 123) 24 | ); 25 | 26 | // Non-Match 27 | assert_eq!(Unit.ok_or_tuple("ERR").unwrap_err(), "ERR"); 28 | } 29 | 30 | #[test] 31 | fn single_value_tuple() { 32 | // Match 33 | assert_eq!(Int(123).int_or("ERR").unwrap(), 123); 34 | 35 | // Non-Match 36 | assert_eq!(Unit.int_or("ERR").unwrap_err(), "ERR"); 37 | } 38 | 39 | #[test] 40 | fn multi_value_tuple() { 41 | // Match 42 | assert_eq!( 43 | TestEnum::new_tuple(123).tuple_or("ERR").unwrap(), 44 | ("123".into(), 123) 45 | ); 46 | 47 | // Non-Match 48 | assert_eq!(Unit.tuple_or("ERR").unwrap_err(), "ERR"); 49 | } 50 | -------------------------------------------------------------------------------- /tests/derived_ok_or_else.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, Unit}, 5 | }; 6 | 7 | #[test] 8 | #[allow(deprecated)] // Test deprecated functions for back-compat. Remove in 1.0.0 or next pre-stable minor bump. 9 | fn single_value_tuple_deprecated() { 10 | // Match 11 | assert_eq!(Int(123).ok_or_else_int(|| "ERR").unwrap(), 123); 12 | 13 | // Non-Match 14 | assert_eq!(Unit.ok_or_else_int(|| "ERR").unwrap_err(), "ERR"); 15 | } 16 | 17 | #[test] 18 | #[allow(deprecated)] // Test deprecated functions for back-compat. Remove in 1.0.0 or next pre-stable minor bump. 19 | fn multi_value_tuple_deprecated() { 20 | // Match 21 | assert_eq!( 22 | TestEnum::new_tuple(123).ok_or_else_tuple(|| "ERR").unwrap(), 23 | ("123".into(), 123) 24 | ); 25 | 26 | // Non-Match 27 | assert_eq!(Unit.ok_or_else_tuple(|| "ERR").unwrap_err(), "ERR"); 28 | } 29 | 30 | #[test] 31 | fn single_value_tuple() { 32 | // Match 33 | assert_eq!(Int(123).int_or_else(|| "ERR").unwrap(), 123); 34 | 35 | // Non-Match 36 | assert_eq!(Unit.int_or_else(|| "ERR").unwrap_err(), "ERR"); 37 | } 38 | 39 | #[test] 40 | fn multi_value_tuple() { 41 | // Match 42 | assert_eq!( 43 | TestEnum::new_tuple(123).tuple_or_else(|| "ERR").unwrap(), 44 | ("123".into(), 123) 45 | ); 46 | 47 | // Non-Match 48 | assert_eq!(Unit.tuple_or_else(|| "ERR").unwrap_err(), "ERR"); 49 | } 50 | -------------------------------------------------------------------------------- /tests/derived_ok_ref_or.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, Unit}, 5 | }; 6 | #[test] 7 | fn single_value_tuple() { 8 | // Match 9 | assert_eq!(Int(123).int_ref_or("ERR").unwrap(), &123); 10 | 11 | // Non-Match 12 | assert_eq!(Unit.int_ref_or("ERR").unwrap_err(), "ERR"); 13 | } 14 | 15 | #[test] 16 | fn multi_value_tuple() { 17 | // Match 18 | assert_eq!( 19 | TestEnum::new_tuple(123).tuple_ref_or("ERR").unwrap(), 20 | (&"123".into(), &123) 21 | ); 22 | 23 | // Non-Match 24 | assert_eq!(Unit.tuple_ref_or("ERR").unwrap_err(), "ERR"); 25 | } 26 | -------------------------------------------------------------------------------- /tests/derived_ok_ref_or_else.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, Unit}, 5 | }; 6 | 7 | #[test] 8 | fn single_value_tuple() { 9 | // Match 10 | assert_eq!(Int(123).int_ref_or_else(|| "ERR").unwrap(), &123); 11 | 12 | // Non-Match 13 | assert_eq!(Unit.int_ref_or_else(|| "ERR").unwrap_err(), "ERR"); 14 | } 15 | 16 | #[test] 17 | fn multi_value_tuple() { 18 | // Match 19 | assert_eq!( 20 | TestEnum::new_tuple(123) 21 | .tuple_ref_or_else(|| "ERR") 22 | .unwrap(), 23 | (&"123".into(), &123) 24 | ); 25 | 26 | // Non-Match 27 | assert_eq!(Unit.tuple_ref_or_else(|| "ERR").unwrap_err(), "ERR"); 28 | } 29 | -------------------------------------------------------------------------------- /tests/derived_or.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, OtherUnit, Tuple, Unit}, 5 | }; 6 | 7 | #[test] 8 | fn single_value_tuple() { 9 | // True True 10 | assert_eq!(Int(123).or_int(Int(456)).unwrap_int(), 123); 11 | 12 | // True False 13 | assert_eq!(Int(123).or_int(Unit).unwrap_int(), 123); 14 | 15 | // False True 16 | assert_eq!(Unit.or_int(Int(123)).unwrap_int(), 123); 17 | 18 | // False False 19 | assert!(Unit.or_int(OtherUnit).is_other_unit()); 20 | } 21 | 22 | fn tuple(num: u128) -> TestEnum { 23 | Tuple(num.to_string(), num) 24 | } 25 | #[test] 26 | fn multi_value_tuple() { 27 | // True True 28 | assert_eq!( 29 | tuple(123).or_tuple(tuple(456)).unwrap_tuple(), 30 | ("123".into(), 123) 31 | ); 32 | 33 | // True False 34 | assert_eq!( 35 | tuple(123).or_tuple(Unit).unwrap_tuple(), 36 | ("123".into(), 123) 37 | ); 38 | 39 | // False True 40 | assert_eq!( 41 | Unit.or_tuple(tuple(123)).unwrap_tuple(), 42 | ("123".into(), 123) 43 | ); 44 | 45 | // False False 46 | assert!(Unit.or_tuple(Unit).is_unit()); 47 | } 48 | -------------------------------------------------------------------------------- /tests/derived_or_else.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, Unit}, 5 | }; 6 | 7 | #[test] 8 | fn single_value_tuple() { 9 | // Match 10 | assert_eq!(Int(123).or_else_int(|| 456).unwrap_int(), 123); 11 | 12 | // Non-Match 13 | assert_eq!(Unit.or_else_int(|| 456).unwrap_int(), 456); 14 | } 15 | 16 | #[test] 17 | fn multi_value_tuple() { 18 | // Match 19 | assert_eq!( 20 | TestEnum::new_tuple(123).unwrap_or_else_tuple(|| ("456".into(), 456)), 21 | ("123".into(), 123) 22 | ); 23 | 24 | // Non-Match 25 | assert_eq!( 26 | Unit.unwrap_or_else_tuple(|| ("456".into(), 456)), 27 | ("456".into(), 456) 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /tests/derived_ref.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, Unit}, 5 | }; 6 | 7 | #[test] 8 | fn single_value_tuple() { 9 | // Match 10 | assert_eq!(Int(123).int_ref(), Some(&123)); 11 | 12 | // Non-Match 13 | assert_eq!(Unit.int_ref(), None); 14 | } 15 | 16 | #[test] 17 | fn multi_value_tuple() { 18 | // Match 19 | assert_eq!( 20 | TestEnum::new_tuple(123).tuple_ref(), 21 | Some((&"123".into(), &123)) 22 | ); 23 | 24 | // Non-Match 25 | assert_eq!(Unit.tuple_ref(), None); 26 | } 27 | -------------------------------------------------------------------------------- /tests/derived_unwrap.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, Unit}, 5 | }; 6 | 7 | #[test] 8 | fn single_value_tuple() { 9 | assert_eq!(Int(123).unwrap_int(), 123); 10 | } 11 | 12 | #[test] 13 | #[should_panic] 14 | fn single_value_tuple_panic() { 15 | Unit.unwrap_int(); 16 | } 17 | 18 | #[test] 19 | fn multi_value_tuple() { 20 | assert_eq!(TestEnum::new_tuple(123).unwrap_tuple(), ("123".into(), 123)); 21 | } 22 | 23 | #[test] 24 | #[should_panic] 25 | fn multi_value_tuple_panic() { 26 | Unit.unwrap_tuple(); 27 | } 28 | -------------------------------------------------------------------------------- /tests/derived_unwrap_or.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, Unit}, 5 | }; 6 | 7 | #[test] 8 | fn single_value_tuple() { 9 | // Match 10 | assert_eq!(Int(123).unwrap_or_int(456), 123); 11 | 12 | // Non-Match 13 | assert_eq!(Unit.unwrap_or_int(456), 456); 14 | } 15 | 16 | #[test] 17 | fn multi_value_tuple() { 18 | // Match 19 | assert_eq!( 20 | TestEnum::new_tuple(123).unwrap_or_tuple(("456".into(), 456)), 21 | ("123".into(), 123) 22 | ); 23 | 24 | // Non-Match 25 | assert_eq!( 26 | Unit.unwrap_or_tuple(("456".into(), 456)), 27 | ("456".into(), 456) 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /tests/derived_unwrap_or_else.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | use helper::{ 3 | TestEnum, 4 | TestEnum::{Int, Unit}, 5 | }; 6 | 7 | #[test] 8 | fn single_value_tuple() { 9 | // Match 10 | assert_eq!(Int(123).unwrap_or_int(456), 123); 11 | 12 | // Non-Match 13 | assert_eq!(Unit.unwrap_or_int(456), 456); 14 | } 15 | 16 | #[test] 17 | fn multi_value_tuple() { 18 | // Match 19 | assert_eq!( 20 | TestEnum::new_tuple(123).unwrap_or_tuple(("456".into(), 456)), 21 | ("123".into(), 123) 22 | ); 23 | 24 | // Non-Match 25 | assert_eq!( 26 | Unit.unwrap_or_tuple(("456".into(), 456)), 27 | ("456".into(), 456) 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /tests/helper.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use variantly::Variantly; 4 | 5 | /// Validate that complex enum variants can validly expand. 6 | #[derive(Variantly)] 7 | pub enum ComplexEnum<'a, A, B> 8 | where 9 | B: Fn() -> String, 10 | { 11 | One((((), ()), ()), ((), ())), 12 | Two(A, B), 13 | Three(&'a ComplexEnum<'a, A, B>), 14 | Four { 15 | first: &'a ComplexEnum<'a, String, B>, 16 | second: &'static str, 17 | }, 18 | } 19 | 20 | #[derive(Variantly, Clone)] 21 | pub enum TestEnum { 22 | Unit, 23 | OtherUnit, 24 | String(String), 25 | Int(u128), 26 | Tuple(String, u128), 27 | StructLike { value: u128 }, 28 | } 29 | 30 | impl TestEnum { 31 | pub fn new_tuple(num: u128) -> Self { 32 | Self::Tuple(num.to_string(), num) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/std_imports.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | use variantly::Variantly; 3 | 4 | /// Test compilation while custom Result/Option/etc in scope. 5 | enum Result { 6 | Ok, 7 | Err, 8 | } 9 | use self::Result::*; 10 | 11 | enum Option { 12 | Some, 13 | None, 14 | } 15 | use self::Option::*; 16 | 17 | trait FnOnce {} 18 | 19 | macro_rules! panic { 20 | () => {}; 21 | } 22 | 23 | #[derive(Variantly)] 24 | enum TestPreludeImportConflict { 25 | Variant(u32), 26 | } 27 | 28 | #[no_implicit_prelude] 29 | mod no_std { 30 | use ::std; 31 | use ::variantly::Variantly; 32 | 33 | // Test compilation w/o access to prelude contents. 34 | #[derive(Variantly)] 35 | enum TestIsolatedDerive { 36 | Variant(u32), 37 | } 38 | } 39 | --------------------------------------------------------------------------------