diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:39:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:39:49 +0000 |
commit | a0aa2307322cd47bbf416810ac0292925e03be87 (patch) | |
tree | 37076262a026c4b48c8a0e84f44ff9187556ca35 /rust/vendor/num_enum_derive | |
parent | Initial commit. (diff) | |
download | suricata-a0aa2307322cd47bbf416810ac0292925e03be87.tar.xz suricata-a0aa2307322cd47bbf416810ac0292925e03be87.zip |
Adding upstream version 1:7.0.3.upstream/1%7.0.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'rust/vendor/num_enum_derive')
-rw-r--r-- | rust/vendor/num_enum_derive/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | rust/vendor/num_enum_derive/Cargo.toml | 52 | ||||
-rw-r--r-- | rust/vendor/num_enum_derive/LICENSE-APACHE | 176 | ||||
-rw-r--r-- | rust/vendor/num_enum_derive/LICENSE-BSD | 27 | ||||
-rw-r--r-- | rust/vendor/num_enum_derive/LICENSE-MIT | 23 | ||||
-rw-r--r-- | rust/vendor/num_enum_derive/README.md | 277 | ||||
-rw-r--r-- | rust/vendor/num_enum_derive/src/lib.rs | 1066 |
7 files changed, 1622 insertions, 0 deletions
diff --git a/rust/vendor/num_enum_derive/.cargo-checksum.json b/rust/vendor/num_enum_derive/.cargo-checksum.json new file mode 100644 index 0000000..4cd36f9 --- /dev/null +++ b/rust/vendor/num_enum_derive/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"91ca56a63860212f0ad546a431076d9650a190a29a6fe1f42dd9624f44ee05e8","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-BSD":"0be96d891d00e0ae0df75d7f3289b12871c000a1f5ac744f3b570768d4bb277c","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"808954bb7af3a919b66c792ccef12e31b98070e78e2a83cde665d0e485cb1a11","src/lib.rs":"92a94fee79ca9c1ae4769246cd88a1f4af28d0b731d416b2643059fed00ef6da"},"package":"dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"}
\ No newline at end of file diff --git a/rust/vendor/num_enum_derive/Cargo.toml b/rust/vendor/num_enum_derive/Cargo.toml new file mode 100644 index 0000000..4397b7d --- /dev/null +++ b/rust/vendor/num_enum_derive/Cargo.toml @@ -0,0 +1,52 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "num_enum_derive" +version = "0.5.11" +authors = [ + "Daniel Wagner-Hall <dawagner@gmail.com>", + "Daniel Henry-Mantilla <daniel.henry.mantilla@gmail.com>", + "Vincent Esche <regexident@gmail.com>", +] +description = "Internal implementation details for ::num_enum (Procedural macros to make inter-operation between primitives and enums easier)" +readme = "README.md" +keywords = [] +categories = [] +license = "BSD-3-Clause OR MIT OR Apache-2.0" +repository = "https://github.com/illicitonion/num_enum" + +[package.metadata.docs.rs] +features = ["external_doc"] + +[lib] +proc-macro = true + +[dependencies.proc-macro-crate] +version = "1" +optional = true + +[dependencies.proc-macro2] +version = "1" + +[dependencies.quote] +version = "1" + +[dependencies.syn] +version = "1.0.15" +features = ["parsing"] + +[features] +complex-expressions = ["syn/full"] +default = ["std"] +external_doc = [] +std = ["proc-macro-crate"] diff --git a/rust/vendor/num_enum_derive/LICENSE-APACHE b/rust/vendor/num_enum_derive/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/rust/vendor/num_enum_derive/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/rust/vendor/num_enum_derive/LICENSE-BSD b/rust/vendor/num_enum_derive/LICENSE-BSD new file mode 100644 index 0000000..b742e29 --- /dev/null +++ b/rust/vendor/num_enum_derive/LICENSE-BSD @@ -0,0 +1,27 @@ +Copyright (c) 2018, Daniel Wagner-Hall +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of num_enum nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/rust/vendor/num_enum_derive/LICENSE-MIT b/rust/vendor/num_enum_derive/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/rust/vendor/num_enum_derive/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/rust/vendor/num_enum_derive/README.md b/rust/vendor/num_enum_derive/README.md new file mode 100644 index 0000000..902cee7 --- /dev/null +++ b/rust/vendor/num_enum_derive/README.md @@ -0,0 +1,277 @@ +num_enum +======== + +Procedural macros to make inter-operation between primitives and enums easier. +This crate is no_std compatible. + +[![crates.io](https://img.shields.io/crates/v/num_enum.svg)](https://crates.io/crates/num_enum) +[![Documentation](https://docs.rs/num_enum/badge.svg)](https://docs.rs/num_enum) +[![Build Status](https://travis-ci.org/illicitonion/num_enum.svg?branch=master)](https://travis-ci.org/illicitonion/num_enum) + +Turning an enum into a primitive +-------------------------------- + +```rust +use num_enum::IntoPrimitive; + +#[derive(IntoPrimitive)] +#[repr(u8)] +enum Number { + Zero, + One, +} + +fn main() { + let zero: u8 = Number::Zero.into(); + assert_eq!(zero, 0u8); +} +``` + +`num_enum`'s `IntoPrimitive` is more type-safe than using `as`, because `as` will silently truncate - `num_enum` only derives `From` for exactly the discriminant type of the enum. + +Attempting to turn a primitive into an enum with try_from +---------------------------------------------- + +```rust +use num_enum::TryFromPrimitive; +use std::convert::TryFrom; + +#[derive(Debug, Eq, PartialEq, TryFromPrimitive)] +#[repr(u8)] +enum Number { + Zero, + One, +} + +fn main() { + let zero = Number::try_from(0u8); + assert_eq!(zero, Ok(Number::Zero)); + + let three = Number::try_from(3u8); + assert_eq!( + three.unwrap_err().to_string(), + "No discriminant in enum `Number` matches the value `3`", + ); +} +``` + +Variant alternatives +--------------- + +Sometimes a single enum variant might be representable by multiple numeric values. + +The `#[num_enum(alternatives = [..])]` attribute allows you to define additional value alternatives for individual variants. + +(The behavior of `IntoPrimitive` is unaffected by this attribute, it will always return the canonical value.) + +```rust +use num_enum::TryFromPrimitive; +use std::convert::TryFrom; + +#[derive(Debug, Eq, PartialEq, TryFromPrimitive)] +#[repr(u8)] +enum Number { + Zero = 0, + #[num_enum(alternatives = [2])] + OneOrTwo = 1, +} + +fn main() { + let zero = Number::try_from(0u8); + assert_eq!(zero, Ok(Number::Zero)); + + let one = Number::try_from(1u8); + assert_eq!(one, Ok(Number::OneOrTwo)); + + let two = Number::try_from(2u8); + assert_eq!(two, Ok(Number::OneOrTwo)); + + let three = Number::try_from(3u8); + assert_eq!( + three.unwrap_err().to_string(), + "No discriminant in enum `Number` matches the value `3`", + ); +} +``` + +Range expressions are also supported for alternatives, but this requires enabling the `complex-expressions` feature: + +```rust +use num_enum::TryFromPrimitive; +use std::convert::TryFrom; + +#[derive(Debug, Eq, PartialEq, TryFromPrimitive)] +#[repr(u8)] +enum Number { + Zero = 0, + #[num_enum(alternatives = [2..16])] + Some = 1, + #[num_enum(alternatives = [17, 18..=255])] + Many = 16, +} + +fn main() { + let zero = Number::try_from(0u8); + assert_eq!(zero, Ok(Number::Zero)); + + let some = Number::try_from(15u8); + assert_eq!(some, Ok(Number::Some)); + + let many = Number::try_from(255u8); + assert_eq!(many, Ok(Number::Many)); +} +``` + +Default variant +--------------- + +Sometimes it is desirable to have an `Other` variant in an enum that acts as a kind of a wildcard matching all the value not yet covered by other variants. + +The `#[num_enum(default)]` attribute allows you to mark variant as the default. + +(The behavior of `IntoPrimitive` is unaffected by this attribute, it will always return the canonical value.) + +```rust +use num_enum::TryFromPrimitive; +use std::convert::TryFrom; + +#[derive(Debug, Eq, PartialEq, TryFromPrimitive)] +#[repr(u8)] +enum Number { + Zero = 0, + #[num_enum(default)] + NonZero = 1, +} + +fn main() { + let zero = Number::try_from(0u8); + assert_eq!(zero, Ok(Number::Zero)); + + let one = Number::try_from(1u8); + assert_eq!(one, Ok(Number::NonZero)); + + let two = Number::try_from(2u8); + assert_eq!(two, Ok(Number::NonZero)); +} +``` + +Safely turning a primitive into an exhaustive enum with from_primitive +------------------------------------------------------------- + +If your enum has all possible primitive values covered, you can derive `FromPrimitive` for it (which auto-implement stdlib's `From`): + +You can cover all possible values by: +* Having variants for every possible value +* Having a variant marked `#[num_enum(default)]` +* Having a variant marked `#[num_enum(catch_all)]` +* Having `#[num_enum(alternatives = [...])`s covering values not covered by a variant. + +```rust +use num_enum::FromPrimitive; + +#[derive(Debug, Eq, PartialEq, FromPrimitive)] +#[repr(u8)] +enum Number { + Zero, + #[num_enum(default)] + NonZero, +} + +fn main() { + assert_eq!( + Number::Zero, + Number::from(0_u8), + ); + assert_eq!( + Number::NonZero, + Number::from(1_u8), + ); +} +``` + +Catch-all variant +----------------- + +Sometimes it is desirable to have an `Other` variant which holds the otherwise un-matched value as a field. + +The `#[num_enum(catch_all)]` attribute allows you to mark at most one variant for this purpose. The variant it's applied to must be a tuple variant with exactly one field matching the `repr` type. + +```rust +use num_enum::FromPrimitive; +use std::convert::TryFrom; + +#[derive(Debug, Eq, PartialEq, FromPrimitive)] +#[repr(u8)] +enum Number { + Zero = 0, + #[num_enum(catch_all)] + NonZero(u8), +} + +fn main() { + let zero = Number::from(0u8); + assert_eq!(zero, Number::Zero); + + let one = Number::from(1u8); + assert_eq!(one, Number::NonZero(1_u8)); + + let two = Number::from(2u8); + assert_eq!(two, Number::NonZero(2_u8)); +} +``` + +As this is naturally exhaustive, this is only supported for `FromPrimitive`, not also `TryFromPrimitive`. + +Unsafely turning a primitive into an enum with from_unchecked +------------------------------------------------------------- + +If you're really certain a conversion will succeed (and have not made use of `#[num_enum(default)]` or `#[num_enum(alternatives = [..])]` +for any of its variants), and want to avoid a small amount of overhead, you can use unsafe code to do this conversion. +Unless you have data showing that the match statement generated in the `try_from` above is a bottleneck for you, +you should avoid doing this, as the unsafe code has potential to cause serious memory issues in your program. + +```rust +use num_enum::UnsafeFromPrimitive; + +#[derive(Debug, Eq, PartialEq, UnsafeFromPrimitive)] +#[repr(u8)] +enum Number { + Zero, + One, +} + +fn main() { + assert_eq!( + unsafe { Number::from_unchecked(0_u8) }, + Number::Zero, + ); + assert_eq!( + unsafe { Number::from_unchecked(1_u8) }, + Number::One, + ); +} + +unsafe fn undefined_behavior() { + let _ = Number::from_unchecked(2); // 2 is not a valid discriminant! +} +``` + +Optional features +----------------- + +Some enum values may be composed of complex expressions, for example: + +```rust +enum Number { + Zero = (0, 1).0, + One = (0, 1).1, +} +``` + +To cut down on compile time, these are not supported by default, but if you enable the `complex-expressions` +feature of your dependency on `num_enum`, these should start working. + +License +------- + +num_enum may be used under your choice of the BSD 3-clause, Apache 2, or MIT license. diff --git a/rust/vendor/num_enum_derive/src/lib.rs b/rust/vendor/num_enum_derive/src/lib.rs new file mode 100644 index 0000000..600730f --- /dev/null +++ b/rust/vendor/num_enum_derive/src/lib.rs @@ -0,0 +1,1066 @@ +// Not supported by MSRV +#![allow(clippy::uninlined_format_args)] + +extern crate proc_macro; + +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::{format_ident, quote}; +use std::collections::BTreeSet; +use syn::{ + parse::{Parse, ParseStream}, + parse_macro_input, parse_quote, + spanned::Spanned, + Attribute, Data, DeriveInput, Error, Expr, ExprLit, ExprUnary, Fields, Ident, Lit, LitInt, + LitStr, Meta, Result, UnOp, +}; + +macro_rules! die { + ($spanned:expr=> + $msg:expr + ) => { + return Err(Error::new_spanned($spanned, $msg)) + }; + + ( + $msg:expr + ) => { + return Err(Error::new(Span::call_site(), $msg)) + }; +} + +fn literal(i: i128) -> Expr { + Expr::Lit(ExprLit { + lit: Lit::Int(LitInt::new(&i.to_string(), Span::call_site())), + attrs: vec![], + }) +} + +enum DiscriminantValue { + Literal(i128), + Expr(Expr), +} + +fn parse_discriminant(val_exp: &Expr) -> Result<DiscriminantValue> { + let mut sign = 1; + let mut unsigned_expr = val_exp; + if let Expr::Unary(ExprUnary { + op: UnOp::Neg(..), + expr, + .. + }) = val_exp + { + unsigned_expr = expr; + sign = -1; + } + if let Expr::Lit(ExprLit { + lit: Lit::Int(ref lit_int), + .. + }) = unsigned_expr + { + Ok(DiscriminantValue::Literal( + sign * lit_int.base10_parse::<i128>()?, + )) + } else { + Ok(DiscriminantValue::Expr(val_exp.clone())) + } +} + +#[cfg(feature = "complex-expressions")] +fn parse_alternative_values(val_expr: &Expr) -> Result<Vec<DiscriminantValue>> { + fn range_expr_value_to_number( + parent_range_expr: &Expr, + range_bound_value: &Option<Box<Expr>>, + ) -> Result<i128> { + // Avoid needing to calculate what the lower and upper bound would be - these are type dependent, + // and also may not be obvious in context (e.g. an omitted bound could reasonably mean "from the last discriminant" or "from the lower bound of the type"). + if let Some(range_bound_value) = range_bound_value { + let range_bound_value = parse_discriminant(range_bound_value.as_ref())?; + // If non-literals are used, we can't expand to the mapped values, so can't write a nice match statement or do exhaustiveness checking. + // Require literals instead. + if let DiscriminantValue::Literal(value) = range_bound_value { + return Ok(value); + } + } + die!(parent_range_expr => "When ranges are used for alternate values, both bounds most be explicitly specified numeric literals") + } + + if let Expr::Range(syn::ExprRange { + from, to, limits, .. + }) = val_expr + { + let lower = range_expr_value_to_number(val_expr, from)?; + let upper = range_expr_value_to_number(val_expr, to)?; + // While this is technically allowed in Rust, and results in an empty range, it's almost certainly a mistake in this context. + if lower > upper { + die!(val_expr => "When using ranges for alternate values, upper bound must not be less than lower bound"); + } + let mut values = Vec::with_capacity((upper - lower) as usize); + let mut next = lower; + loop { + match limits { + syn::RangeLimits::HalfOpen(..) => { + if next == upper { + break; + } + } + syn::RangeLimits::Closed(..) => { + if next > upper { + break; + } + } + } + values.push(DiscriminantValue::Literal(next)); + next += 1; + } + return Ok(values); + } + parse_discriminant(val_expr).map(|v| vec![v]) +} + +#[cfg(not(feature = "complex-expressions"))] +fn parse_alternative_values(val_expr: &Expr) -> Result<Vec<DiscriminantValue>> { + parse_discriminant(val_expr).map(|v| vec![v]) +} + +mod kw { + syn::custom_keyword!(default); + syn::custom_keyword!(catch_all); + syn::custom_keyword!(alternatives); +} + +struct NumEnumVariantAttributes { + items: syn::punctuated::Punctuated<NumEnumVariantAttributeItem, syn::Token![,]>, +} + +impl Parse for NumEnumVariantAttributes { + fn parse(input: ParseStream<'_>) -> Result<Self> { + Ok(Self { + items: input.parse_terminated(NumEnumVariantAttributeItem::parse)?, + }) + } +} + +enum NumEnumVariantAttributeItem { + Default(VariantDefaultAttribute), + CatchAll(VariantCatchAllAttribute), + Alternatives(VariantAlternativesAttribute), +} + +impl Parse for NumEnumVariantAttributeItem { + fn parse(input: ParseStream<'_>) -> Result<Self> { + let lookahead = input.lookahead1(); + if lookahead.peek(kw::default) { + input.parse().map(Self::Default) + } else if lookahead.peek(kw::catch_all) { + input.parse().map(Self::CatchAll) + } else if lookahead.peek(kw::alternatives) { + input.parse().map(Self::Alternatives) + } else { + Err(lookahead.error()) + } + } +} + +struct VariantDefaultAttribute { + keyword: kw::default, +} + +impl Parse for VariantDefaultAttribute { + fn parse(input: ParseStream) -> Result<Self> { + Ok(Self { + keyword: input.parse()?, + }) + } +} + +impl Spanned for VariantDefaultAttribute { + fn span(&self) -> Span { + self.keyword.span() + } +} + +struct VariantCatchAllAttribute { + keyword: kw::catch_all, +} + +impl Parse for VariantCatchAllAttribute { + fn parse(input: ParseStream) -> Result<Self> { + Ok(Self { + keyword: input.parse()?, + }) + } +} + +impl Spanned for VariantCatchAllAttribute { + fn span(&self) -> Span { + self.keyword.span() + } +} + +struct VariantAlternativesAttribute { + keyword: kw::alternatives, + _eq_token: syn::Token![=], + _bracket_token: syn::token::Bracket, + expressions: syn::punctuated::Punctuated<Expr, syn::Token![,]>, +} + +impl Parse for VariantAlternativesAttribute { + fn parse(input: ParseStream) -> Result<Self> { + let content; + let keyword = input.parse()?; + let _eq_token = input.parse()?; + let _bracket_token = syn::bracketed!(content in input); + let expressions = content.parse_terminated(Expr::parse)?; + Ok(Self { + keyword, + _eq_token, + _bracket_token, + expressions, + }) + } +} + +impl Spanned for VariantAlternativesAttribute { + fn span(&self) -> Span { + self.keyword.span() + } +} + +#[derive(::core::default::Default)] +struct AttributeSpans { + default: Vec<Span>, + catch_all: Vec<Span>, + alternatives: Vec<Span>, +} + +struct VariantInfo { + ident: Ident, + attr_spans: AttributeSpans, + is_default: bool, + is_catch_all: bool, + canonical_value: Expr, + alternative_values: Vec<Expr>, +} + +impl VariantInfo { + fn all_values(&self) -> impl Iterator<Item = &Expr> { + ::core::iter::once(&self.canonical_value).chain(self.alternative_values.iter()) + } + + fn is_complex(&self) -> bool { + !self.alternative_values.is_empty() + } +} + +struct EnumInfo { + name: Ident, + repr: Ident, + variants: Vec<VariantInfo>, +} + +impl EnumInfo { + /// Returns whether the number of variants (ignoring defaults, catch-alls, etc) is the same as + /// the capacity of the repr. + fn is_naturally_exhaustive(&self) -> Result<bool> { + let repr_str = self.repr.to_string(); + if !repr_str.is_empty() { + let suffix = repr_str + .strip_prefix('i') + .or_else(|| repr_str.strip_prefix('u')); + if let Some(suffix) = suffix { + if let Ok(bits) = suffix.parse::<u32>() { + let variants = 1usize.checked_shl(bits); + return Ok(variants.map_or(false, |v| { + v == self + .variants + .iter() + .map(|v| v.alternative_values.len() + 1) + .sum() + })); + } + } + } + die!(self.repr.clone() => "Failed to parse repr into bit size"); + } + + fn has_default_variant(&self) -> bool { + self.default().is_some() + } + + fn has_complex_variant(&self) -> bool { + self.variants.iter().any(|info| info.is_complex()) + } + + fn default(&self) -> Option<&Ident> { + self.variants + .iter() + .find(|info| info.is_default) + .map(|info| &info.ident) + } + + fn catch_all(&self) -> Option<&Ident> { + self.variants + .iter() + .find(|info| info.is_catch_all) + .map(|info| &info.ident) + } + + fn first_default_attr_span(&self) -> Option<&Span> { + self.variants + .iter() + .find_map(|info| info.attr_spans.default.first()) + } + + fn first_alternatives_attr_span(&self) -> Option<&Span> { + self.variants + .iter() + .find_map(|info| info.attr_spans.alternatives.first()) + } + + fn variant_idents(&self) -> Vec<Ident> { + self.variants + .iter() + .map(|variant| variant.ident.clone()) + .collect() + } + + fn expression_idents(&self) -> Vec<Vec<Ident>> { + self.variants + .iter() + .filter(|variant| !variant.is_catch_all) + .map(|info| { + let indices = 0..(info.alternative_values.len() + 1); + indices + .map(|index| format_ident!("{}__num_enum_{}__", info.ident, index)) + .collect() + }) + .collect() + } + + fn variant_expressions(&self) -> Vec<Vec<Expr>> { + self.variants + .iter() + .map(|variant| variant.all_values().cloned().collect()) + .collect() + } +} + +impl Parse for EnumInfo { + fn parse(input: ParseStream) -> Result<Self> { + Ok({ + let input: DeriveInput = input.parse()?; + let name = input.ident; + let data = match input.data { + Data::Enum(data) => data, + Data::Union(data) => die!(data.union_token => "Expected enum but found union"), + Data::Struct(data) => die!(data.struct_token => "Expected enum but found struct"), + }; + + let repr: Ident = { + let mut attrs = input.attrs.into_iter(); + loop { + if let Some(attr) = attrs.next() { + if let Ok(Meta::List(meta_list)) = attr.parse_meta() { + if let Some(ident) = meta_list.path.get_ident() { + if ident == "repr" { + let mut nested = meta_list.nested.iter(); + if nested.len() != 1 { + die!(attr => + "Expected exactly one `repr` argument" + ); + } + let repr = nested.next().unwrap(); + let repr: Ident = parse_quote! { + #repr + }; + if repr == "C" { + die!(repr => + "repr(C) doesn't have a well defined size" + ); + } else { + break repr; + } + } + } + } + } else { + die!("Missing `#[repr({Integer})]` attribute"); + } + } + }; + + let mut variants: Vec<VariantInfo> = vec![]; + let mut has_default_variant: bool = false; + let mut has_catch_all_variant: bool = false; + + // Vec to keep track of the used discriminants and alt values. + let mut discriminant_int_val_set = BTreeSet::new(); + + let mut next_discriminant = literal(0); + for variant in data.variants.into_iter() { + let ident = variant.ident.clone(); + + let discriminant = match &variant.discriminant { + Some(d) => d.1.clone(), + None => next_discriminant.clone(), + }; + + let mut attr_spans: AttributeSpans = Default::default(); + let mut raw_alternative_values: Vec<Expr> = vec![]; + // Keep the attribute around for better error reporting. + let mut alt_attr_ref: Vec<&Attribute> = vec![]; + + // `#[num_enum(default)]` is required by `#[derive(FromPrimitive)]` + // and forbidden by `#[derive(UnsafeFromPrimitive)]`, so we need to + // keep track of whether we encountered such an attribute: + let mut is_default: bool = false; + let mut is_catch_all: bool = false; + + for attribute in &variant.attrs { + if attribute.path.is_ident("default") { + if has_default_variant { + die!(attribute => + "Multiple variants marked `#[default]` or `#[num_enum(default)]` found" + ); + } else if has_catch_all_variant { + die!(attribute => + "Attribute `default` is mutually exclusive with `catch_all`" + ); + } + attr_spans.default.push(attribute.span()); + is_default = true; + has_default_variant = true; + } + + if attribute.path.is_ident("num_enum") { + match attribute.parse_args_with(NumEnumVariantAttributes::parse) { + Ok(variant_attributes) => { + for variant_attribute in variant_attributes.items { + match variant_attribute { + NumEnumVariantAttributeItem::Default(default) => { + if has_default_variant { + die!(default.keyword => + "Multiple variants marked `#[default]` or `#[num_enum(default)]` found" + ); + } else if has_catch_all_variant { + die!(default.keyword => + "Attribute `default` is mutually exclusive with `catch_all`" + ); + } + attr_spans.default.push(default.span()); + is_default = true; + has_default_variant = true; + } + NumEnumVariantAttributeItem::CatchAll(catch_all) => { + if has_catch_all_variant { + die!(catch_all.keyword => + "Multiple variants marked with `#[num_enum(catch_all)]`" + ); + } else if has_default_variant { + die!(catch_all.keyword => + "Attribute `catch_all` is mutually exclusive with `default`" + ); + } + + match variant + .fields + .iter() + .collect::<Vec<_>>() + .as_slice() + { + [syn::Field { + ty: syn::Type::Path(syn::TypePath { path, .. }), + .. + }] if path.is_ident(&repr) => { + attr_spans.catch_all.push(catch_all.span()); + is_catch_all = true; + has_catch_all_variant = true; + } + _ => { + die!(catch_all.keyword => + "Variant with `catch_all` must be a tuple with exactly 1 field matching the repr type" + ); + } + } + } + NumEnumVariantAttributeItem::Alternatives(alternatives) => { + attr_spans.alternatives.push(alternatives.span()); + raw_alternative_values.extend(alternatives.expressions); + alt_attr_ref.push(attribute); + } + } + } + } + Err(err) => { + if cfg!(not(feature = "complex-expressions")) { + let attribute_str = format!("{}", attribute.tokens); + if attribute_str.contains("alternatives") + && attribute_str.contains("..") + { + // Give a nice error message suggesting how to fix the problem. + die!(attribute => "Ranges are only supported as num_enum alternate values if the `complex-expressions` feature of the crate `num_enum` is enabled".to_string()) + } + } + die!(attribute => + format!("Invalid attribute: {}", err) + ); + } + } + } + } + + if !is_catch_all { + match &variant.fields { + Fields::Named(_) | Fields::Unnamed(_) => { + die!(variant => format!("`{}` only supports unit variants (with no associated data), but `{}::{}` was not a unit variant.", get_crate_name(), name, ident)); + } + Fields::Unit => {} + } + } + + let discriminant_value = parse_discriminant(&discriminant)?; + + // Check for collision. + // We can't do const evaluation, or even compare arbitrary Exprs, + // so unfortunately we can't check for duplicates. + // That's not the end of the world, just we'll end up with compile errors for + // matches with duplicate branches in generated code instead of nice friendly error messages. + if let DiscriminantValue::Literal(canonical_value_int) = discriminant_value { + if discriminant_int_val_set.contains(&canonical_value_int) { + die!(ident => format!("The discriminant '{}' collides with a value attributed to a previous variant", canonical_value_int)) + } + } + + // Deal with the alternative values. + let mut flattened_alternative_values = Vec::new(); + let mut flattened_raw_alternative_values = Vec::new(); + for raw_alternative_value in raw_alternative_values { + let expanded_values = parse_alternative_values(&raw_alternative_value)?; + for expanded_value in expanded_values { + flattened_alternative_values.push(expanded_value); + flattened_raw_alternative_values.push(raw_alternative_value.clone()) + } + } + + if !flattened_alternative_values.is_empty() { + let alternate_int_values = flattened_alternative_values + .into_iter() + .map(|v| { + match v { + DiscriminantValue::Literal(value) => Ok(value), + DiscriminantValue::Expr(expr) => { + if let Expr::Range(_) = expr { + if cfg!(not(feature = "complex-expressions")) { + // Give a nice error message suggesting how to fix the problem. + die!(expr => "Ranges are only supported as num_enum alternate values if the `complex-expressions` feature of the crate `num_enum` is enabled".to_string()) + } + } + // We can't do uniqueness checking on non-literals, so we don't allow them as alternate values. + // We could probably allow them, but there doesn't seem to be much of a use-case, + // and it's easier to give good error messages about duplicate values this way, + // rather than rustc errors on conflicting match branches. + die!(expr => "Only literals are allowed as num_enum alternate values".to_string()) + }, + } + }) + .collect::<Result<Vec<i128>>>()?; + let mut sorted_alternate_int_values = alternate_int_values.clone(); + sorted_alternate_int_values.sort_unstable(); + let sorted_alternate_int_values = sorted_alternate_int_values; + + // Check if the current discriminant is not in the alternative values. + if let DiscriminantValue::Literal(canonical_value_int) = discriminant_value { + if let Some(index) = alternate_int_values + .iter() + .position(|&x| x == canonical_value_int) + { + die!(&flattened_raw_alternative_values[index] => format!("'{}' in the alternative values is already attributed as the discriminant of this variant", canonical_value_int)); + } + } + + // Search for duplicates, the vec is sorted. Warn about them. + if (1..sorted_alternate_int_values.len()).any(|i| { + sorted_alternate_int_values[i] == sorted_alternate_int_values[i - 1] + }) { + let attr = *alt_attr_ref.last().unwrap(); + die!(attr => "There is duplication in the alternative values"); + } + // Search if those discriminant_int_val_set where already attributed. + // (discriminant_int_val_set is BTreeSet, and iter().next_back() is the is the maximum in the set.) + if let Some(last_upper_val) = discriminant_int_val_set.iter().next_back() { + if sorted_alternate_int_values.first().unwrap() <= last_upper_val { + for (index, val) in alternate_int_values.iter().enumerate() { + if discriminant_int_val_set.contains(val) { + die!(&flattened_raw_alternative_values[index] => format!("'{}' in the alternative values is already attributed to a previous variant", val)); + } + } + } + } + + // Reconstruct the alternative_values vec of Expr but sorted. + flattened_raw_alternative_values = sorted_alternate_int_values + .iter() + .map(|val| literal(val.to_owned())) + .collect(); + + // Add the alternative values to the the set to keep track. + discriminant_int_val_set.extend(sorted_alternate_int_values); + } + + // Add the current discriminant to the the set to keep track. + if let DiscriminantValue::Literal(canonical_value_int) = discriminant_value { + discriminant_int_val_set.insert(canonical_value_int); + } + + variants.push(VariantInfo { + ident, + attr_spans, + is_default, + is_catch_all, + canonical_value: discriminant, + alternative_values: flattened_raw_alternative_values, + }); + + // Get the next value for the discriminant. + next_discriminant = match discriminant_value { + DiscriminantValue::Literal(int_value) => literal(int_value.wrapping_add(1)), + DiscriminantValue::Expr(expr) => { + parse_quote! { + #repr::wrapping_add(#expr, 1) + } + } + } + } + + EnumInfo { + name, + repr, + variants, + } + }) + } +} + +/// Implements `Into<Primitive>` for a `#[repr(Primitive)] enum`. +/// +/// (It actually implements `From<Enum> for Primitive`) +/// +/// ## Allows turning an enum into a primitive. +/// +/// ```rust +/// use num_enum::IntoPrimitive; +/// +/// #[derive(IntoPrimitive)] +/// #[repr(u8)] +/// enum Number { +/// Zero, +/// One, +/// } +/// +/// let zero: u8 = Number::Zero.into(); +/// assert_eq!(zero, 0u8); +/// ``` +#[proc_macro_derive(IntoPrimitive, attributes(num_enum, catch_all))] +pub fn derive_into_primitive(input: TokenStream) -> TokenStream { + let enum_info = parse_macro_input!(input as EnumInfo); + let catch_all = enum_info.catch_all(); + let name = &enum_info.name; + let repr = &enum_info.repr; + + let body = if let Some(catch_all_ident) = catch_all { + quote! { + match enum_value { + #name::#catch_all_ident(raw) => raw, + rest => unsafe { *(&rest as *const #name as *const Self) } + } + } + } else { + quote! { enum_value as Self } + }; + + TokenStream::from(quote! { + impl From<#name> for #repr { + #[inline] + fn from (enum_value: #name) -> Self + { + #body + } + } + }) +} + +/// Implements `From<Primitive>` for a `#[repr(Primitive)] enum`. +/// +/// Turning a primitive into an enum with `from`. +/// ---------------------------------------------- +/// +/// ```rust +/// use num_enum::FromPrimitive; +/// +/// #[derive(Debug, Eq, PartialEq, FromPrimitive)] +/// #[repr(u8)] +/// enum Number { +/// Zero, +/// #[num_enum(default)] +/// NonZero, +/// } +/// +/// let zero = Number::from(0u8); +/// assert_eq!(zero, Number::Zero); +/// +/// let one = Number::from(1u8); +/// assert_eq!(one, Number::NonZero); +/// +/// let two = Number::from(2u8); +/// assert_eq!(two, Number::NonZero); +/// ``` +#[proc_macro_derive(FromPrimitive, attributes(num_enum, default, catch_all))] +pub fn derive_from_primitive(input: TokenStream) -> TokenStream { + let enum_info: EnumInfo = parse_macro_input!(input); + let krate = Ident::new(&get_crate_name(), Span::call_site()); + + let is_naturally_exhaustive = enum_info.is_naturally_exhaustive(); + let catch_all_body = match is_naturally_exhaustive { + Ok(is_naturally_exhaustive) => { + if is_naturally_exhaustive { + quote! { unreachable!("exhaustive enum") } + } else if let Some(default_ident) = enum_info.default() { + quote! { Self::#default_ident } + } else if let Some(catch_all_ident) = enum_info.catch_all() { + quote! { Self::#catch_all_ident(number) } + } else { + let span = Span::call_site(); + let message = + "#[derive(num_enum::FromPrimitive)] requires enum to be exhaustive, or a variant marked with `#[default]`, `#[num_enum(default)]`, or `#[num_enum(catch_all)`"; + return syn::Error::new(span, message).to_compile_error().into(); + } + } + Err(err) => { + return err.to_compile_error().into(); + } + }; + + let EnumInfo { + ref name, ref repr, .. + } = enum_info; + + let variant_idents: Vec<Ident> = enum_info.variant_idents(); + let expression_idents: Vec<Vec<Ident>> = enum_info.expression_idents(); + let variant_expressions: Vec<Vec<Expr>> = enum_info.variant_expressions(); + + debug_assert_eq!(variant_idents.len(), variant_expressions.len()); + + TokenStream::from(quote! { + impl ::#krate::FromPrimitive for #name { + type Primitive = #repr; + + fn from_primitive(number: Self::Primitive) -> Self { + // Use intermediate const(s) so that enums defined like + // `Two = ONE + 1u8` work properly. + #![allow(non_upper_case_globals)] + #( + #( + const #expression_idents: #repr = #variant_expressions; + )* + )* + #[deny(unreachable_patterns)] + match number { + #( + #( #expression_idents )|* + => Self::#variant_idents, + )* + #[allow(unreachable_patterns)] + _ => #catch_all_body, + } + } + } + + impl ::core::convert::From<#repr> for #name { + #[inline] + fn from ( + number: #repr, + ) -> Self { + ::#krate::FromPrimitive::from_primitive(number) + } + } + + // The Rust stdlib will implement `#name: From<#repr>` for us for free! + + impl ::#krate::TryFromPrimitive for #name { + type Primitive = #repr; + + const NAME: &'static str = stringify!(#name); + + #[inline] + fn try_from_primitive ( + number: Self::Primitive, + ) -> ::core::result::Result< + Self, + ::#krate::TryFromPrimitiveError<Self>, + > + { + Ok(::#krate::FromPrimitive::from_primitive(number)) + } + } + }) +} + +/// Implements `TryFrom<Primitive>` for a `#[repr(Primitive)] enum`. +/// +/// Attempting to turn a primitive into an enum with `try_from`. +/// ---------------------------------------------- +/// +/// ```rust +/// use num_enum::TryFromPrimitive; +/// use std::convert::TryFrom; +/// +/// #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] +/// #[repr(u8)] +/// enum Number { +/// Zero, +/// One, +/// } +/// +/// let zero = Number::try_from(0u8); +/// assert_eq!(zero, Ok(Number::Zero)); +/// +/// let three = Number::try_from(3u8); +/// assert_eq!( +/// three.unwrap_err().to_string(), +/// "No discriminant in enum `Number` matches the value `3`", +/// ); +/// ``` +#[proc_macro_derive(TryFromPrimitive, attributes(num_enum))] +pub fn derive_try_from_primitive(input: TokenStream) -> TokenStream { + let enum_info: EnumInfo = parse_macro_input!(input); + let krate = Ident::new(&get_crate_name(), Span::call_site()); + + let EnumInfo { + ref name, ref repr, .. + } = enum_info; + + let variant_idents: Vec<Ident> = enum_info.variant_idents(); + let expression_idents: Vec<Vec<Ident>> = enum_info.expression_idents(); + let variant_expressions: Vec<Vec<Expr>> = enum_info.variant_expressions(); + + debug_assert_eq!(variant_idents.len(), variant_expressions.len()); + + let default_arm = match enum_info.default() { + Some(ident) => { + quote! { + _ => ::core::result::Result::Ok( + #name::#ident + ) + } + } + None => { + quote! { + _ => ::core::result::Result::Err( + ::#krate::TryFromPrimitiveError { number } + ) + } + } + }; + + TokenStream::from(quote! { + impl ::#krate::TryFromPrimitive for #name { + type Primitive = #repr; + + const NAME: &'static str = stringify!(#name); + + fn try_from_primitive ( + number: Self::Primitive, + ) -> ::core::result::Result< + Self, + ::#krate::TryFromPrimitiveError<Self> + > { + // Use intermediate const(s) so that enums defined like + // `Two = ONE + 1u8` work properly. + #![allow(non_upper_case_globals)] + #( + #( + const #expression_idents: #repr = #variant_expressions; + )* + )* + #[deny(unreachable_patterns)] + match number { + #( + #( #expression_idents )|* + => ::core::result::Result::Ok(Self::#variant_idents), + )* + #[allow(unreachable_patterns)] + #default_arm, + } + } + } + + impl ::core::convert::TryFrom<#repr> for #name { + type Error = ::#krate::TryFromPrimitiveError<Self>; + + #[inline] + fn try_from ( + number: #repr, + ) -> ::core::result::Result<Self, ::#krate::TryFromPrimitiveError<Self>> + { + ::#krate::TryFromPrimitive::try_from_primitive(number) + } + } + }) +} + +#[cfg(feature = "proc-macro-crate")] +fn get_crate_name() -> String { + let found_crate = proc_macro_crate::crate_name("num_enum").unwrap_or_else(|err| { + eprintln!("Warning: {}\n => defaulting to `num_enum`", err,); + proc_macro_crate::FoundCrate::Itself + }); + + match found_crate { + proc_macro_crate::FoundCrate::Itself => String::from("num_enum"), + proc_macro_crate::FoundCrate::Name(name) => name, + } +} + +// Don't depend on proc-macro-crate in no_std environments because it causes an awkward dependency +// on serde with std. +// +// no_std dependees on num_enum cannot rename the num_enum crate when they depend on it. Sorry. +// +// See https://github.com/illicitonion/num_enum/issues/18 +#[cfg(not(feature = "proc-macro-crate"))] +fn get_crate_name() -> String { + String::from("num_enum") +} + +/// Generates a `unsafe fn from_unchecked (number: Primitive) -> Self` +/// associated function. +/// +/// Allows unsafely turning a primitive into an enum with from_unchecked. +/// ------------------------------------------------------------- +/// +/// If you're really certain a conversion will succeed, and want to avoid a small amount of overhead, you can use unsafe +/// code to do this conversion. Unless you have data showing that the match statement generated in the `try_from` above is a +/// bottleneck for you, you should avoid doing this, as the unsafe code has potential to cause serious memory issues in +/// your program. +/// +/// ```rust +/// use num_enum::UnsafeFromPrimitive; +/// +/// #[derive(Debug, Eq, PartialEq, UnsafeFromPrimitive)] +/// #[repr(u8)] +/// enum Number { +/// Zero, +/// One, +/// } +/// +/// fn main() { +/// assert_eq!( +/// Number::Zero, +/// unsafe { Number::from_unchecked(0_u8) }, +/// ); +/// assert_eq!( +/// Number::One, +/// unsafe { Number::from_unchecked(1_u8) }, +/// ); +/// } +/// +/// unsafe fn undefined_behavior() { +/// let _ = Number::from_unchecked(2); // 2 is not a valid discriminant! +/// } +/// ``` +#[proc_macro_derive(UnsafeFromPrimitive, attributes(num_enum))] +pub fn derive_unsafe_from_primitive(stream: TokenStream) -> TokenStream { + let enum_info = parse_macro_input!(stream as EnumInfo); + + if enum_info.has_default_variant() { + let span = enum_info + .first_default_attr_span() + .cloned() + .expect("Expected span"); + let message = "#[derive(UnsafeFromPrimitive)] does not support `#[num_enum(default)]`"; + return syn::Error::new(span, message).to_compile_error().into(); + } + + if enum_info.has_complex_variant() { + let span = enum_info + .first_alternatives_attr_span() + .cloned() + .expect("Expected span"); + let message = + "#[derive(UnsafeFromPrimitive)] does not support `#[num_enum(alternatives = [..])]`"; + return syn::Error::new(span, message).to_compile_error().into(); + } + + let EnumInfo { + ref name, ref repr, .. + } = enum_info; + + let doc_string = LitStr::new( + &format!( + r#" +Transmutes `number: {repr}` into a [`{name}`]. + +# Safety + + - `number` must represent a valid discriminant of [`{name}`] +"#, + repr = repr, + name = name, + ), + Span::call_site(), + ); + + TokenStream::from(quote! { + impl #name { + #[doc = #doc_string] + #[inline] + pub unsafe fn from_unchecked(number: #repr) -> Self { + ::core::mem::transmute(number) + } + } + }) +} + +/// Implements `core::default::Default` for a `#[repr(Primitive)] enum`. +/// +/// Whichever variant has the `#[default]` or `#[num_enum(default)]` attribute will be returned. +/// ---------------------------------------------- +/// +/// ```rust +/// #[derive(Debug, Eq, PartialEq, num_enum::Default)] +/// #[repr(u8)] +/// enum Number { +/// Zero, +/// #[default] +/// One, +/// } +/// +/// assert_eq!(Number::One, Number::default()); +/// assert_eq!(Number::One, <Number as ::core::default::Default>::default()); +/// ``` +#[proc_macro_derive(Default, attributes(num_enum, default))] +pub fn derive_default(stream: TokenStream) -> TokenStream { + let enum_info = parse_macro_input!(stream as EnumInfo); + + let default_ident = match enum_info.default() { + Some(ident) => ident, + None => { + let span = Span::call_site(); + let message = + "#[derive(num_enum::Default)] requires enum to be exhaustive, or a variant marked with `#[default]` or `#[num_enum(default)]`"; + return syn::Error::new(span, message).to_compile_error().into(); + } + }; + + let EnumInfo { ref name, .. } = enum_info; + + TokenStream::from(quote! { + impl ::core::default::Default for #name { + #[inline] + fn default() -> Self { + Self::#default_ident + } + } + }) +} |