diff options
author | David Tolnay <dtolnay@chromium.org> | 2018-12-06 01:58:21 -0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-12-07 17:35:43 -0800 |
commit | f97991985d6921375eb5a533c49fd7df5a75d2cd (patch) | |
tree | 52278731797f91cb0d42b5c81ba545acaaae9b53 /enumn/src/tests.rs | |
parent | 1d4d44a8e229d63aa16d05615ed33100f949863e (diff) | |
download | crosvm-f97991985d6921375eb5a533c49fd7df5a75d2cd.tar crosvm-f97991985d6921375eb5a533c49fd7df5a75d2cd.tar.gz crosvm-f97991985d6921375eb5a533c49fd7df5a75d2cd.tar.bz2 crosvm-f97991985d6921375eb5a533c49fd7df5a75d2cd.tar.lz crosvm-f97991985d6921375eb5a533c49fd7df5a75d2cd.tar.xz crosvm-f97991985d6921375eb5a533c49fd7df5a75d2cd.tar.zst crosvm-f97991985d6921375eb5a533c49fd7df5a75d2cd.zip |
macros: Derive macro to generate integer to enum conversion
This CL adds a procedural macro to generate functions for converting a primitive integer into the corresponding variant of an enum. Loosely based on https://docs.rs/enum-primitive-derive but implemented against a newer version of Syn and without the dependency on num-traits. The generated function is named `n` and has the following signature: impl YourEnum { pub fn n(value: Repr) -> Option<Self>; } where `Repr` is an integer type of the right size as described in more detail below. EXAMPLE extern crate enumn; #[derive(PartialEq, Debug, enumn::N)] enum Status { LegendaryTriumph, QualifiedSuccess, FortuitousRevival, IndeterminateStalemate, RecoverableSetback, DireMisadventure, AbjectFailure, } fn main() { let s = Status::n(1); assert_eq!(s, Some(Status::QualifiedSuccess)); let s = Status::n(9); assert_eq!(s, None); } SIGNATURE The generated signature depends on whether the enum has a `#[repr(..)]` attribute. If a `repr` is specified, the input to `n` will be required to be of that type. #[derive(enumn::N)] #[repr(u8)] enum E { /* ... */ } // expands to: impl E { pub fn n(value: u8) -> Option<Self> { /* ... */ } } On the other hand if no `repr` is specified then we get a signature that is generic over a variety of possible types. impl E { pub fn n<REPR: Into<i64>>(value: REPR) -> Option<Self> { /* ... */ } } DISCRIMINANTS The conversion respects explictly specified enum discriminants. Consider this enum: #[derive(enumn::N)] enum Letter { A = 65, B = 66, } Here `Letter::n(65)` would return `Some(Letter::A)`. TEST=`cargo test` against the new crate Change-Id: I4286a816828c83507b35185fe497455ee30ae9e8 Reviewed-on: https://chromium-review.googlesource.com/1365114 Commit-Ready: David Tolnay <dtolnay@chromium.org> Tested-by: David Tolnay <dtolnay@chromium.org> Reviewed-by: Chirantan Ekbote <chirantan@chromium.org> Reviewed-by: Dylan Reid <dgreid@chromium.org>
Diffstat (limited to 'enumn/src/tests.rs')
-rw-r--r-- | enumn/src/tests.rs | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/enumn/src/tests.rs b/enumn/src/tests.rs new file mode 100644 index 0000000..b7e0cda --- /dev/null +++ b/enumn/src/tests.rs @@ -0,0 +1,69 @@ +// Copyright 2018 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use quote::quote; +use syn::{parse_quote, DeriveInput}; + +#[test] +fn test_repr() { + let input: DeriveInput = parse_quote! { + #[repr(u8)] + enum E { + A, + B, + C, + } + }; + let actual = ::testable_derive(input); + let expected = quote! { + impl E { + pub fn n(value: u8) -> Option<Self> { + struct discriminant; + impl discriminant { + const A: u8 = E::A as u8; + const B: u8 = E::B as u8; + const C: u8 = E::C as u8; + } + match value { + discriminant::A => Some(E::A), + discriminant::B => Some(E::B), + discriminant::C => Some(E::C), + _ => None, + } + } + } + }; + assert_eq!(actual.to_string(), expected.to_string()); +} + +#[test] +fn test_no_repr() { + let input: DeriveInput = parse_quote! { + enum E { + A, + B, + C, + } + }; + let actual = ::testable_derive(input); + let expected = quote! { + impl E { + pub fn n<REPR: Into<i64>>(value: REPR) -> Option<Self> { + struct discriminant; + impl discriminant { + const A: i64 = E::A as i64; + const B: i64 = E::B as i64; + const C: i64 = E::C as i64; + } + match <REPR as Into<i64>>::into(value) { + discriminant::A => Some(E::A), + discriminant::B => Some(E::B), + discriminant::C => Some(E::C), + _ => None, + } + } + } + }; + assert_eq!(actual.to_string(), expected.to_string()); +} |