Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
1
third-party/vendor/num_enum_derive/.cargo-checksum.json
vendored
Normal file
1
third-party/vendor/num_enum_derive/.cargo-checksum.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"files":{"Cargo.toml":"5c36e95cf82b83461ddd6ccdfb64aad8bedeb2222a87437d8c68eb7201dbe3d2","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-BSD":"0be96d891d00e0ae0df75d7f3289b12871c000a1f5ac744f3b570768d4bb277c","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"338e12501163f84e7c9652269672af9b4449798fe29adee921bca33f4f52a99b","src/lib.rs":"ee897da050bac895fadd6a044923d92922697214ea53e8ab07f310933e843db3"},"package":"96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6"}
|
||||
54
third-party/vendor/num_enum_derive/Cargo.toml
vendored
Normal file
54
third-party/vendor/num_enum_derive/Cargo.toml
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# 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 = "2021"
|
||||
rust-version = "1.56.0"
|
||||
name = "num_enum_derive"
|
||||
version = "0.6.1"
|
||||
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"
|
||||
resolver = "1"
|
||||
|
||||
[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 = "2"
|
||||
features = ["parsing"]
|
||||
|
||||
[features]
|
||||
complex-expressions = ["syn/full"]
|
||||
default = ["std"]
|
||||
external_doc = []
|
||||
std = ["proc-macro-crate"]
|
||||
176
third-party/vendor/num_enum_derive/LICENSE-APACHE
vendored
Normal file
176
third-party/vendor/num_enum_derive/LICENSE-APACHE
vendored
Normal file
|
|
@ -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
|
||||
27
third-party/vendor/num_enum_derive/LICENSE-BSD
vendored
Normal file
27
third-party/vendor/num_enum_derive/LICENSE-BSD
vendored
Normal file
|
|
@ -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.
|
||||
23
third-party/vendor/num_enum_derive/LICENSE-MIT
vendored
Normal file
23
third-party/vendor/num_enum_derive/LICENSE-MIT
vendored
Normal file
|
|
@ -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.
|
||||
299
third-party/vendor/num_enum_derive/README.md
vendored
Normal file
299
third-party/vendor/num_enum_derive/README.md
vendored
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
num_enum
|
||||
========
|
||||
|
||||
Procedural macros to make inter-operation between primitives and enums easier.
|
||||
This crate is no_std compatible.
|
||||
|
||||
[](https://crates.io/crates/num_enum)
|
||||
[](https://docs.rs/num_enum)
|
||||
[](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 (or the stdlib `#[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::FromPrimitive;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, FromPrimitive)]
|
||||
#[repr(u8)]
|
||||
enum Number {
|
||||
Zero = 0,
|
||||
#[num_enum(default)]
|
||||
NonZero = 1,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
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);
|
||||
}
|
||||
```
|
||||
|
||||
Only `FromPrimitive` pays attention to `default` attributes, `TryFromPrimitive` ignores them.
|
||||
|
||||
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 unchecked_transmute_from
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
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.
|
||||
|
||||
Note that this derive ignores any `default`, `catch_all`, and `alternatives` attributes on the enum.
|
||||
If you need support for conversions from these values, you should use `TryFromPrimitive` or `FromPrimitive`.
|
||||
|
||||
- This means, for instance, that the following is UB:
|
||||
|
||||
```rust,no_run
|
||||
use num_enum::UnsafeFromPrimitive;
|
||||
|
||||
#[derive(UnsafeFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
enum Number {
|
||||
Zero = 0,
|
||||
|
||||
// Same for `#[num_enum(catch_all)]`, and `#[num_enum(alternatives = [2, ...])]`
|
||||
#[num_enum(default)]
|
||||
One = 1,
|
||||
}
|
||||
let _undefined_behavior = unsafe { Number::unchecked_transmute_from(2) };
|
||||
```
|
||||
|
||||
```rust
|
||||
use num_enum::UnsafeFromPrimitive;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, UnsafeFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
enum Number {
|
||||
Zero,
|
||||
One,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(
|
||||
unsafe { Number::unchecked_transmute_from(0_u8) },
|
||||
Number::Zero,
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { Number::unchecked_transmute_from(1_u8) },
|
||||
Number::One,
|
||||
);
|
||||
}
|
||||
|
||||
unsafe fn undefined_behavior() {
|
||||
let _ = Number::unchecked_transmute_from(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.
|
||||
954
third-party/vendor/num_enum_derive/src/lib.rs
vendored
Normal file
954
third-party/vendor/num_enum_derive/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,954 @@
|
|||
// 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, ToTokens};
|
||||
use std::collections::BTreeSet;
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
parse_macro_input, parse_quote, Attribute, Data, DeriveInput, Error, Expr, ExprLit, ExprUnary,
|
||||
Fields, Ident, Lit, LitInt, 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 {
|
||||
start, end, limits, ..
|
||||
}) = val_expr
|
||||
{
|
||||
let lower = range_expr_value_to_number(val_expr, start)?;
|
||||
let upper = range_expr_value_to_number(val_expr, end)?;
|
||||
// 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, syn::Token![,])?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct VariantCatchAllAttribute {
|
||||
keyword: kw::catch_all,
|
||||
}
|
||||
|
||||
impl Parse for VariantCatchAllAttribute {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
Ok(Self {
|
||||
keyword: input.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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, syn::Token![,])?;
|
||||
Ok(Self {
|
||||
_keyword: keyword,
|
||||
_eq_token,
|
||||
_bracket_token,
|
||||
expressions,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct VariantInfo {
|
||||
ident: Ident,
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
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 suffix == "size" {
|
||||
return Ok(false);
|
||||
} else 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 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 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 Meta::List(meta_list) = &attr.meta {
|
||||
if let Some(ident) = meta_list.path.get_ident() {
|
||||
if ident == "repr" {
|
||||
let mut nested = meta_list.tokens.clone().into_iter();
|
||||
let repr = match (nested.next(), nested.next()) {
|
||||
(Some(repr), None) => repr,
|
||||
_ => die!(attr =>
|
||||
"Expected exactly one `repr` argument"
|
||||
),
|
||||
};
|
||||
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 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`"
|
||||
);
|
||||
}
|
||||
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`"
|
||||
);
|
||||
}
|
||||
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) => {
|
||||
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) => {
|
||||
raw_alternative_values.extend(alternatives.expressions);
|
||||
alt_attr_ref.push(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
if cfg!(not(feature = "complex-expressions")) {
|
||||
let tokens = attribute.meta.to_token_stream();
|
||||
|
||||
let attribute_str = format!("{}", 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,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl ::#krate::CannotDeriveBothFromPrimitiveAndTryFromPrimitive for #name {}
|
||||
})
|
||||
}
|
||||
|
||||
/// 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());
|
||||
|
||||
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)]
|
||||
_ => ::core::result::Result::Err(
|
||||
::#krate::TryFromPrimitiveError { number }
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl ::#krate::CannotDeriveBothFromPrimitiveAndTryFromPrimitive for #name {}
|
||||
})
|
||||
}
|
||||
|
||||
#[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 unchecked_transmute_from(number: Primitive) -> Self`
|
||||
/// associated function.
|
||||
///
|
||||
/// Allows unsafely turning a primitive into an enum with unchecked_transmute_from
|
||||
/// ------------------------------------------------------------------------------
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// Note that this derive ignores any `default`, `catch_all`, and `alternatives` attributes on the enum.
|
||||
/// If you need support for conversions from these values, you should use `TryFromPrimitive` or `FromPrimitive`.
|
||||
///
|
||||
/// ```rust
|
||||
/// use num_enum::UnsafeFromPrimitive;
|
||||
///
|
||||
/// #[derive(Debug, Eq, PartialEq, UnsafeFromPrimitive)]
|
||||
/// #[repr(u8)]
|
||||
/// enum Number {
|
||||
/// Zero,
|
||||
/// One,
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// assert_eq!(
|
||||
/// Number::Zero,
|
||||
/// unsafe { Number::unchecked_transmute_from(0_u8) },
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// Number::One,
|
||||
/// unsafe { Number::unchecked_transmute_from(1_u8) },
|
||||
/// );
|
||||
/// }
|
||||
///
|
||||
/// unsafe fn undefined_behavior() {
|
||||
/// let _ = Number::unchecked_transmute_from(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);
|
||||
let krate = Ident::new(&get_crate_name(), Span::call_site());
|
||||
|
||||
let EnumInfo {
|
||||
ref name, ref repr, ..
|
||||
} = enum_info;
|
||||
|
||||
TokenStream::from(quote! {
|
||||
impl ::#krate::UnsafeFromPrimitive for #name {
|
||||
type Primitive = #repr;
|
||||
|
||||
unsafe fn unchecked_transmute_from(number: Self::Primitive) -> 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
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue