├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benchmarks ├── Cargo.toml ├── from_iterator.rs └── iterator.rs ├── enumflags_derive ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT └── src │ └── lib.rs ├── src ├── const_api.rs ├── fallible.rs ├── formatting.rs ├── iter.rs └── lib.rs └── test_suite ├── Cargo.toml ├── common.rs ├── tests ├── bitflag_tests.rs ├── bitflag_tests_2018.rs ├── no_implicit_prelude.rs ├── no_implicit_prelude_2018.rs ├── no_std.rs ├── no_std_2018.rs ├── not_literal.rs ├── requires_std.rs └── serde.rs ├── ui ├── invalid_attribute_syntax.rs ├── invalid_attribute_syntax.stderr ├── invalid_name_in_default.rs ├── invalid_name_in_default.stderr ├── invalid_repr.rs ├── invalid_repr.stderr ├── literal_out_of_range.rs ├── literal_out_of_range.stderr ├── multiple_bits.rs ├── multiple_bits.stderr ├── multiple_bits_deferred.rs ├── multiple_bits_deferred.stderr ├── not_enum.rs ├── not_enum.stderr ├── overlapping_flags.rs ├── overlapping_flags.stderr ├── shift_out_of_range.rs ├── shift_out_of_range.stderr ├── sneaky_make_bitflags.rs ├── sneaky_make_bitflags.stderr ├── with_fields.rs ├── with_fields.stderr ├── with_generics.rs ├── with_generics.stderr ├── zero_disciminant.rs ├── zero_disciminant.stderr ├── zero_discriminant_deferred.rs └── zero_discriminant_deferred.stderr └── ui_tests.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | strategy: 8 | matrix: 9 | include: 10 | - rust: nightly 11 | - rust: stable 12 | extra-test-opts: -- --skip ui 13 | - rust: 1.56 14 | extra-test-opts: -- --skip ui 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v1 19 | - uses: actions-rs/toolchain@v1 20 | with: 21 | toolchain: ${{ matrix.rust }} 22 | profile: minimal 23 | default: true 24 | - uses: taiki-e/install-action@cargo-hack 25 | - run: cargo hack build --feature-powerset 26 | - run: cargo test --all ${{ matrix.extra-test-opts }} 27 | clippy: 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v1 31 | - uses: actions-rs/toolchain@v1 32 | with: 33 | profile: minimal 34 | toolchain: nightly 35 | override: true 36 | components: clippy 37 | - uses: taiki-e/install-action@cargo-hack 38 | - run: cargo hack clippy --workspace -- -D warnings 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | *~ 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "enumflags2" 3 | version = "0.7.11" 4 | authors = ["maik klein ", "Maja Kądziołka "] 5 | description = "Enum-based bit flags" 6 | license = "MIT OR Apache-2.0" 7 | repository = "https://github.com/meithecatte/enumflags2" 8 | readme = "README.md" 9 | keywords = ["enum", "bitflag", "flag", "bitflags"] 10 | documentation = "https://docs.rs/enumflags2" 11 | edition = "2018" 12 | rust-version = "1.56" 13 | 14 | [dependencies.enumflags2_derive] 15 | version = "=0.7.11" 16 | path = "enumflags_derive" 17 | 18 | [dependencies.serde] 19 | version = "^1.0.0" 20 | default-features = false 21 | optional = true 22 | 23 | [features] 24 | std = [] 25 | 26 | [workspace] 27 | members = [ 28 | "enumflags_derive", 29 | "test_suite", 30 | "benchmarks", 31 | ] 32 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 14 | 15 | "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. 16 | 17 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 18 | 19 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 20 | 21 | "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. 22 | 23 | "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). 24 | 25 | "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. 26 | 27 | "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." 28 | 29 | "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. 30 | 31 | 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. 32 | 33 | 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. 34 | 35 | 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: 36 | 37 | You must give any other recipients of the Work or Derivative Works a copy of this License; and 38 | You must cause any modified files to carry prominent notices stating that You changed the files; and 39 | 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 40 | 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. 41 | 42 | 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. 43 | 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. 44 | 45 | 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. 46 | 47 | 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. 48 | 49 | 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. 50 | 51 | 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. 52 | 53 | END OF TERMS AND CONDITIONS 54 | 55 | 56 | Copyright 2017-2023 Maik Klein, Maja Kądziołka 57 | 58 | Licensed under the Apache License, Version 2.0 (the "License"); 59 | you may not use this file except in compliance with the License. 60 | You may obtain a copy of the License at 61 | 62 | http://www.apache.org/licenses/LICENSE-2.0 63 | 64 | Unless required by applicable law or agreed to in writing, software 65 | distributed under the License is distributed on an "AS IS" BASIS, 66 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 67 | See the License for the specific language governing permissions and 68 | limitations under the License. 69 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2023 Maik Klein, Maja Kądziołka 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE-MIT) 2 | [![LICENSE](https://img.shields.io/badge/license-apache-blue.svg)](LICENSE-APACHE) 3 | [![Documentation](https://docs.rs/enumflags2/badge.svg)](https://docs.rs/enumflags2) 4 | [![Crates.io Version](https://img.shields.io/crates/v/enumflags2.svg)](https://crates.io/crates/enumflags2) 5 | 6 | # Enumflags 7 | 8 | `enumflags2` implements the classic bitflags datastructure. Annotate an enum 9 | with `#[bitflags]`, and `BitFlags` will be able to hold arbitrary combinations 10 | of your enum within the space of a single integer. 11 | 12 | Unlike other crates, `enumflags2` makes the type-level distinction between 13 | a single flag (`YourEnum`) and a set of flags (`BitFlags`). 14 | This allows idiomatic handling of bitflags, such as with `match` and `iter`. 15 | 16 | ## Features 17 | 18 | - [x] Uses enums to represent individual flags—a set of flags is a separate type from a single flag. 19 | - [x] Automatically chooses a free bit when you don't specify. 20 | - [x] Detects incorrect BitFlags at compile time. 21 | - [x] Has a similar API compared to the popular [bitflags](https://crates.io/crates/bitflags) crate. 22 | - [x] Does not expose the generated types explicity. The user interacts exclusively with `struct BitFlags;`. 23 | - [x] The debug formatter prints the binary flag value as well as the flag enums: `BitFlags(0b1111, [A, B, C, D])`. 24 | - [x] Optional support for serialization with the [`serde`](https://serde.rs/) feature flag. 25 | 26 | ## Example 27 | 28 | ```rust 29 | use enumflags2::{bitflags, make_bitflags, BitFlags}; 30 | 31 | #[bitflags] 32 | #[repr(u8)] 33 | #[derive(Copy, Clone, Debug, PartialEq)] 34 | enum Test { 35 | A = 0b0001, 36 | B = 0b0010, 37 | C, // unspecified variants pick unused bits automatically 38 | D = 0b1000, 39 | } 40 | 41 | // Flags can be combined with |, this creates a BitFlags of your type: 42 | let a_b: BitFlags = Test::A | Test::B; 43 | let a_c = Test::A | Test::C; 44 | let b_c_d = make_bitflags!(Test::{B | C | D}); 45 | 46 | // The debug output lets you inspect both the numeric value and 47 | // the actual flags: 48 | assert_eq!(format!("{:?}", a_b), "BitFlags(0b11, A | B)"); 49 | 50 | // But if you'd rather see only one of those, that's available too: 51 | assert_eq!(format!("{}", a_b), "A | B"); 52 | assert_eq!(format!("{:04b}", a_b), "0011"); 53 | 54 | // Iterate over the flags like a normal set 55 | assert_eq!(a_b.iter().collect::>(), &[Test::A, Test::B]); 56 | 57 | // Query the contents with contains and intersects 58 | assert!(a_b.contains(Test::A)); 59 | assert!(b_c_d.contains(Test::B | Test::C)); 60 | assert!(!(b_c_d.contains(a_b))); 61 | 62 | assert!(a_b.intersects(a_c)); 63 | assert!(!(a_b.intersects(Test::C | Test::D))); 64 | ``` 65 | 66 | ## Optional Feature Flags 67 | 68 | - [`serde`](https://serde.rs/) implements `Serialize` and `Deserialize` 69 | for `BitFlags`. 70 | - `std` implements `std::error::Error` for `FromBitsError`. 71 | 72 | ## `const fn`-compatible APIs 73 | 74 | **Background:** The subset of `const fn` features currently stabilized is pretty limited. 75 | Most notably, [const traits are still at the RFC stage][const-trait-rfc], 76 | which makes it impossible to use any overloaded operators in a const 77 | context. 78 | 79 | **Naming convention:** If a separate, more limited function is provided 80 | for usage in a `const fn`, the name is suffixed with `_c`. 81 | 82 | Apart from functions whose name ends with `_c`, the [`make_bitflags!`] macro 83 | is often useful for many `const` and `const fn` usecases. 84 | 85 | **Blanket implementations:** If you attempt to write a `const fn` ranging 86 | over `T: BitFlag`, you will be met with an error explaining that currently, 87 | the only allowed trait bound for a `const fn` is `?Sized`. You will probably 88 | want to write a separate implementation for `BitFlags`, 89 | `BitFlags`, etc — best accomplished by a simple macro. 90 | 91 | **Documentation considerations:** The strategy described above is often used 92 | by `enumflags2` itself. To avoid clutter in the auto-generated documentation, 93 | the implementations for widths other than `u8` are marked with `#[doc(hidden)]`. 94 | 95 | ## Customizing `Default` 96 | 97 | By default, creating an instance of `BitFlags` with `Default` will result in an empty 98 | set. If that's undesirable, you may customize this: 99 | 100 | ```rust 101 | #[bitflags(default = B | C)] 102 | #[repr(u8)] 103 | #[derive(Copy, Clone, Debug, PartialEq)] 104 | enum Test { 105 | A = 0b0001, 106 | B = 0b0010, 107 | C = 0b0100, 108 | D = 0b1000, 109 | } 110 | 111 | assert_eq!(BitFlags::default(), Test::B | Test::C); 112 | ``` 113 | 114 | [const-trait-rfc]: https://github.com/rust-lang/rfcs/pull/2632 115 | -------------------------------------------------------------------------------- /benchmarks/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "enumflags-benchmarks" 3 | version = "0.0.0" 4 | publish = false 5 | edition = "2018" 6 | 7 | [dependencies.enumflags2] 8 | path = "../" 9 | 10 | [dev-dependencies] 11 | criterion = "0.3" 12 | 13 | [[bench]] 14 | name = "from_iterator" 15 | harness = false 16 | path = "from_iterator.rs" 17 | 18 | [[bench]] 19 | name = "iterator" 20 | harness = false 21 | path = "iterator.rs" 22 | -------------------------------------------------------------------------------- /benchmarks/from_iterator.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 2 | use enumflags2::{bitflags, BitFlags}; 3 | 4 | #[bitflags] 5 | #[repr(u16)] 6 | #[derive(Clone, Copy)] 7 | pub enum Test { 8 | Flag1 = 1 << 0, 9 | Flag2 = 1 << 1, 10 | Flag3 = 1 << 2, 11 | Flag4 = 1 << 3, 12 | Flag5 = 1 << 4, 13 | Flag6 = 1 << 5, 14 | Flag7 = 1 << 6, 15 | Flag8 = 1 << 7, 16 | Flag9 = 1 << 8, 17 | Flag10 = 1 << 9, 18 | Flag11 = 1 << 10, 19 | } 20 | 21 | pub fn iterators(c: &mut Criterion) { 22 | let v = vec![Test::Flag3, Test::Flag7, Test::Flag5, Test::Flag11]; 23 | 24 | let v2 = vec![Test::Flag10, Test::Flag3, Test::Flag1, Test::Flag4]; 25 | 26 | c.bench_function("simple iterator collect", |b| { 27 | b.iter(|| black_box(&v).iter().copied().collect::>()) 28 | }); 29 | 30 | c.bench_function("chained iterator collect", |b| { 31 | b.iter(|| { 32 | black_box(&v) 33 | .iter() 34 | .chain(black_box(&v2).iter()) 35 | .copied() 36 | .collect::>() 37 | }) 38 | }); 39 | 40 | c.bench_function("simple iterator extend", |b| { 41 | b.iter(|| { 42 | let mut flags = BitFlags::empty(); 43 | flags.extend(black_box(&v).iter().copied()); 44 | flags 45 | }) 46 | }); 47 | 48 | c.bench_function("chained iterator extend", |b| { 49 | b.iter(|| { 50 | let mut flags = BitFlags::empty(); 51 | flags.extend(black_box(&v).iter().chain(black_box(&v2).iter()).copied()); 52 | flags 53 | }) 54 | }); 55 | } 56 | 57 | criterion_group!(benches, iterators); 58 | criterion_main!(benches); 59 | -------------------------------------------------------------------------------- /benchmarks/iterator.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 2 | use enumflags2::{bitflags, BitFlags}; 3 | 4 | #[bitflags] 5 | #[repr(u16)] 6 | #[derive(Clone, Copy, Debug)] 7 | pub enum Test { 8 | Flag1 = 1 << 0, 9 | Flag2 = 1 << 1, 10 | Flag3 = 1 << 2, 11 | Flag4 = 1 << 3, 12 | Flag5 = 1 << 4, 13 | Flag6 = 1 << 5, 14 | Flag7 = 1 << 6, 15 | Flag8 = 1 << 7, 16 | Flag9 = 1 << 8, 17 | Flag10 = 1 << 9, 18 | Flag11 = 1 << 10, 19 | Flag12 = 1 << 11, 20 | } 21 | 22 | pub fn iterators(c: &mut Criterion) { 23 | let v1 = BitFlags::::from_bits(0x003).unwrap(); 24 | let v2 = BitFlags::::from_bits(0x691).unwrap(); 25 | let v3 = BitFlags::::from_bits(0xfed).unwrap(); 26 | 27 | c.bench_function("iterate (2/12)", |b| { 28 | b.iter(|| black_box(&v1).iter().collect::>()) 29 | }); 30 | 31 | c.bench_function("iterate (5/12)", |b| { 32 | b.iter(|| black_box(&v2).iter().collect::>()) 33 | }); 34 | 35 | c.bench_function("iterate (10/12)", |b| { 36 | b.iter(|| black_box(&v3).iter().collect::>()) 37 | }); 38 | } 39 | 40 | criterion_group!(benches, iterators); 41 | criterion_main!(benches); 42 | 43 | -------------------------------------------------------------------------------- /enumflags_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "enumflags2_derive" 3 | version = "0.7.11" 4 | authors = ["maik klein ", "Maja Kądziołka "] 5 | description = "Do not use directly, use the reexport in the `enumflags2` crate. This allows for better compatibility across versions." 6 | license = "MIT OR Apache-2.0" 7 | repository = "https://github.com/meithecatte/enumflags2" 8 | keywords = ["enum", "bitflag", "flag", "bitflags"] 9 | edition = "2018" 10 | rust-version = "1.56" 11 | 12 | [lib] 13 | proc-macro = true 14 | 15 | [dependencies.syn] 16 | version = "^2.0" 17 | features = ["parsing", "printing", "derive", "proc-macro"] 18 | default-features = false 19 | 20 | [dependencies] 21 | quote = "^1.0" 22 | proc-macro2 = "^1.0" 23 | -------------------------------------------------------------------------------- /enumflags_derive/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 14 | 15 | "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. 16 | 17 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 18 | 19 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 20 | 21 | "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. 22 | 23 | "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). 24 | 25 | "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. 26 | 27 | "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." 28 | 29 | "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. 30 | 31 | 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. 32 | 33 | 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. 34 | 35 | 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: 36 | 37 | You must give any other recipients of the Work or Derivative Works a copy of this License; and 38 | You must cause any modified files to carry prominent notices stating that You changed the files; and 39 | 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 40 | 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. 41 | 42 | 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. 43 | 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. 44 | 45 | 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. 46 | 47 | 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. 48 | 49 | 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. 50 | 51 | 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. 52 | 53 | END OF TERMS AND CONDITIONS 54 | 55 | 56 | Copyright [2017] [Maik Klein] 57 | 58 | Licensed under the Apache License, Version 2.0 (the "License"); 59 | you may not use this file except in compliance with the License. 60 | You may obtain a copy of the License at 61 | 62 | http://www.apache.org/licenses/LICENSE-2.0 63 | 64 | Unless required by applicable law or agreed to in writing, software 65 | distributed under the License is distributed on an "AS IS" BASIS, 66 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 67 | See the License for the specific language governing permissions and 68 | limitations under the License. 69 | -------------------------------------------------------------------------------- /enumflags_derive/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Maik Klein 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /enumflags_derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "2048"] 2 | extern crate proc_macro; 3 | #[macro_use] 4 | extern crate quote; 5 | 6 | use proc_macro2::{Span, TokenStream}; 7 | use std::convert::TryFrom; 8 | use syn::{ 9 | parse::{Parse, ParseStream}, 10 | parse_macro_input, 11 | spanned::Spanned, 12 | Expr, Ident, DeriveInput, Data, Token, Variant, 13 | }; 14 | 15 | struct Flag<'a> { 16 | name: Ident, 17 | span: Span, 18 | value: FlagValue<'a>, 19 | } 20 | 21 | enum FlagValue<'a> { 22 | Literal(u128), 23 | Deferred, 24 | Inferred(&'a mut Variant), 25 | } 26 | 27 | impl FlagValue<'_> { 28 | fn is_inferred(&self) -> bool { 29 | matches!(self, FlagValue::Inferred(_)) 30 | } 31 | } 32 | 33 | struct Parameters { 34 | default: Vec, 35 | } 36 | 37 | impl Parse for Parameters { 38 | fn parse(input: ParseStream) -> syn::parse::Result { 39 | if input.is_empty() { 40 | return Ok(Parameters { default: vec![] }); 41 | } 42 | 43 | input.parse::()?; 44 | input.parse::()?; 45 | let mut default = vec![input.parse()?]; 46 | while !input.is_empty() { 47 | input.parse::()?; 48 | default.push(input.parse()?); 49 | } 50 | 51 | Ok(Parameters { default }) 52 | } 53 | } 54 | 55 | #[proc_macro_attribute] 56 | pub fn bitflags_internal( 57 | attr: proc_macro::TokenStream, 58 | input: proc_macro::TokenStream, 59 | ) -> proc_macro::TokenStream { 60 | let Parameters { default } = parse_macro_input!(attr as Parameters); 61 | let mut ast = parse_macro_input!(input as DeriveInput); 62 | let output = gen_enumflags(&mut ast, default); 63 | 64 | output 65 | .unwrap_or_else(|err| { 66 | let error = err.to_compile_error(); 67 | quote! { 68 | #ast 69 | #error 70 | } 71 | }) 72 | .into() 73 | } 74 | 75 | /// Try to evaluate the expression given. 76 | fn fold_expr(expr: &syn::Expr) -> Option { 77 | match expr { 78 | Expr::Lit(ref expr_lit) => match expr_lit.lit { 79 | syn::Lit::Int(ref lit_int) => lit_int.base10_parse().ok(), 80 | _ => None, 81 | }, 82 | Expr::Binary(ref expr_binary) => { 83 | let l = fold_expr(&expr_binary.left)?; 84 | let r = fold_expr(&expr_binary.right)?; 85 | match &expr_binary.op { 86 | syn::BinOp::Shl(_) => u32::try_from(r).ok().and_then(|r| l.checked_shl(r)), 87 | _ => None, 88 | } 89 | } 90 | Expr::Paren(syn::ExprParen { expr, .. }) | Expr::Group(syn::ExprGroup { expr, .. }) => { 91 | fold_expr(expr) 92 | } 93 | _ => None, 94 | } 95 | } 96 | 97 | fn collect_flags<'a>( 98 | variants: impl Iterator, 99 | ) -> Result>, syn::Error> { 100 | variants 101 | .map(|variant| { 102 | if !matches!(variant.fields, syn::Fields::Unit) { 103 | return Err(syn::Error::new_spanned( 104 | &variant.fields, 105 | "Bitflag variants cannot contain additional data", 106 | )); 107 | } 108 | 109 | let name = variant.ident.clone(); 110 | let span = variant.span(); 111 | let value = if let Some(ref expr) = variant.discriminant { 112 | if let Some(n) = fold_expr(&expr.1) { 113 | FlagValue::Literal(n) 114 | } else { 115 | FlagValue::Deferred 116 | } 117 | } else { 118 | FlagValue::Inferred(variant) 119 | }; 120 | 121 | Ok(Flag { name, span, value }) 122 | }) 123 | .collect() 124 | } 125 | 126 | fn inferred_value(type_name: &Ident, previous_variants: &[Ident], repr: &Ident) -> Expr { 127 | let tokens = if previous_variants.is_empty() { 128 | quote!(1) 129 | } else { 130 | quote!(::enumflags2::_internal::next_bit( 131 | #(#type_name::#previous_variants as u128)|* 132 | ) as #repr) 133 | }; 134 | 135 | syn::parse2(tokens).expect("couldn't parse inferred value") 136 | } 137 | 138 | fn infer_values(flags: &mut [Flag], type_name: &Ident, repr: &Ident) { 139 | let mut previous_variants: Vec = flags 140 | .iter() 141 | .filter(|flag| !flag.value.is_inferred()) 142 | .map(|flag| flag.name.clone()) 143 | .collect(); 144 | 145 | for flag in flags { 146 | if let FlagValue::Inferred(ref mut variant) = flag.value { 147 | variant.discriminant = Some(( 148 | ::default(), 149 | inferred_value(type_name, &previous_variants, repr), 150 | )); 151 | previous_variants.push(flag.name.clone()); 152 | } 153 | } 154 | } 155 | 156 | /// Given a list of attributes, find the `repr`, if any, and return the integer 157 | /// type specified. 158 | fn extract_repr(attrs: &[syn::Attribute]) -> Result, syn::Error> { 159 | let mut res = None; 160 | for attr in attrs { 161 | if attr.path().is_ident("repr") { 162 | attr.parse_nested_meta(|meta| { 163 | if let Some(ident) = meta.path.get_ident() { 164 | res = Some(ident.clone()); 165 | } 166 | Ok(()) 167 | })?; 168 | } 169 | } 170 | Ok(res) 171 | } 172 | 173 | /// Check the repr and return the number of bits available 174 | fn type_bits(ty: &Ident) -> Result { 175 | // This would be so much easier if we could just match on an Ident... 176 | if ty == "usize" { 177 | Err(syn::Error::new_spanned( 178 | ty, 179 | "#[repr(usize)] is not supported. Use u32 or u64 instead.", 180 | )) 181 | } else if ty == "i8" 182 | || ty == "i16" 183 | || ty == "i32" 184 | || ty == "i64" 185 | || ty == "i128" 186 | || ty == "isize" 187 | { 188 | Err(syn::Error::new_spanned( 189 | ty, 190 | "Signed types in a repr are not supported.", 191 | )) 192 | } else if ty == "u8" { 193 | Ok(8) 194 | } else if ty == "u16" { 195 | Ok(16) 196 | } else if ty == "u32" { 197 | Ok(32) 198 | } else if ty == "u64" { 199 | Ok(64) 200 | } else if ty == "u128" { 201 | Ok(128) 202 | } else { 203 | Err(syn::Error::new_spanned( 204 | ty, 205 | "repr must be an integer type for #[bitflags].", 206 | )) 207 | } 208 | } 209 | 210 | /// Returns deferred checks 211 | fn check_flag(type_name: &Ident, flag: &Flag, bits: u8) -> Result, syn::Error> { 212 | use FlagValue::*; 213 | match flag.value { 214 | Literal(n) => { 215 | if !n.is_power_of_two() { 216 | Err(syn::Error::new( 217 | flag.span, 218 | "Flags must have exactly one set bit", 219 | )) 220 | } else if bits < 128 && n >= 1 << bits { 221 | Err(syn::Error::new( 222 | flag.span, 223 | format!("Flag value out of range for u{}", bits), 224 | )) 225 | } else { 226 | Ok(None) 227 | } 228 | } 229 | Inferred(_) => Ok(None), 230 | Deferred => { 231 | let variant_name = &flag.name; 232 | Ok(Some(quote_spanned!(flag.span => 233 | const _: 234 | <<[(); ( 235 | (#type_name::#variant_name as u128).is_power_of_two() 236 | ) as usize] as ::enumflags2::_internal::AssertionHelper> 237 | ::Status as ::enumflags2::_internal::ExactlyOneBitSet>::X 238 | = (); 239 | ))) 240 | } 241 | } 242 | } 243 | 244 | fn gen_enumflags(ast: &mut DeriveInput, default: Vec) -> Result { 245 | let ident = &ast.ident; 246 | 247 | let span = Span::call_site(); 248 | 249 | let ast_variants = match &mut ast.data { 250 | Data::Enum(ref mut data) => &mut data.variants, 251 | Data::Struct(data) => { 252 | return Err(syn::Error::new_spanned(&data.struct_token, 253 | "expected enum for #[bitflags], found struct")); 254 | } 255 | Data::Union(data) => { 256 | return Err(syn::Error::new_spanned(&data.union_token, 257 | "expected enum for #[bitflags], found union")); 258 | } 259 | }; 260 | 261 | if ast.generics.lt_token.is_some() || ast.generics.where_clause.is_some() { 262 | return Err(syn::Error::new_spanned(&ast.generics, 263 | "bitflags cannot be generic")); 264 | } 265 | 266 | let repr = extract_repr(&ast.attrs)? 267 | .ok_or_else(|| syn::Error::new_spanned(ident, 268 | "repr attribute missing. Add #[repr(u64)] or a similar attribute to specify the size of the bitfield."))?; 269 | let bits = type_bits(&repr)?; 270 | 271 | let mut variants = collect_flags(ast_variants.iter_mut())?; 272 | let deferred = variants 273 | .iter() 274 | .flat_map(|variant| check_flag(ident, variant, bits).transpose()) 275 | .collect::, _>>()?; 276 | 277 | infer_values(&mut variants, ident, &repr); 278 | 279 | if (bits as usize) < variants.len() { 280 | return Err(syn::Error::new_spanned( 281 | &repr, 282 | format!("Not enough bits for {} flags", variants.len()), 283 | )); 284 | } 285 | 286 | let std = quote_spanned!(span => ::enumflags2::_internal::core); 287 | let ast_variants = match &ast.data { 288 | Data::Enum(ref data) => &data.variants, 289 | _ => unreachable!(), 290 | }; 291 | 292 | let variant_names = ast_variants.iter().map(|v| &v.ident).collect::>(); 293 | 294 | Ok(quote_spanned! { 295 | span => 296 | #ast 297 | #(#deferred)* 298 | impl #std::ops::Not for #ident { 299 | type Output = ::enumflags2::BitFlags; 300 | #[inline(always)] 301 | fn not(self) -> Self::Output { 302 | use ::enumflags2::BitFlags; 303 | BitFlags::from_flag(self).not() 304 | } 305 | } 306 | 307 | impl #std::ops::BitOr for #ident { 308 | type Output = ::enumflags2::BitFlags; 309 | #[inline(always)] 310 | fn bitor(self, other: Self) -> Self::Output { 311 | use ::enumflags2::BitFlags; 312 | BitFlags::from_flag(self) | other 313 | } 314 | } 315 | 316 | impl #std::ops::BitAnd for #ident { 317 | type Output = ::enumflags2::BitFlags; 318 | #[inline(always)] 319 | fn bitand(self, other: Self) -> Self::Output { 320 | use ::enumflags2::BitFlags; 321 | BitFlags::from_flag(self) & other 322 | } 323 | } 324 | 325 | impl #std::ops::BitXor for #ident { 326 | type Output = ::enumflags2::BitFlags; 327 | #[inline(always)] 328 | fn bitxor(self, other: Self) -> Self::Output { 329 | use ::enumflags2::BitFlags; 330 | BitFlags::from_flag(self) ^ other 331 | } 332 | } 333 | 334 | unsafe impl ::enumflags2::_internal::RawBitFlags for #ident { 335 | type Numeric = #repr; 336 | 337 | const EMPTY: Self::Numeric = 0; 338 | 339 | const DEFAULT: Self::Numeric = 340 | 0 #(| (Self::#default as #repr))*; 341 | 342 | const ALL_BITS: Self::Numeric = 343 | 0 #(| (Self::#variant_names as #repr))*; 344 | 345 | const BITFLAGS_TYPE_NAME : &'static str = 346 | concat!("BitFlags<", stringify!(#ident), ">"); 347 | 348 | fn bits(self) -> Self::Numeric { 349 | self as #repr 350 | } 351 | } 352 | 353 | impl ::enumflags2::BitFlag for #ident {} 354 | }) 355 | } 356 | -------------------------------------------------------------------------------- /src/const_api.rs: -------------------------------------------------------------------------------- 1 | use crate::{BitFlags, BitFlag}; 2 | use core::marker::PhantomData; 3 | 4 | /// Workaround for `const fn` limitations. 5 | /// 6 | /// Some `const fn`s in this crate will need an instance of this type 7 | /// for some type-level information usually provided by traits. 8 | /// 9 | /// A token can be obtained from [`BitFlags::CONST_TOKEN`]. The relevant types 10 | /// should be readily inferred from context. 11 | /// 12 | /// For an example of usage, see [`not_c`][BitFlags::not_c]. 13 | pub struct ConstToken(BitFlags); 14 | 15 | impl BitFlags 16 | where 17 | T: BitFlag, 18 | { 19 | /// An empty `BitFlags`. Equivalent to [`empty()`][BitFlags::empty], 20 | /// but works in a const context. 21 | pub const EMPTY: Self = BitFlags { 22 | val: T::EMPTY, 23 | marker: PhantomData, 24 | }; 25 | 26 | /// A `BitFlags` with all flags set. Equivalent to [`all()`][BitFlags::all], 27 | /// but works in a const context. 28 | pub const ALL: Self = BitFlags { 29 | val: T::ALL_BITS, 30 | marker: PhantomData, 31 | }; 32 | 33 | /// A [`ConstToken`] for this type of flag. 34 | pub const CONST_TOKEN: ConstToken = ConstToken(Self::ALL); 35 | } 36 | 37 | for_each_uint! { $ty $hide_docs => 38 | impl BitFlags { 39 | /// Create a new BitFlags unsafely, without checking if the bits form 40 | /// a valid bit pattern for the type. 41 | /// 42 | /// Const variant of 43 | /// [`from_bits_unchecked`][BitFlags::from_bits_unchecked]. 44 | /// 45 | /// Consider using 46 | /// [`from_bits_truncate_c`][BitFlags::from_bits_truncate_c] instead. 47 | /// 48 | /// # Safety 49 | /// 50 | /// All bits set in `val` must correspond to a value of the enum. 51 | #[must_use] 52 | #[inline(always)] 53 | $(#[$hide_docs])? 54 | pub const unsafe fn from_bits_unchecked_c( 55 | val: $ty, const_token: ConstToken 56 | ) -> Self { 57 | let _ = const_token; 58 | BitFlags { 59 | val, 60 | marker: PhantomData, 61 | } 62 | } 63 | 64 | /// Create a `BitFlags` from an underlying bitwise value. If any 65 | /// invalid bits are set, ignore them. 66 | /// 67 | /// ``` 68 | /// # use enumflags2::{bitflags, BitFlags}; 69 | /// #[bitflags] 70 | /// #[repr(u8)] 71 | /// #[derive(Clone, Copy, Debug, PartialEq, Eq)] 72 | /// enum MyFlag { 73 | /// One = 1 << 0, 74 | /// Two = 1 << 1, 75 | /// Three = 1 << 2, 76 | /// } 77 | /// 78 | /// const FLAGS: BitFlags = 79 | /// BitFlags::::from_bits_truncate_c(0b10101010, BitFlags::CONST_TOKEN); 80 | /// assert_eq!(FLAGS, MyFlag::Two); 81 | /// ``` 82 | #[must_use] 83 | #[inline(always)] 84 | $(#[$hide_docs])? 85 | pub const fn from_bits_truncate_c( 86 | bits: $ty, const_token: ConstToken 87 | ) -> Self { 88 | BitFlags { 89 | val: bits & const_token.0.val, 90 | marker: PhantomData, 91 | } 92 | } 93 | 94 | /// Bitwise OR — return value contains flag if either argument does. 95 | /// 96 | /// Also available as `a | b`, but operator overloads are not usable 97 | /// in `const fn`s at the moment. 98 | #[must_use] 99 | #[inline(always)] 100 | $(#[$hide_docs])? 101 | pub const fn union_c(self, other: Self) -> Self { 102 | BitFlags { 103 | val: self.val | other.val, 104 | marker: PhantomData, 105 | } 106 | } 107 | 108 | /// Bitwise AND — return value contains flag if both arguments do. 109 | /// 110 | /// Also available as `a & b`, but operator overloads are not usable 111 | /// in `const fn`s at the moment. 112 | #[must_use] 113 | #[inline(always)] 114 | $(#[$hide_docs])? 115 | pub const fn intersection_c(self, other: Self) -> Self { 116 | BitFlags { 117 | val: self.val & other.val, 118 | marker: PhantomData, 119 | } 120 | } 121 | 122 | /// Bitwise NOT — return value contains flag if argument doesn't. 123 | /// 124 | /// Also available as `!a`, but operator overloads are not usable 125 | /// in `const fn`s at the moment. 126 | /// 127 | /// Moreover, due to `const fn` limitations, `not_c` needs a 128 | /// [`ConstToken`] as an argument. 129 | /// 130 | /// ``` 131 | /// # use enumflags2::{bitflags, BitFlags, make_bitflags}; 132 | /// #[bitflags] 133 | /// #[repr(u8)] 134 | /// #[derive(Clone, Copy, Debug, PartialEq, Eq)] 135 | /// enum MyFlag { 136 | /// One = 1 << 0, 137 | /// Two = 1 << 1, 138 | /// Three = 1 << 2, 139 | /// } 140 | /// 141 | /// const FLAGS: BitFlags = make_bitflags!(MyFlag::{One | Two}); 142 | /// const NEGATED: BitFlags = FLAGS.not_c(BitFlags::CONST_TOKEN); 143 | /// assert_eq!(NEGATED, MyFlag::Three); 144 | /// ``` 145 | #[must_use] 146 | #[inline(always)] 147 | $(#[$hide_docs])? 148 | pub const fn not_c(self, const_token: ConstToken) -> Self { 149 | BitFlags { 150 | val: !self.val & const_token.0.val, 151 | marker: PhantomData, 152 | } 153 | } 154 | 155 | /// Returns the underlying bitwise value. 156 | /// 157 | /// `const` variant of [`bits`][BitFlags::bits]. 158 | #[inline(always)] 159 | $(#[$hide_docs])? 160 | pub const fn bits_c(self) -> $ty { 161 | self.val 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/fallible.rs: -------------------------------------------------------------------------------- 1 | use super::BitFlag; 2 | use super::BitFlags; 3 | use core::convert::TryFrom; 4 | use core::fmt; 5 | 6 | // Coherence doesn't let us use a generic type here. Work around by implementing 7 | // for each integer type manually. 8 | for_each_uint! { $ty $hide_docs => 9 | impl TryFrom<$ty> for BitFlags 10 | where 11 | T: BitFlag, 12 | { 13 | type Error = FromBitsError; 14 | 15 | fn try_from(bits: T::Numeric) -> Result { 16 | Self::from_bits(bits) 17 | } 18 | } 19 | } 20 | 21 | /// The error struct used by [`BitFlags::from_bits`] 22 | /// and the [`TryFrom`] implementation for invalid values. 23 | /// 24 | /// Note that the implementation of [`std::error::Error`] 25 | /// for this type is gated on the `std` feature flag. 26 | /// 27 | /// ``` 28 | /// # use std::convert::TryInto; 29 | /// # use enumflags2::{bitflags, BitFlags}; 30 | /// #[bitflags] 31 | /// #[derive(Clone, Copy, Debug)] 32 | /// #[repr(u8)] 33 | /// enum MyFlags { 34 | /// A = 0b0001, 35 | /// B = 0b0010, 36 | /// C = 0b0100, 37 | /// D = 0b1000, 38 | /// } 39 | /// 40 | /// let result: Result, _> = 0b10101u8.try_into(); 41 | /// assert!(result.is_err()); 42 | /// let error = result.unwrap_err(); 43 | /// assert_eq!(error.truncate(), MyFlags::C | MyFlags::A); 44 | /// assert_eq!(error.invalid_bits(), 0b10000); 45 | /// ``` 46 | #[derive(Debug, Copy, Clone)] 47 | pub struct FromBitsError { 48 | pub(crate) flags: BitFlags, 49 | pub(crate) invalid: T::Numeric, 50 | } 51 | 52 | impl FromBitsError { 53 | /// Return the truncated result of the conversion. 54 | pub fn truncate(self) -> BitFlags { 55 | self.flags 56 | } 57 | 58 | /// Return the bits that didn't correspond to any flags. 59 | pub fn invalid_bits(self) -> T::Numeric { 60 | self.invalid 61 | } 62 | } 63 | 64 | impl fmt::Display for FromBitsError { 65 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 66 | write!( 67 | fmt, 68 | "Invalid bits for {:?}: {:#b}", 69 | self.flags, self.invalid 70 | ) 71 | } 72 | } 73 | 74 | #[cfg(feature = "std")] 75 | impl std::error::Error for FromBitsError { 76 | fn description(&self) -> &str { 77 | "invalid bitflags representation" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/formatting.rs: -------------------------------------------------------------------------------- 1 | use crate::{BitFlag, BitFlags}; 2 | use core::fmt::{self, Binary, Debug}; 3 | 4 | impl fmt::Debug for BitFlags 5 | where 6 | T: BitFlag + fmt::Debug, 7 | { 8 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 9 | let name = T::BITFLAGS_TYPE_NAME; 10 | let bits = DebugBinaryFormatter(&self.val); 11 | let iter = if !self.is_empty() { 12 | Some(FlagFormatter(self.iter())) 13 | } else { 14 | None 15 | }; 16 | 17 | if !fmt.alternate() { 18 | // Concise tuple formatting is a better default 19 | let mut debug = fmt.debug_tuple(name); 20 | debug.field(&bits); 21 | if let Some(iter) = iter { 22 | debug.field(&iter); 23 | } 24 | debug.finish() 25 | } else { 26 | // Pretty-printed tuples are ugly and hard to read, so use struct format 27 | let mut debug = fmt.debug_struct(name); 28 | debug.field("bits", &bits); 29 | if let Some(iter) = iter { 30 | debug.field("flags", &iter); 31 | } 32 | debug.finish() 33 | } 34 | } 35 | } 36 | 37 | impl fmt::Display for BitFlags 38 | where 39 | T: BitFlag + fmt::Debug, 40 | { 41 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 42 | fmt::Debug::fmt(&FlagFormatter(self.iter()), fmt) 43 | } 44 | } 45 | 46 | impl fmt::Binary for BitFlags 47 | where 48 | T: BitFlag, 49 | T::Numeric: fmt::Binary, 50 | { 51 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 52 | fmt::Binary::fmt(&self.bits(), fmt) 53 | } 54 | } 55 | 56 | impl fmt::Octal for BitFlags 57 | where 58 | T: BitFlag, 59 | T::Numeric: fmt::Octal, 60 | { 61 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 62 | fmt::Octal::fmt(&self.bits(), fmt) 63 | } 64 | } 65 | 66 | impl fmt::LowerHex for BitFlags 67 | where 68 | T: BitFlag, 69 | T::Numeric: fmt::LowerHex, 70 | { 71 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 72 | fmt::LowerHex::fmt(&self.bits(), fmt) 73 | } 74 | } 75 | 76 | impl fmt::UpperHex for BitFlags 77 | where 78 | T: BitFlag, 79 | T::Numeric: fmt::UpperHex, 80 | { 81 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 82 | fmt::UpperHex::fmt(&self.bits(), fmt) 83 | } 84 | } 85 | 86 | // Format an iterator of flags into "A | B | etc" 87 | struct FlagFormatter(I); 88 | 89 | impl> Debug for FlagFormatter { 90 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 91 | let mut iter = self.0.clone(); 92 | if let Some(val) = iter.next() { 93 | Debug::fmt(&val, fmt)?; 94 | for val in iter { 95 | fmt.write_str(" | ")?; 96 | Debug::fmt(&val, fmt)?; 97 | } 98 | Ok(()) 99 | } else { 100 | fmt.write_str("") 101 | } 102 | } 103 | } 104 | 105 | // A formatter that obeys format arguments but falls back to binary when 106 | // no explicit format is requested. Supports {:08?}, {:08x?}, etc. 107 | struct DebugBinaryFormatter<'a, F>(&'a F); 108 | 109 | impl<'a, F: Debug + Binary + 'a> Debug for DebugBinaryFormatter<'a, F> { 110 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 111 | // Check if {:x?} or {:X?} was used; this is determined via the 112 | // discriminator of core::fmt::FlagV1::{DebugLowerHex, DebugUpperHex}, 113 | // which is not an accessible type: https://github.com/rust-lang/rust/blob/d65e272a9fe3e61aa5f229c5358e35a909435575/src/libcore/fmt/mod.rs#L306 114 | // See also: https://github.com/rust-lang/rfcs/pull/2226 115 | #[allow(deprecated)] 116 | let format_hex = fmt.flags() >> 4; 117 | let width = fmt.width().unwrap_or(0); 118 | 119 | if format_hex & 1 != 0 { 120 | // FlagV1::DebugLowerHex 121 | write!(fmt, "{:#0width$x?}", &self.0, width = width) 122 | } else if format_hex & 2 != 0 { 123 | // FlagV1::DebugUpperHex 124 | write!(fmt, "{:#0width$X?}", &self.0, width = width) 125 | } else { 126 | // Fall back to binary otheriwse 127 | write!(fmt, "{:#0width$b}", &self.0, width = width) 128 | } 129 | } 130 | } 131 | 132 | #[test] 133 | fn flag_formatter() { 134 | use core::iter; 135 | 136 | macro_rules! assert_fmt { 137 | ($fmt:expr, $expr:expr, $expected:expr) => { 138 | assert_eq!(format!($fmt, FlagFormatter($expr)), $expected) 139 | }; 140 | } 141 | 142 | assert_fmt!("{:?}", iter::empty::(), ""); 143 | assert_fmt!("{:?}", iter::once(1), "1"); 144 | assert_fmt!("{:?}", [1, 2].iter(), "1 | 2"); 145 | assert_fmt!("{:?}", [1, 2, 10].iter(), "1 | 2 | 10"); 146 | assert_fmt!("{:02x?}", [1, 2, 10].iter(), "01 | 02 | 0a"); 147 | assert_fmt!("{:#04X?}", [1, 2, 10].iter(), "0x01 | 0x02 | 0x0A"); 148 | } 149 | 150 | #[test] 151 | fn debug_binary_formatter() { 152 | macro_rules! assert_fmt { 153 | ($fmt:expr, $expr:expr, $expected:expr) => { 154 | assert_eq!(format!($fmt, DebugBinaryFormatter(&$expr)), $expected) 155 | }; 156 | } 157 | 158 | assert_fmt!("{:?}", 10, "0b1010"); 159 | assert_fmt!("{:#?}", 10, "0b1010"); 160 | assert_fmt!("{:010?}", 10, "0b00001010"); 161 | assert_fmt!("{:010x?}", 10, "0x0000000a"); 162 | assert_fmt!("{:#010X?}", 10, "0x0000000A"); 163 | } 164 | -------------------------------------------------------------------------------- /src/iter.rs: -------------------------------------------------------------------------------- 1 | use crate::{BitFlag, BitFlags, BitFlagNum}; 2 | use core::iter::{FromIterator, FusedIterator}; 3 | 4 | impl BitFlags 5 | where 6 | T: BitFlag, 7 | { 8 | /// Iterate over the `BitFlags`. 9 | /// 10 | /// ``` 11 | /// # use enumflags2::{bitflags, make_bitflags}; 12 | /// # #[bitflags] 13 | /// # #[derive(Clone, Copy, PartialEq, Debug)] 14 | /// # #[repr(u8)] 15 | /// # enum MyFlag { 16 | /// # A = 1 << 0, 17 | /// # B = 1 << 1, 18 | /// # C = 1 << 2, 19 | /// # } 20 | /// let flags = make_bitflags!(MyFlag::{A | C}); 21 | /// 22 | /// flags.iter() 23 | /// .for_each(|flag| println!("{:?}", flag)); 24 | /// ``` 25 | #[inline] 26 | pub fn iter(self) -> Iter { 27 | Iter { rest: self } 28 | } 29 | } 30 | 31 | impl IntoIterator for BitFlags { 32 | type IntoIter = Iter; 33 | type Item = T; 34 | 35 | fn into_iter(self) -> Self::IntoIter { 36 | self.iter() 37 | } 38 | } 39 | 40 | /// Iterator that yields each flag set in a `BitFlags`. 41 | #[derive(Clone, Debug)] 42 | pub struct Iter { 43 | rest: BitFlags, 44 | } 45 | 46 | impl Iterator for Iter 47 | where 48 | T: BitFlag, 49 | { 50 | type Item = T; 51 | 52 | fn next(&mut self) -> Option { 53 | if self.rest.is_empty() { 54 | None 55 | } else { 56 | // SAFETY: `flag` will be a single bit, because 57 | // x & -x = x & (~x + 1), and the increment causes only one 0 -> 1 transition. 58 | // The invariant of `from_bits_unchecked` is satisfied, because bits & x 59 | // is a subset of bits, which we know are the valid bits. 60 | unsafe { 61 | let bits = self.rest.bits(); 62 | let flag: T::Numeric = bits & bits.wrapping_neg(); 63 | let flag: T = core::mem::transmute_copy(&flag); 64 | self.rest = BitFlags::from_bits_unchecked(bits & (bits - BitFlagNum::ONE)); 65 | Some(flag) 66 | } 67 | } 68 | } 69 | 70 | fn size_hint(&self) -> (usize, Option) { 71 | let l = self.rest.len(); 72 | (l, Some(l)) 73 | } 74 | } 75 | 76 | impl ExactSizeIterator for Iter 77 | where 78 | T: BitFlag, 79 | { 80 | fn len(&self) -> usize { 81 | self.rest.len() 82 | } 83 | } 84 | 85 | impl FusedIterator for Iter {} 86 | 87 | impl FromIterator for BitFlags 88 | where 89 | T: BitFlag, 90 | B: Into>, 91 | { 92 | #[inline] 93 | fn from_iter(it: I) -> BitFlags 94 | where 95 | I: IntoIterator, 96 | { 97 | it.into_iter() 98 | .fold(BitFlags::empty(), |acc, flag| acc | flag) 99 | } 100 | } 101 | 102 | impl Extend for BitFlags 103 | where 104 | T: BitFlag, 105 | B: Into>, 106 | { 107 | #[inline] 108 | fn extend(&mut self, it: I) 109 | where 110 | I: IntoIterator, 111 | { 112 | *self = it.into_iter().fold(*self, |acc, flag| acc | flag) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Enum Flags 2 | //! `enumflags2` implements the classic bitflags datastructure. Annotate an enum 3 | //! with `#[bitflags]`, and `BitFlags` will be able to hold arbitrary combinations 4 | //! of your enum within the space of a single integer. 5 | //! 6 | //! Unlike other crates, `enumflags2` makes the type-level distinction between 7 | //! a single flag (`YourEnum`) and a set of flags (`BitFlags`). 8 | //! This allows idiomatic handling of bitflags, such as with `match` and `iter`. 9 | //! 10 | //! ## Example 11 | //! ``` 12 | //! use enumflags2::{bitflags, make_bitflags, BitFlags}; 13 | //! 14 | //! #[bitflags] 15 | //! #[repr(u8)] 16 | //! #[derive(Copy, Clone, Debug, PartialEq)] 17 | //! enum Test { 18 | //! A = 0b0001, 19 | //! B = 0b0010, 20 | //! C, // unspecified variants pick unused bits automatically 21 | //! D = 0b1000, 22 | //! } 23 | //! 24 | //! // Flags can be combined with |, this creates a BitFlags of your type: 25 | //! let a_b: BitFlags = Test::A | Test::B; 26 | //! let a_c = Test::A | Test::C; 27 | //! let b_c_d = make_bitflags!(Test::{B | C | D}); 28 | //! 29 | //! // The debug output lets you inspect both the numeric value and 30 | //! // the actual flags: 31 | //! assert_eq!(format!("{:?}", a_b), "BitFlags(0b11, A | B)"); 32 | //! 33 | //! // But if you'd rather see only one of those, that's available too: 34 | //! assert_eq!(format!("{}", a_b), "A | B"); 35 | //! assert_eq!(format!("{:04b}", a_b), "0011"); 36 | //! 37 | //! // Iterate over the flags like a normal set 38 | //! assert_eq!(a_b.iter().collect::>(), &[Test::A, Test::B]); 39 | //! 40 | //! // Query the contents with contains and intersects 41 | //! assert!(a_b.contains(Test::A)); 42 | //! assert!(b_c_d.contains(Test::B | Test::C)); 43 | //! assert!(!(b_c_d.contains(a_b))); 44 | //! 45 | //! assert!(a_b.intersects(a_c)); 46 | //! assert!(!(a_b.intersects(Test::C | Test::D))); 47 | //! ``` 48 | //! 49 | //! ## Optional Feature Flags 50 | //! 51 | //! - [`serde`](https://serde.rs/) implements `Serialize` and `Deserialize` 52 | //! for `BitFlags`. 53 | //! - `std` implements `std::error::Error` for `FromBitsError`. 54 | //! 55 | //! ## `const fn`-compatible APIs 56 | //! 57 | //! **Background:** The subset of `const fn` features currently stabilized is pretty limited. 58 | //! Most notably, [const traits are still at the RFC stage][const-trait-rfc], 59 | //! which makes it impossible to use any overloaded operators in a const 60 | //! context. 61 | //! 62 | //! **Naming convention:** If a separate, more limited function is provided 63 | //! for usage in a `const fn`, the name is suffixed with `_c`. 64 | //! 65 | //! Apart from functions whose name ends with `_c`, the [`make_bitflags!`] macro 66 | //! is often useful for many `const` and `const fn` usecases. 67 | //! 68 | //! **Blanket implementations:** If you attempt to write a `const fn` ranging 69 | //! over `T: BitFlag`, you will be met with an error explaining that currently, 70 | //! the only allowed trait bound for a `const fn` is `?Sized`. You will probably 71 | //! want to write a separate implementation for `BitFlags`, 72 | //! `BitFlags`, etc — best accomplished by a simple macro. 73 | //! 74 | //! **Documentation considerations:** The strategy described above is often used 75 | //! by `enumflags2` itself. To avoid clutter in the auto-generated documentation, 76 | //! the implementations for widths other than `u8` are marked with `#[doc(hidden)]`. 77 | //! 78 | //! ## Customizing `Default` 79 | //! 80 | //! By default, creating an instance of `BitFlags` with `Default` will result in an empty 81 | //! set. If that's undesirable, you may customize this: 82 | //! 83 | //! ``` 84 | //! # use enumflags2::{BitFlags, bitflags}; 85 | //! #[bitflags(default = B | C)] 86 | //! #[repr(u8)] 87 | //! #[derive(Copy, Clone, Debug, PartialEq)] 88 | //! enum Test { 89 | //! A = 0b0001, 90 | //! B = 0b0010, 91 | //! C = 0b0100, 92 | //! D = 0b1000, 93 | //! } 94 | //! 95 | //! assert_eq!(BitFlags::default(), Test::B | Test::C); 96 | //! ``` 97 | //! 98 | //! [const-trait-rfc]: https://github.com/rust-lang/rfcs/pull/2632 99 | #![warn(missing_docs)] 100 | #![cfg_attr(all(not(test), not(feature = "std")), no_std)] 101 | 102 | use core::hash::{Hash, Hasher}; 103 | use core::marker::PhantomData; 104 | use core::{cmp, ops}; 105 | 106 | #[allow(unused_imports)] 107 | #[macro_use] 108 | extern crate enumflags2_derive; 109 | 110 | #[doc(hidden)] 111 | pub use enumflags2_derive::bitflags_internal as bitflags; 112 | 113 | // Internal macro: expand into a separate copy for each supported numeric type. 114 | macro_rules! for_each_uint { 115 | ( $d:tt $tyvar:ident $dd:tt $docattr:ident => $($input:tt)* ) => { 116 | macro_rules! implement { 117 | ( $d $tyvar:ty => $d($d $docattr:meta)? ) => { 118 | $($input)* 119 | } 120 | } 121 | 122 | implement! { u8 => } 123 | implement! { u16 => doc(hidden) } 124 | implement! { u32 => doc(hidden) } 125 | implement! { u64 => doc(hidden) } 126 | implement! { u128 => doc(hidden) } 127 | } 128 | } 129 | 130 | /// A trait automatically implemented by `#[bitflags]` to make the enum 131 | /// a valid type parameter for `BitFlags`. 132 | pub trait BitFlag: Copy + Clone + 'static + _internal::RawBitFlags { 133 | /// Create a `BitFlags` with no flags set (in other words, with a value of 0). 134 | /// 135 | /// This is a convenience reexport of [`BitFlags::empty`]. It can be called with 136 | /// `MyFlag::empty()`, thus bypassing the need for type hints in some situations. 137 | /// 138 | /// ``` 139 | /// # use enumflags2::{bitflags, BitFlags}; 140 | /// #[bitflags] 141 | /// #[repr(u8)] 142 | /// #[derive(Clone, Copy, PartialEq, Eq)] 143 | /// enum MyFlag { 144 | /// One = 1 << 0, 145 | /// Two = 1 << 1, 146 | /// Three = 1 << 2, 147 | /// } 148 | /// 149 | /// use enumflags2::BitFlag; 150 | /// 151 | /// let empty = MyFlag::empty(); 152 | /// assert!(empty.is_empty()); 153 | /// assert_eq!(empty.contains(MyFlag::One), false); 154 | /// assert_eq!(empty.contains(MyFlag::Two), false); 155 | /// assert_eq!(empty.contains(MyFlag::Three), false); 156 | /// ``` 157 | #[inline] 158 | fn empty() -> BitFlags { 159 | BitFlags::empty() 160 | } 161 | 162 | /// Create a `BitFlags` with all flags set. 163 | /// 164 | /// This is a convenience reexport of [`BitFlags::all`]. It can be called with 165 | /// `MyFlag::all()`, thus bypassing the need for type hints in some situations. 166 | /// 167 | /// ``` 168 | /// # use enumflags2::{bitflags, BitFlags}; 169 | /// #[bitflags] 170 | /// #[repr(u8)] 171 | /// #[derive(Clone, Copy, PartialEq, Eq)] 172 | /// enum MyFlag { 173 | /// One = 1 << 0, 174 | /// Two = 1 << 1, 175 | /// Three = 1 << 2, 176 | /// } 177 | /// 178 | /// use enumflags2::BitFlag; 179 | /// 180 | /// let all = MyFlag::all(); 181 | /// assert!(all.is_all()); 182 | /// assert_eq!(all.contains(MyFlag::One), true); 183 | /// assert_eq!(all.contains(MyFlag::Two), true); 184 | /// assert_eq!(all.contains(MyFlag::Three), true); 185 | /// ``` 186 | #[inline] 187 | fn all() -> BitFlags { 188 | BitFlags::all() 189 | } 190 | 191 | /// Create a `BitFlags` if the raw value provided does not contain 192 | /// any illegal flags. 193 | /// 194 | /// This is a convenience reexport of [`BitFlags::from_bits`]. It can be called 195 | /// with `MyFlag::from_bits(bits)`, thus bypassing the need for type hints in 196 | /// some situations. 197 | /// 198 | /// ``` 199 | /// # use enumflags2::{bitflags, BitFlags}; 200 | /// #[bitflags] 201 | /// #[repr(u8)] 202 | /// #[derive(Clone, Copy, PartialEq, Eq, Debug)] 203 | /// enum MyFlag { 204 | /// One = 1 << 0, 205 | /// Two = 1 << 1, 206 | /// Three = 1 << 2, 207 | /// } 208 | /// 209 | /// use enumflags2::BitFlag; 210 | /// 211 | /// let flags = MyFlag::from_bits(0b11).unwrap(); 212 | /// assert_eq!(flags.contains(MyFlag::One), true); 213 | /// assert_eq!(flags.contains(MyFlag::Two), true); 214 | /// assert_eq!(flags.contains(MyFlag::Three), false); 215 | /// let invalid = MyFlag::from_bits(1 << 3); 216 | /// assert!(invalid.is_err()); 217 | /// ``` 218 | #[inline] 219 | fn from_bits(bits: Self::Numeric) -> Result, FromBitsError> { 220 | BitFlags::from_bits(bits) 221 | } 222 | 223 | /// Create a `BitFlags` from an underlying bitwise value. If any 224 | /// invalid bits are set, ignore them. 225 | /// 226 | /// This is a convenience reexport of [`BitFlags::from_bits_truncate`]. It can be 227 | /// called with `MyFlag::from_bits_truncate(bits)`, thus bypassing the need for 228 | /// type hints in some situations. 229 | /// 230 | /// ``` 231 | /// # use enumflags2::{bitflags, BitFlags}; 232 | /// #[bitflags] 233 | /// #[repr(u8)] 234 | /// #[derive(Clone, Copy, PartialEq, Eq)] 235 | /// enum MyFlag { 236 | /// One = 1 << 0, 237 | /// Two = 1 << 1, 238 | /// Three = 1 << 2, 239 | /// } 240 | /// 241 | /// use enumflags2::BitFlag; 242 | /// 243 | /// let flags = MyFlag::from_bits_truncate(0b1_1011); 244 | /// assert_eq!(flags.contains(MyFlag::One), true); 245 | /// assert_eq!(flags.contains(MyFlag::Two), true); 246 | /// assert_eq!(flags.contains(MyFlag::Three), false); 247 | /// ``` 248 | #[inline] 249 | fn from_bits_truncate(bits: Self::Numeric) -> BitFlags { 250 | BitFlags::from_bits_truncate(bits) 251 | } 252 | 253 | /// Create a `BitFlags` unsafely, without checking if the bits form 254 | /// a valid bit pattern for the type. 255 | /// 256 | /// Consider using [`from_bits`][BitFlag::from_bits] 257 | /// or [`from_bits_truncate`][BitFlag::from_bits_truncate] instead. 258 | /// 259 | /// # Safety 260 | /// 261 | /// All bits set in `val` must correspond to a value of the enum. 262 | /// 263 | /// # Example 264 | /// 265 | /// This is a convenience reexport of [`BitFlags::from_bits_unchecked`]. It can be 266 | /// called with `MyFlag::from_bits_unchecked(bits)`, thus bypassing the need for 267 | /// type hints in some situations. 268 | /// 269 | /// ``` 270 | /// # use enumflags2::{bitflags, BitFlags}; 271 | /// #[bitflags] 272 | /// #[repr(u8)] 273 | /// #[derive(Clone, Copy, PartialEq, Eq)] 274 | /// enum MyFlag { 275 | /// One = 1 << 0, 276 | /// Two = 1 << 1, 277 | /// Three = 1 << 2, 278 | /// } 279 | /// 280 | /// use enumflags2::BitFlag; 281 | /// 282 | /// let flags = unsafe { 283 | /// MyFlag::from_bits_unchecked(0b011) 284 | /// }; 285 | /// 286 | /// assert_eq!(flags.contains(MyFlag::One), true); 287 | /// assert_eq!(flags.contains(MyFlag::Two), true); 288 | /// assert_eq!(flags.contains(MyFlag::Three), false); 289 | /// ``` 290 | #[inline] 291 | unsafe fn from_bits_unchecked(bits: Self::Numeric) -> BitFlags { 292 | BitFlags::from_bits_unchecked(bits) 293 | } 294 | } 295 | 296 | /// While the module is public, this is only the case because it needs to be 297 | /// accessed by the macro. Do not use this directly. Stability guarantees 298 | /// don't apply. 299 | #[doc(hidden)] 300 | pub mod _internal { 301 | /// A trait automatically implemented by `#[bitflags]` to make the enum 302 | /// a valid type parameter for `BitFlags`. 303 | /// 304 | /// # Safety 305 | /// 306 | /// The values should reflect reality, like they do if the implementation 307 | /// is generated by the procmacro. 308 | /// 309 | /// `bits` must return the same value as 310 | /// [`transmute_copy`][std::mem::transmute_copy]. 311 | /// 312 | /// Representations for all values of `T` must have exactly one bit set. 313 | pub unsafe trait RawBitFlags: Copy + Clone + 'static { 314 | /// The underlying integer type. 315 | type Numeric: BitFlagNum; 316 | 317 | /// A value with no bits set. 318 | const EMPTY: Self::Numeric; 319 | 320 | /// The value used by the Default implementation. Equivalent to EMPTY, unless 321 | /// customized. 322 | const DEFAULT: Self::Numeric; 323 | 324 | /// A value with all flag bits set. 325 | const ALL_BITS: Self::Numeric; 326 | 327 | /// The name of the type for debug formatting purposes. 328 | /// 329 | /// This is typically `BitFlags` 330 | const BITFLAGS_TYPE_NAME: &'static str; 331 | 332 | /// Return the bits as a number type. 333 | fn bits(self) -> Self::Numeric; 334 | } 335 | 336 | use ::core::fmt; 337 | use ::core::ops::{BitAnd, BitOr, BitXor, Not, Sub}; 338 | use ::core::hash::Hash; 339 | 340 | pub trait BitFlagNum: 341 | Default 342 | + BitOr 343 | + BitAnd 344 | + BitXor 345 | + Sub 346 | + Not 347 | + PartialOrd 348 | + Ord 349 | + Hash 350 | + fmt::Debug 351 | + fmt::Binary 352 | + Copy 353 | + Clone 354 | { 355 | const ONE: Self; 356 | 357 | fn is_power_of_two(self) -> bool; 358 | fn count_ones(self) -> u32; 359 | fn wrapping_neg(self) -> Self; 360 | } 361 | 362 | for_each_uint! { $ty $hide_docs => 363 | impl BitFlagNum for $ty { 364 | const ONE: Self = 1; 365 | 366 | fn is_power_of_two(self) -> bool { 367 | <$ty>::is_power_of_two(self) 368 | } 369 | 370 | fn count_ones(self) -> u32 { 371 | <$ty>::count_ones(self) 372 | } 373 | 374 | fn wrapping_neg(self) -> Self { 375 | <$ty>::wrapping_neg(self) 376 | } 377 | } 378 | } 379 | 380 | // Re-export libcore so the macro doesn't inject "extern crate" downstream. 381 | pub mod core { 382 | pub use core::{convert, ops, option}; 383 | } 384 | 385 | pub struct AssertionSucceeded; 386 | pub struct AssertionFailed; 387 | pub trait ExactlyOneBitSet { 388 | type X; 389 | } 390 | impl ExactlyOneBitSet for AssertionSucceeded { 391 | type X = (); 392 | } 393 | 394 | pub trait AssertionHelper { 395 | type Status; 396 | } 397 | 398 | impl AssertionHelper for [(); 1] { 399 | type Status = AssertionSucceeded; 400 | } 401 | 402 | impl AssertionHelper for [(); 0] { 403 | type Status = AssertionFailed; 404 | } 405 | 406 | pub const fn next_bit(x: u128) -> u128 { 407 | 1 << x.trailing_ones() 408 | } 409 | } 410 | 411 | use _internal::BitFlagNum; 412 | 413 | // Internal debug formatting implementations 414 | mod formatting; 415 | 416 | // impl TryFrom for BitFlags 417 | mod fallible; 418 | pub use crate::fallible::FromBitsError; 419 | 420 | mod iter; 421 | pub use crate::iter::Iter; 422 | 423 | mod const_api; 424 | pub use crate::const_api::ConstToken; 425 | 426 | /// Represents a set of flags of some type `T`. 427 | /// `T` must have the `#[bitflags]` attribute applied. 428 | /// 429 | /// A `BitFlags` is as large as the `T` itself, 430 | /// and stores one flag per bit. 431 | /// 432 | /// ## Comparison operators, [`PartialOrd`] and [`Ord`] 433 | /// 434 | /// To make it possible to use `BitFlags` as the key of a 435 | /// [`BTreeMap`][std::collections::BTreeMap], `BitFlags` implements 436 | /// [`Ord`]. There is no meaningful total order for bitflags, 437 | /// so the implementation simply compares the integer values of the bits. 438 | /// 439 | /// Unfortunately, this means that comparing `BitFlags` with an operator 440 | /// like `<=` will compile, and return values that are probably useless 441 | /// and not what you expect. In particular, `<=` does *not* check whether 442 | /// one value is a subset of the other. Use [`BitFlags::contains`] for that. 443 | /// 444 | /// ## Customizing `Default` 445 | /// 446 | /// By default, creating an instance of `BitFlags` with `Default` will result 447 | /// in an empty set. If that's undesirable, you may customize this: 448 | /// 449 | /// ``` 450 | /// # use enumflags2::{BitFlags, bitflags}; 451 | /// #[bitflags(default = B | C)] 452 | /// #[repr(u8)] 453 | /// #[derive(Copy, Clone, Debug, PartialEq)] 454 | /// enum MyFlag { 455 | /// A = 0b0001, 456 | /// B = 0b0010, 457 | /// C = 0b0100, 458 | /// D = 0b1000, 459 | /// } 460 | /// 461 | /// assert_eq!(BitFlags::default(), MyFlag::B | MyFlag::C); 462 | /// ``` 463 | /// 464 | /// ## Memory layout 465 | /// 466 | /// `BitFlags` is marked with the `#[repr(transparent)]` trait, meaning 467 | /// it can be safely transmuted into the corresponding numeric type. 468 | /// 469 | /// Usually, the same can be achieved by using [`BitFlags::bits`] in one 470 | /// direction, and [`BitFlags::from_bits`], [`BitFlags::from_bits_truncate`], 471 | /// or [`BitFlags::from_bits_unchecked`] in the other direction. However, 472 | /// transmuting might still be useful if, for example, you're dealing with 473 | /// an entire array of `BitFlags`. 474 | /// 475 | /// When transmuting *into* a `BitFlags`, make sure that each set bit 476 | /// corresponds to an existing flag 477 | /// (cf. [`from_bits_unchecked`][BitFlags::from_bits_unchecked]). 478 | /// 479 | /// For example: 480 | /// 481 | /// ``` 482 | /// # use enumflags2::{BitFlags, bitflags}; 483 | /// #[bitflags] 484 | /// #[repr(u8)] // <-- the repr determines the numeric type 485 | /// #[derive(Copy, Clone)] 486 | /// enum TransmuteMe { 487 | /// One = 1 << 0, 488 | /// Two = 1 << 1, 489 | /// } 490 | /// 491 | /// # use std::slice; 492 | /// // NOTE: we use a small, self-contained function to handle the slice 493 | /// // conversion to make sure the lifetimes are right. 494 | /// fn transmute_slice<'a>(input: &'a [BitFlags]) -> &'a [u8] { 495 | /// unsafe { 496 | /// slice::from_raw_parts(input.as_ptr() as *const u8, input.len()) 497 | /// } 498 | /// } 499 | /// 500 | /// let many_flags = &[ 501 | /// TransmuteMe::One.into(), 502 | /// TransmuteMe::One | TransmuteMe::Two, 503 | /// ]; 504 | /// 505 | /// let as_nums = transmute_slice(many_flags); 506 | /// assert_eq!(as_nums, &[0b01, 0b11]); 507 | /// ``` 508 | /// 509 | /// ## Implementation notes 510 | /// 511 | /// You might expect this struct to be defined as 512 | /// 513 | /// ```ignore 514 | /// struct BitFlags { 515 | /// value: T::Numeric 516 | /// } 517 | /// ``` 518 | /// 519 | /// Ideally, that would be the case. However, because `const fn`s cannot 520 | /// have trait bounds in current Rust, this would prevent us from providing 521 | /// most `const fn` APIs. As a workaround, we define `BitFlags` with two 522 | /// type parameters, with a default for the second one: 523 | /// 524 | /// ```ignore 525 | /// struct BitFlags::Numeric> { 526 | /// value: N, 527 | /// marker: PhantomData, 528 | /// } 529 | /// ``` 530 | /// 531 | /// Manually providing a type for the `N` type parameter shouldn't ever 532 | /// be necessary. 533 | /// 534 | /// The types substituted for `T` and `N` must always match, creating a 535 | /// `BitFlags` value where that isn't the case is only possible with 536 | /// incorrect unsafe code. 537 | #[derive(Copy, Clone)] 538 | #[repr(transparent)] 539 | pub struct BitFlags::Numeric> { 540 | val: N, 541 | marker: PhantomData, 542 | } 543 | 544 | /// `make_bitflags!` provides a succint syntax for creating instances of 545 | /// `BitFlags`. Instead of repeating the name of your type for each flag 546 | /// you want to add, try `make_bitflags!(Flags::{Foo | Bar})`. 547 | /// ``` 548 | /// # use enumflags2::{bitflags, BitFlags, make_bitflags}; 549 | /// # #[bitflags] 550 | /// # #[repr(u8)] 551 | /// # #[derive(Clone, Copy, Debug)] 552 | /// # enum Test { 553 | /// # A = 1 << 0, 554 | /// # B = 1 << 1, 555 | /// # C = 1 << 2, 556 | /// # } 557 | /// let x = make_bitflags!(Test::{A | C}); 558 | /// assert_eq!(x, Test::A | Test::C); 559 | /// 560 | /// // Also works in const contexts: 561 | /// const X: BitFlags = make_bitflags!(Test::A); 562 | /// ``` 563 | #[macro_export] 564 | macro_rules! make_bitflags { 565 | ( $enum:ident ::{ $($variant:ident)|* } ) => { 566 | { 567 | let mut n = 0; 568 | $( 569 | { 570 | let flag: $enum = $enum::$variant; 571 | n |= flag as <$enum as $crate::_internal::RawBitFlags>::Numeric; 572 | } 573 | )* 574 | // SAFETY: The value has been created from numeric values of the underlying 575 | // enum, so only valid bits are set. 576 | unsafe { $crate::BitFlags::<$enum>::from_bits_unchecked_c( 577 | n, $crate::BitFlags::CONST_TOKEN) } 578 | } 579 | }; 580 | ( $enum:ident :: $variant:ident ) => { 581 | { 582 | let flag: $enum = $enum::$variant; 583 | let n = flag as <$enum as $crate::_internal::RawBitFlags>::Numeric; 584 | // SAFETY: The value has been created from the numeric value of 585 | // the underlying enum, so only valid bits are set. 586 | unsafe { $crate::BitFlags::<$enum>::from_bits_unchecked_c( 587 | n, $crate::BitFlags::CONST_TOKEN) } 588 | } 589 | }; 590 | } 591 | 592 | /// The default value returned is one with all flags unset, i. e. [`empty`][Self::empty], 593 | /// unless [customized](index.html#customizing-default). 594 | impl Default for BitFlags 595 | where 596 | T: BitFlag, 597 | { 598 | #[inline(always)] 599 | fn default() -> Self { 600 | BitFlags { 601 | val: T::DEFAULT, 602 | marker: PhantomData, 603 | } 604 | } 605 | } 606 | 607 | impl From for BitFlags { 608 | #[inline(always)] 609 | fn from(t: T) -> BitFlags { 610 | Self::from_flag(t) 611 | } 612 | } 613 | 614 | impl BitFlags 615 | where 616 | T: BitFlag, 617 | { 618 | /// Create a `BitFlags` if the raw value provided does not contain 619 | /// any illegal flags. 620 | /// 621 | /// See also: [a convenience re-export in the `BitFlag` trait][BitFlag::from_bits], 622 | /// which can help avoid the need for type hints. 623 | /// 624 | /// ``` 625 | /// # use enumflags2::{bitflags, BitFlags}; 626 | /// #[bitflags] 627 | /// #[repr(u8)] 628 | /// #[derive(Clone, Copy, PartialEq, Eq, Debug)] 629 | /// enum MyFlag { 630 | /// One = 1 << 0, 631 | /// Two = 1 << 1, 632 | /// Three = 1 << 2, 633 | /// } 634 | /// 635 | /// let flags: BitFlags = BitFlags::from_bits(0b11).unwrap(); 636 | /// assert_eq!(flags.contains(MyFlag::One), true); 637 | /// assert_eq!(flags.contains(MyFlag::Two), true); 638 | /// assert_eq!(flags.contains(MyFlag::Three), false); 639 | /// let invalid = BitFlags::::from_bits(1 << 3); 640 | /// assert!(invalid.is_err()); 641 | /// ``` 642 | #[inline] 643 | pub fn from_bits(bits: T::Numeric) -> Result> { 644 | let flags = Self::from_bits_truncate(bits); 645 | if flags.bits() == bits { 646 | Ok(flags) 647 | } else { 648 | Err(FromBitsError { 649 | flags, 650 | invalid: bits & !flags.bits(), 651 | }) 652 | } 653 | } 654 | 655 | /// Create a `BitFlags` from an underlying bitwise value. If any 656 | /// invalid bits are set, ignore them. 657 | /// 658 | /// See also: [a convenience re-export in the `BitFlag` trait][BitFlag::from_bits_truncate], 659 | /// which can help avoid the need for type hints. 660 | /// 661 | /// ``` 662 | /// # use enumflags2::{bitflags, BitFlags}; 663 | /// #[bitflags] 664 | /// #[repr(u8)] 665 | /// #[derive(Clone, Copy, PartialEq, Eq)] 666 | /// enum MyFlag { 667 | /// One = 1 << 0, 668 | /// Two = 1 << 1, 669 | /// Three = 1 << 2, 670 | /// } 671 | /// 672 | /// let flags: BitFlags = BitFlags::from_bits_truncate(0b1_1011); 673 | /// assert_eq!(flags.contains(MyFlag::One), true); 674 | /// assert_eq!(flags.contains(MyFlag::Two), true); 675 | /// assert_eq!(flags.contains(MyFlag::Three), false); 676 | /// ``` 677 | #[must_use] 678 | #[inline(always)] 679 | pub fn from_bits_truncate(bits: T::Numeric) -> Self { 680 | // SAFETY: We're truncating out all the invalid bits, so the remaining 681 | // ones must be valid. 682 | unsafe { BitFlags::from_bits_unchecked(bits & T::ALL_BITS) } 683 | } 684 | 685 | /// Create a new BitFlags unsafely, without checking if the bits form 686 | /// a valid bit pattern for the type. 687 | /// 688 | /// Consider using [`from_bits`][BitFlags::from_bits] 689 | /// or [`from_bits_truncate`][BitFlags::from_bits_truncate] instead. 690 | /// 691 | /// # Safety 692 | /// 693 | /// All bits set in `val` must correspond to a value of the enum. 694 | /// 695 | /// # Example 696 | /// 697 | /// ``` 698 | /// # use enumflags2::{bitflags, BitFlags}; 699 | /// #[bitflags] 700 | /// #[repr(u8)] 701 | /// #[derive(Clone, Copy, PartialEq, Eq)] 702 | /// enum MyFlag { 703 | /// One = 1 << 0, 704 | /// Two = 1 << 1, 705 | /// Three = 1 << 2, 706 | /// } 707 | /// 708 | /// let flags: BitFlags = unsafe { 709 | /// BitFlags::from_bits_unchecked(0b011) 710 | /// }; 711 | /// 712 | /// assert_eq!(flags.contains(MyFlag::One), true); 713 | /// assert_eq!(flags.contains(MyFlag::Two), true); 714 | /// assert_eq!(flags.contains(MyFlag::Three), false); 715 | /// ``` 716 | #[must_use] 717 | #[inline(always)] 718 | pub unsafe fn from_bits_unchecked(val: T::Numeric) -> Self { 719 | BitFlags { 720 | val, 721 | marker: PhantomData, 722 | } 723 | } 724 | 725 | /// Turn a `T` into a `BitFlags`. Also available as `flag.into()`. 726 | #[must_use] 727 | #[inline(always)] 728 | pub fn from_flag(flag: T) -> Self { 729 | // SAFETY: A value of the underlying enum is valid by definition. 730 | unsafe { Self::from_bits_unchecked(flag.bits()) } 731 | } 732 | 733 | /// Create a `BitFlags` with no flags set (in other words, with a value of `0`). 734 | /// 735 | /// See also: [`BitFlag::empty`], a convenience reexport; 736 | /// [`BitFlags::EMPTY`], the same functionality available 737 | /// as a constant for `const fn` code. 738 | /// 739 | /// ``` 740 | /// # use enumflags2::{bitflags, BitFlags}; 741 | /// #[bitflags] 742 | /// #[repr(u8)] 743 | /// #[derive(Clone, Copy, PartialEq, Eq)] 744 | /// enum MyFlag { 745 | /// One = 1 << 0, 746 | /// Two = 1 << 1, 747 | /// Three = 1 << 2, 748 | /// } 749 | /// 750 | /// let empty: BitFlags = BitFlags::empty(); 751 | /// assert!(empty.is_empty()); 752 | /// assert_eq!(empty.contains(MyFlag::One), false); 753 | /// assert_eq!(empty.contains(MyFlag::Two), false); 754 | /// assert_eq!(empty.contains(MyFlag::Three), false); 755 | /// ``` 756 | #[inline(always)] 757 | pub fn empty() -> Self { 758 | Self::EMPTY 759 | } 760 | 761 | /// Create a `BitFlags` with all flags set. 762 | /// 763 | /// See also: [`BitFlag::all`], a convenience reexport; 764 | /// [`BitFlags::ALL`], the same functionality available 765 | /// as a constant for `const fn` code. 766 | /// 767 | /// ``` 768 | /// # use enumflags2::{bitflags, BitFlags}; 769 | /// #[bitflags] 770 | /// #[repr(u8)] 771 | /// #[derive(Clone, Copy, PartialEq, Eq)] 772 | /// enum MyFlag { 773 | /// One = 1 << 0, 774 | /// Two = 1 << 1, 775 | /// Three = 1 << 2, 776 | /// } 777 | /// 778 | /// let empty: BitFlags = BitFlags::all(); 779 | /// assert!(empty.is_all()); 780 | /// assert_eq!(empty.contains(MyFlag::One), true); 781 | /// assert_eq!(empty.contains(MyFlag::Two), true); 782 | /// assert_eq!(empty.contains(MyFlag::Three), true); 783 | /// ``` 784 | #[inline(always)] 785 | pub fn all() -> Self { 786 | Self::ALL 787 | } 788 | 789 | /// Returns true if all flags are set 790 | #[inline(always)] 791 | pub fn is_all(self) -> bool { 792 | self.val == T::ALL_BITS 793 | } 794 | 795 | /// Returns true if no flag is set 796 | #[inline(always)] 797 | pub fn is_empty(self) -> bool { 798 | self.val == T::EMPTY 799 | } 800 | 801 | /// Returns the number of flags set. 802 | #[inline(always)] 803 | pub fn len(self) -> usize { 804 | self.val.count_ones() as usize 805 | } 806 | 807 | /// If exactly one flag is set, the flag is returned. Otherwise, returns `None`. 808 | /// 809 | /// See also [`Itertools::exactly_one`](https://docs.rs/itertools/latest/itertools/trait.Itertools.html#method.exactly_one). 810 | #[inline(always)] 811 | pub fn exactly_one(self) -> Option { 812 | if self.val.is_power_of_two() { 813 | // SAFETY: By the invariant of the BitFlags type, all bits are valid 814 | // in isolation for the underlying enum. 815 | Some(unsafe { core::mem::transmute_copy(&self.val) }) 816 | } else { 817 | None 818 | } 819 | } 820 | 821 | /// Returns the underlying bitwise value. 822 | /// 823 | /// ``` 824 | /// # use enumflags2::{bitflags, BitFlags}; 825 | /// #[bitflags] 826 | /// #[repr(u8)] 827 | /// #[derive(Clone, Copy)] 828 | /// enum Flags { 829 | /// Foo = 1 << 0, 830 | /// Bar = 1 << 1, 831 | /// } 832 | /// 833 | /// let both_flags = Flags::Foo | Flags::Bar; 834 | /// assert_eq!(both_flags.bits(), 0b11); 835 | /// ``` 836 | #[inline(always)] 837 | pub fn bits(self) -> T::Numeric { 838 | self.val 839 | } 840 | 841 | /// Returns true if at least one flag is shared. 842 | #[inline(always)] 843 | pub fn intersects>>(self, other: B) -> bool { 844 | (self.bits() & other.into().bits()) != Self::EMPTY.val 845 | } 846 | 847 | /// Returns true if all flags are contained. 848 | #[inline(always)] 849 | pub fn contains>>(self, other: B) -> bool { 850 | let other = other.into(); 851 | (self.bits() & other.bits()) == other.bits() 852 | } 853 | 854 | /// Toggles the matching bits 855 | #[inline(always)] 856 | pub fn toggle>>(&mut self, other: B) { 857 | *self ^= other.into(); 858 | } 859 | 860 | /// Inserts the flags into the BitFlag 861 | #[inline(always)] 862 | pub fn insert>>(&mut self, other: B) { 863 | *self |= other.into(); 864 | } 865 | 866 | /// Removes the matching flags 867 | #[inline(always)] 868 | pub fn remove>>(&mut self, other: B) { 869 | *self &= !other.into(); 870 | } 871 | 872 | /// Inserts if `cond` holds, else removes 873 | /// 874 | /// ``` 875 | /// # use enumflags2::bitflags; 876 | /// #[bitflags] 877 | /// #[derive(Clone, Copy, PartialEq, Debug)] 878 | /// #[repr(u8)] 879 | /// enum MyFlag { 880 | /// A = 1 << 0, 881 | /// B = 1 << 1, 882 | /// C = 1 << 2, 883 | /// } 884 | /// 885 | /// let mut state = MyFlag::A | MyFlag::C; 886 | /// state.set(MyFlag::A | MyFlag::B, false); 887 | /// 888 | /// // Because the condition was false, both 889 | /// // `A` and `B` are removed from the set 890 | /// assert_eq!(state, MyFlag::C); 891 | /// ``` 892 | #[inline(always)] 893 | pub fn set>>(&mut self, other: B, cond: bool) { 894 | if cond { 895 | self.insert(other); 896 | } else { 897 | self.remove(other); 898 | } 899 | } 900 | } 901 | 902 | impl PartialEq for BitFlags { 903 | #[inline(always)] 904 | fn eq(&self, other: &Self) -> bool { 905 | self.val == other.val 906 | } 907 | } 908 | 909 | impl Eq for BitFlags {} 910 | 911 | impl PartialOrd for BitFlags { 912 | #[inline(always)] 913 | fn partial_cmp(&self, other: &Self) -> Option { 914 | self.val.partial_cmp(&other.val) 915 | } 916 | } 917 | 918 | impl Ord for BitFlags { 919 | #[inline(always)] 920 | fn cmp(&self, other: &Self) -> cmp::Ordering { 921 | self.val.cmp(&other.val) 922 | } 923 | } 924 | 925 | // Clippy complains when Hash is derived while PartialEq is implemented manually 926 | impl Hash for BitFlags { 927 | #[inline(always)] 928 | fn hash(&self, state: &mut H) { 929 | self.val.hash(state) 930 | } 931 | } 932 | 933 | impl cmp::PartialEq for BitFlags 934 | where 935 | T: BitFlag, 936 | { 937 | #[inline(always)] 938 | fn eq(&self, other: &T) -> bool { 939 | self.bits() == Into::::into(*other).bits() 940 | } 941 | } 942 | 943 | impl ops::BitOr for BitFlags 944 | where 945 | T: BitFlag, 946 | B: Into>, 947 | { 948 | type Output = BitFlags; 949 | #[inline(always)] 950 | fn bitor(self, other: B) -> BitFlags { 951 | // SAFETY: The two operands are known to be composed of valid bits, 952 | // and 0 | 0 = 0 in the columns of the invalid bits. 953 | unsafe { BitFlags::from_bits_unchecked(self.bits() | other.into().bits()) } 954 | } 955 | } 956 | 957 | impl ops::BitAnd for BitFlags 958 | where 959 | T: BitFlag, 960 | B: Into>, 961 | { 962 | type Output = BitFlags; 963 | #[inline(always)] 964 | fn bitand(self, other: B) -> BitFlags { 965 | // SAFETY: The two operands are known to be composed of valid bits, 966 | // and 0 & 0 = 0 in the columns of the invalid bits. 967 | unsafe { BitFlags::from_bits_unchecked(self.bits() & other.into().bits()) } 968 | } 969 | } 970 | 971 | impl ops::BitXor for BitFlags 972 | where 973 | T: BitFlag, 974 | B: Into>, 975 | { 976 | type Output = BitFlags; 977 | #[inline(always)] 978 | fn bitxor(self, other: B) -> BitFlags { 979 | // SAFETY: The two operands are known to be composed of valid bits, 980 | // and 0 ^ 0 = 0 in the columns of the invalid bits. 981 | unsafe { BitFlags::from_bits_unchecked(self.bits() ^ other.into().bits()) } 982 | } 983 | } 984 | 985 | impl ops::BitOrAssign for BitFlags 986 | where 987 | T: BitFlag, 988 | B: Into>, 989 | { 990 | #[inline(always)] 991 | fn bitor_assign(&mut self, other: B) { 992 | *self = *self | other; 993 | } 994 | } 995 | 996 | impl ops::BitAndAssign for BitFlags 997 | where 998 | T: BitFlag, 999 | B: Into>, 1000 | { 1001 | #[inline(always)] 1002 | fn bitand_assign(&mut self, other: B) { 1003 | *self = *self & other; 1004 | } 1005 | } 1006 | impl ops::BitXorAssign for BitFlags 1007 | where 1008 | T: BitFlag, 1009 | B: Into>, 1010 | { 1011 | #[inline(always)] 1012 | fn bitxor_assign(&mut self, other: B) { 1013 | *self = *self ^ other; 1014 | } 1015 | } 1016 | 1017 | impl ops::Not for BitFlags 1018 | where 1019 | T: BitFlag, 1020 | { 1021 | type Output = BitFlags; 1022 | #[inline(always)] 1023 | fn not(self) -> BitFlags { 1024 | BitFlags::from_bits_truncate(!self.bits()) 1025 | } 1026 | } 1027 | 1028 | #[cfg(feature = "serde")] 1029 | mod impl_serde { 1030 | use super::{BitFlag, BitFlags}; 1031 | use serde::de::{Error, Unexpected}; 1032 | use serde::{Deserialize, Serialize}; 1033 | 1034 | impl<'a, T> Deserialize<'a> for BitFlags 1035 | where 1036 | T: BitFlag, 1037 | T::Numeric: Deserialize<'a> + Into, 1038 | { 1039 | fn deserialize>(d: D) -> Result { 1040 | let val = T::Numeric::deserialize(d)?; 1041 | Self::from_bits(val).map_err(|_| { 1042 | D::Error::invalid_value( 1043 | Unexpected::Unsigned(val.into()), 1044 | &"valid bit representation", 1045 | ) 1046 | }) 1047 | } 1048 | } 1049 | 1050 | impl Serialize for BitFlags 1051 | where 1052 | T: BitFlag, 1053 | T::Numeric: Serialize, 1054 | { 1055 | fn serialize(&self, s: S) -> Result { 1056 | T::Numeric::serialize(&self.val, s) 1057 | } 1058 | } 1059 | } 1060 | -------------------------------------------------------------------------------- /test_suite/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bitflags-test-suite" 3 | version = "0.0.0" 4 | publish = false 5 | edition = "2018" 6 | 7 | [dependencies.enumflags2] 8 | path = "../" 9 | features = ["serde"] 10 | 11 | [dependencies.serde] 12 | version = "1" 13 | features = ["derive"] 14 | 15 | [dev-dependencies] 16 | trybuild = "1.0" 17 | glob = "0.3" 18 | 19 | [[test]] 20 | name = "ui-tests" 21 | path = "ui_tests.rs" 22 | edition = "2018" 23 | 24 | [[test]] 25 | name = "bitflags-test" 26 | path = "tests/bitflag_tests.rs" 27 | edition = "2015" 28 | 29 | [[test]] 30 | name = "bitflags-test-no-std" 31 | path = "tests/no_std.rs" 32 | edition = "2015" 33 | 34 | [[test]] 35 | name = "bitflags-test-no-implicit-prelude" 36 | path = "tests/no_implicit_prelude.rs" 37 | edition = "2015" 38 | 39 | [[test]] 40 | name = "bitflags-test-2018" 41 | path = "tests/bitflag_tests_2018.rs" 42 | edition = "2018" 43 | 44 | [[test]] 45 | name = "bitflags-test-no-std-2018" 46 | path = "tests/no_std_2018.rs" 47 | edition = "2018" 48 | 49 | [[test]] 50 | name = "bitflags-test-no-implicit-prelude-2018" 51 | path = "tests/no_implicit_prelude_2018.rs" 52 | edition = "2018" 53 | 54 | [[test]] 55 | name = "bitflags-requires-std" 56 | path = "tests/requires_std.rs" 57 | edition = "2018" 58 | 59 | [[test]] 60 | name = "serde" 61 | path = "tests/serde.rs" 62 | edition = "2018" 63 | 64 | [[test]] 65 | name = "not_literal" 66 | path = "tests/not_literal.rs" 67 | edition = "2018" 68 | -------------------------------------------------------------------------------- /test_suite/common.rs: -------------------------------------------------------------------------------- 1 | #[bitflags] 2 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 3 | #[repr(u8)] 4 | enum Test { 5 | A = 1 << 0, 6 | B = 1 << 1, 7 | C = 1 << 2, 8 | D = 1 << 3, 9 | } 10 | 11 | #[bitflags] 12 | #[derive(Copy, Clone, Debug)] 13 | #[repr(u64)] 14 | enum Test1 { 15 | A = 1 << 0, 16 | B = 1 << 1, 17 | C = 1 << 2, 18 | D = 1 << 3, 19 | E = 1 << 34, 20 | } 21 | 22 | #[bitflags(default = B | C)] 23 | #[derive(Copy, Clone, Debug)] 24 | #[repr(u8)] 25 | enum Default6 { 26 | A = 1 << 0, 27 | B = 1 << 1, 28 | C = 1 << 2, 29 | D = 1 << 3, 30 | } 31 | 32 | #[test] 33 | fn test_ctors() { 34 | use enumflags2::BitFlags; 35 | assert_eq!( 36 | BitFlags::::all(), 37 | Test::A | Test::B | Test::C | Test::D 38 | ); 39 | assert_eq!(BitFlags::::all() & Test::A, Test::A); 40 | assert_eq!(BitFlags::::from_bits_truncate(4), Test::C); 41 | assert_eq!(BitFlags::::from_bits_truncate(5), Test::A | Test::C); 42 | assert_eq!( 43 | BitFlags::::from_bits_truncate(16), 44 | BitFlags::::empty() 45 | ); 46 | assert_eq!(BitFlags::::from_bits_truncate(17), Test::A); 47 | assert!(BitFlags::::from_bits(17).is_err()); 48 | assert_eq!( 49 | BitFlags::::from_bits(15).unwrap(), 50 | BitFlags::::all() 51 | ); 52 | assert_eq!((Test::A | Test::B).bits(), 3); 53 | assert_eq!((!(Test::A | Test::B)).bits(), 12); 54 | assert_eq!(BitFlags::::all().bits(), 15); 55 | assert_eq!(BitFlags::::default(), Default6::B | Default6::C); 56 | } 57 | 58 | #[test] 59 | fn test_ops() { 60 | assert_eq!(!Test::A, Test::B | Test::C | Test::D); 61 | assert_eq!((Test::A | Test::C) ^ (Test::C | Test::B), Test::A | Test::B); 62 | assert!((Test::A | Test::B).intersects(Test::B)); 63 | assert!(!(Test::A | Test::B).intersects(Test::C)); 64 | assert!((Test::A | Test::B | Test::C).contains(Test::A | Test::B)); 65 | assert!(!(Test::A | Test::B | Test::C).contains(Test::A | Test::D)); 66 | assert_eq!(!(Test::A | Test::B), Test::C | Test::D); 67 | assert_eq!((Test::A ^ Test::B), Test::A | Test::B); 68 | } 69 | 70 | #[test] 71 | fn test_mutation() { 72 | { 73 | let mut b = Test::A | Test::B; 74 | b.insert(Test::C); 75 | assert_eq!(b, Test::A | Test::B | Test::C); 76 | } 77 | { 78 | let mut b = Test::A | Test::B | Test::C; 79 | b.remove(Test::B); 80 | assert_eq!(b, Test::A | Test::C); 81 | } 82 | } 83 | 84 | #[test] 85 | fn test_exactly_one() { 86 | use enumflags2::BitFlags; 87 | assert_eq!(BitFlags::::empty().exactly_one(), None); 88 | assert_eq!(BitFlags::::from(Test::B).exactly_one(), Some(Test::B)); 89 | assert_eq!((Test::A | Test::C).exactly_one(), None); 90 | } 91 | 92 | #[test] 93 | fn test_len() { 94 | use enumflags2::BitFlags; 95 | assert_eq!(BitFlags::::empty().len(), 0); 96 | assert_eq!(BitFlags::::all().len(), 4); 97 | assert_eq!((Test::A | Test::D).len(), 2); 98 | } 99 | 100 | #[test] 101 | fn iterator() { 102 | use enumflags2::BitFlags; 103 | 104 | // it's a separate statement because type ascription is nightly 105 | let tests: &[(BitFlags, &[Test])] = &[ 106 | (BitFlags::empty(), &[]), 107 | (Test::A.into(), &[Test::A]), 108 | (Test::A | Test::B, &[Test::A, Test::B]), 109 | ]; 110 | 111 | for &(bitflag, expected) in tests { 112 | assert!(bitflag.iter().zip(expected.iter().copied()).all(|(a, b)| a == b)); 113 | // If cloned, the iterator will yield the same elements. 114 | let it = bitflag.iter(); 115 | assert!(it.clone().zip(it).all(|(a, b)| a == b)); 116 | // The ExactLenIterator implementation should always yield the 117 | // correct remaining length. 118 | let mut it = bitflag.iter(); 119 | for rest in (0..=expected.len()).rev() { 120 | assert_eq!(it.len(), rest); 121 | assert_eq!(it.size_hint(), (rest, Some(rest))); 122 | it.next(); 123 | } 124 | } 125 | } 126 | 127 | #[test] 128 | fn assign_ops() { 129 | let mut x = Test::A | Test::B; 130 | x |= Test::C; 131 | assert_eq!(x, Test::A | Test::B | Test::C); 132 | 133 | let mut x = Test::A | Test::B; 134 | x &= Test::B | Test::C; 135 | assert_eq!(x, Test::B); 136 | 137 | let mut x = Test::A | Test::B; 138 | x ^= Test::B | Test::C; 139 | assert_eq!(x, Test::A | Test::C); 140 | } 141 | 142 | #[test] 143 | const fn fn_derive() { 144 | #[bitflags] 145 | #[derive(Copy, Clone, Debug)] 146 | #[repr(u8)] 147 | enum TestFn { 148 | A = 1 << 0, 149 | } 150 | } 151 | 152 | #[test] 153 | const fn module() { 154 | mod some_modules { 155 | #[enumflags2::bitflags] 156 | #[derive(Copy, Clone, Debug)] 157 | #[repr(u8)] 158 | enum Test2 { 159 | A = 1 << 0, 160 | B = 1 << 1, 161 | C = 1 << 2, 162 | D = 1 << 3, 163 | } 164 | } 165 | } 166 | 167 | #[test] 168 | fn inferred_values() { 169 | #[bitflags] 170 | #[derive(Copy, Clone, Debug)] 171 | #[repr(u8)] 172 | enum Inferred { 173 | Infer2, 174 | SpecifiedA = 1, 175 | Infer8, 176 | SpecifiedB = 4, 177 | } 178 | 179 | #[bitflags] 180 | #[derive(Copy, Clone, Debug)] 181 | #[repr(u8)] 182 | enum OnlyInferred { 183 | Infer1, 184 | Infer2, 185 | Infer4, 186 | Infer8, 187 | } 188 | 189 | assert_eq!(Inferred::Infer2 as u8, 2); 190 | assert_eq!(Inferred::Infer8 as u8, 8); 191 | 192 | assert_eq!(OnlyInferred::Infer1 as u8, 1); 193 | assert_eq!(OnlyInferred::Infer2 as u8, 2); 194 | assert_eq!(OnlyInferred::Infer4 as u8, 4); 195 | assert_eq!(OnlyInferred::Infer8 as u8, 8); 196 | } 197 | -------------------------------------------------------------------------------- /test_suite/tests/bitflag_tests.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate enumflags2; 3 | 4 | include!("../common.rs"); 5 | -------------------------------------------------------------------------------- /test_suite/tests/bitflag_tests_2018.rs: -------------------------------------------------------------------------------- 1 | // "an inner attribute is not permitted in this context" :/ 2 | #[deny(clippy::all, clippy::pedantic, clippy::nursery)] 3 | mod everything { 4 | 5 | use enumflags2::bitflags; 6 | 7 | include!("../common.rs"); 8 | } 9 | -------------------------------------------------------------------------------- /test_suite/tests/no_implicit_prelude.rs: -------------------------------------------------------------------------------- 1 | #![no_implicit_prelude] 2 | 3 | #[macro_use] 4 | extern crate enumflags2; 5 | use enumflags2::BitFlags; 6 | 7 | #[bitflags] 8 | #[derive(Copy, Clone, Debug, PartialEq)] 9 | #[repr(u8)] 10 | pub enum Test { 11 | A = 1 << 0, 12 | B = 1 << 1, 13 | C = 1 << 2, 14 | D = 1 << 3, 15 | } 16 | 17 | #[test] 18 | fn test_foo() { 19 | // assert!() doesn't even work in no_implicit_prelude! 20 | let _ = BitFlags::::all(); 21 | } 22 | -------------------------------------------------------------------------------- /test_suite/tests/no_implicit_prelude_2018.rs: -------------------------------------------------------------------------------- 1 | #![no_implicit_prelude] 2 | 3 | #[::enumflags2::bitflags] 4 | #[derive(Copy, Clone, Debug, PartialEq)] 5 | #[repr(u8)] 6 | pub enum Test { 7 | A = 1 << 0, 8 | B = 1 << 1, 9 | C = 1 << 2, 10 | D = 1 << 3, 11 | } 12 | 13 | #[test] 14 | fn test_foo() { 15 | // assert!() doesn't even work in no_implicit_prelude! 16 | use ::enumflags2::BitFlags; 17 | let _ = BitFlags::::all(); 18 | } 19 | -------------------------------------------------------------------------------- /test_suite/tests/no_std.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | include!("bitflag_tests.rs"); 3 | -------------------------------------------------------------------------------- /test_suite/tests/no_std_2018.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | include!("bitflag_tests_2018.rs"); 3 | -------------------------------------------------------------------------------- /test_suite/tests/not_literal.rs: -------------------------------------------------------------------------------- 1 | #![forbid(trivial_numeric_casts)] 2 | 3 | const FOO_BAR: u8 = 1; 4 | const FOO_BAZ: u8 = 2; 5 | 6 | #[enumflags2::bitflags] 7 | #[derive(Clone, Copy)] 8 | #[repr(u8)] 9 | enum Foo { 10 | Bar = FOO_BAR, 11 | Baz = FOO_BAZ, 12 | } 13 | 14 | #[enumflags2::bitflags] 15 | #[derive(Clone, Copy)] 16 | #[repr(u8)] 17 | enum SingleTest { 18 | Hello = FOO_BAR, 19 | } 20 | -------------------------------------------------------------------------------- /test_suite/tests/requires_std.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | use enumflags2::{bitflags, BitFlag, BitFlags}; 3 | 4 | include!("../common.rs"); 5 | 6 | #[test] 7 | fn debug_format() { 8 | // Assert that our Debug output format meets expectations 9 | 10 | assert_eq!( 11 | format!("{:?}", BitFlags::::all()), 12 | "BitFlags(0b1111, A | B | C | D)" 13 | ); 14 | 15 | assert_eq!( 16 | format!("{:?}", BitFlags::::empty()), 17 | "BitFlags(0b0)" 18 | ); 19 | 20 | assert_eq!( 21 | format!("{:?}", BitFlags::from_flag(Test::B)), 22 | "BitFlags(0b10, B)" 23 | ); 24 | 25 | assert_eq!( 26 | format!("{:04x?}", BitFlags::::all()), 27 | "BitFlags(0x0f, A | B | C | D)" 28 | ); 29 | 30 | assert_eq!( 31 | format!("{:04X?}", BitFlags::::all()), 32 | "BitFlags(0x0F, A | B | C | D)" 33 | ); 34 | } 35 | 36 | #[test] 37 | fn debug_format_alternate() { 38 | /// Handle the slight difference in alternate debug output on rustc 1.34.2. 39 | fn compare(mut actual: String, expected: &str) { 40 | if actual.ends_with("\n}") && !actual.ends_with(",\n}") { 41 | actual.replace_range(actual.len() - 2.., ",\n}"); 42 | } 43 | 44 | assert_eq!(actual, expected); 45 | } 46 | 47 | compare( 48 | format!("{:#010?}", BitFlags::::all()), 49 | "BitFlags { 50 | bits: 0b00001111, 51 | flags: A | B | C | D, 52 | }", 53 | ); 54 | 55 | compare( 56 | format!("{:#?}", BitFlags::::empty()), 57 | "BitFlags { 58 | bits: 0b0, 59 | }", 60 | ); 61 | } 62 | 63 | #[test] 64 | fn display_format() { 65 | // Assert that our Debug output format meets expectations 66 | 67 | assert_eq!( 68 | format!("{}", BitFlags::::all()), 69 | "A | B | C | D" 70 | ); 71 | 72 | assert_eq!( 73 | format!("{}", BitFlags::::empty()), 74 | "" 75 | ); 76 | 77 | assert_eq!( 78 | format!("{}", BitFlags::from_flag(Test::B)), 79 | "B" 80 | ); 81 | } 82 | 83 | #[test] 84 | fn format() { 85 | // Assert BitFlags impls fmt::{Binary, Octal, LowerHex, UpperHex} 86 | 87 | assert_eq!(format!("{:b}", BitFlags::::all()), "1111"); 88 | 89 | assert_eq!(format!("{:o}", BitFlags::::all()), "17"); 90 | 91 | assert_eq!(format!("{:x}", BitFlags::::all()), "f"); 92 | 93 | assert_eq!(format!("{:#04X}", BitFlags::::all()), "0x0F"); 94 | } 95 | 96 | #[test] 97 | fn debug_generic() { 98 | #[derive(Debug)] 99 | struct Debug(BitFlags); 100 | 101 | let _ = format!("{:?}", Debug(BitFlags::::all())); 102 | } 103 | 104 | fn works_in_maps() { 105 | // Assert that BitFlags implements Hash and Ord. 106 | 107 | use std::collections::{BTreeSet, HashSet}; 108 | let mut map: BTreeSet> = BTreeSet::new(); 109 | map.insert(BitFlags::empty()); 110 | let mut map: HashSet> = HashSet::new(); 111 | map.insert(BitFlags::empty()); 112 | } 113 | 114 | fn works_in_maps_generic() { 115 | // Assert that BitFlags implements Hash and Ord. 116 | 117 | use std::collections::{BTreeSet, HashSet}; 118 | let mut map: BTreeSet> = BTreeSet::new(); 119 | map.insert(BitFlags::empty()); 120 | let mut map: HashSet> = HashSet::new(); 121 | map.insert(BitFlags::empty()); 122 | } 123 | -------------------------------------------------------------------------------- /test_suite/tests/serde.rs: -------------------------------------------------------------------------------- 1 | use enumflags2::{bitflags, BitFlags}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[test] 5 | fn serde_compile() { 6 | #[bitflags] 7 | #[derive(Copy, Clone, Debug, Serialize, Deserialize)] 8 | #[repr(u8)] 9 | enum Test { 10 | A = 1 << 0, 11 | B = 1 << 1, 12 | C = 1 << 2, 13 | D = 1 << 3, 14 | } 15 | 16 | type TestBitFlags = BitFlags; 17 | 18 | #[derive(Clone, Debug, Serialize, Deserialize)] 19 | struct TestStructContainsFlags { 20 | flags: TestBitFlags, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test_suite/ui/invalid_attribute_syntax.rs: -------------------------------------------------------------------------------- 1 | use enumflags2::bitflags; 2 | 3 | #[bitflags(default = A + B)] 4 | enum Test { 5 | A = 1, 6 | B = 2, 7 | } 8 | 9 | #[bitflags(default = A |)] 10 | enum Test { 11 | A = 1, 12 | B = 2, 13 | } 14 | 15 | #[bitflags(default =)] 16 | enum Test { 17 | A = 1, 18 | B = 2, 19 | } 20 | 21 | #[bitflags(default)] 22 | enum Test { 23 | A = 1, 24 | B = 2, 25 | } 26 | 27 | #[bitflags(yes)] 28 | enum Test { 29 | A = 1, 30 | B = 2, 31 | } 32 | 33 | fn main() {} 34 | -------------------------------------------------------------------------------- /test_suite/ui/invalid_attribute_syntax.stderr: -------------------------------------------------------------------------------- 1 | error: expected `|` 2 | --> $DIR/invalid_attribute_syntax.rs:3:24 3 | | 4 | 3 | #[bitflags(default = A + B)] 5 | | ^ 6 | 7 | error: unexpected end of input, expected identifier 8 | --> $DIR/invalid_attribute_syntax.rs:9:1 9 | | 10 | 9 | #[bitflags(default = A |)] 11 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 12 | | 13 | = note: this error originates in the attribute macro `bitflags` (in Nightly builds, run with -Z macro-backtrace for more info) 14 | 15 | error: unexpected end of input, expected identifier 16 | --> $DIR/invalid_attribute_syntax.rs:15:1 17 | | 18 | 15 | #[bitflags(default =)] 19 | | ^^^^^^^^^^^^^^^^^^^^^^ 20 | | 21 | = note: this error originates in the attribute macro `bitflags` (in Nightly builds, run with -Z macro-backtrace for more info) 22 | 23 | error: expected `=` 24 | --> $DIR/invalid_attribute_syntax.rs:21:1 25 | | 26 | 21 | #[bitflags(default)] 27 | | ^^^^^^^^^^^^^^^^^^^^ 28 | | 29 | = note: this error originates in the attribute macro `bitflags` (in Nightly builds, run with -Z macro-backtrace for more info) 30 | 31 | error: expected `default` 32 | --> $DIR/invalid_attribute_syntax.rs:27:12 33 | | 34 | 27 | #[bitflags(yes)] 35 | | ^^^ 36 | -------------------------------------------------------------------------------- /test_suite/ui/invalid_name_in_default.rs: -------------------------------------------------------------------------------- 1 | use enumflags2::bitflags; 2 | 3 | #[bitflags(default = A | C)] 4 | #[repr(u8)] 5 | #[derive(Clone, Copy)] 6 | enum Test { 7 | A = 1, 8 | B = 2, 9 | } 10 | 11 | fn main() {} 12 | -------------------------------------------------------------------------------- /test_suite/ui/invalid_name_in_default.stderr: -------------------------------------------------------------------------------- 1 | error[E0599]: no variant or associated item named `C` found for enum `Test` in the current scope 2 | --> ui/invalid_name_in_default.rs:3:26 3 | | 4 | 3 | #[bitflags(default = A | C)] 5 | | ^ variant or associated item not found in `Test` 6 | ... 7 | 6 | enum Test { 8 | | --------- variant or associated item `C` not found for this enum 9 | | 10 | help: there is a variant with a similar name 11 | | 12 | 3 - #[bitflags(default = A | C)] 13 | 3 + #[bitflags(default = A | A)] 14 | | 15 | -------------------------------------------------------------------------------- /test_suite/ui/invalid_repr.rs: -------------------------------------------------------------------------------- 1 | #[enumflags2::bitflags] 2 | #[repr(C)] 3 | #[derive(Clone, Copy)] 4 | enum NotAType { 5 | Bar = 1, 6 | Baz = 2, 7 | } 8 | 9 | #[enumflags2::bitflags] 10 | #[repr(i32)] 11 | #[derive(Clone, Copy)] 12 | enum SignedType { 13 | Bar = 1, 14 | Baz = 2, 15 | } 16 | 17 | #[enumflags2::bitflags] 18 | #[repr(usize)] 19 | #[derive(Clone, Copy)] 20 | enum Usize { 21 | Bar = 1, 22 | Baz = 2, 23 | } 24 | 25 | fn main() {} 26 | -------------------------------------------------------------------------------- /test_suite/ui/invalid_repr.stderr: -------------------------------------------------------------------------------- 1 | error: repr must be an integer type for #[bitflags]. 2 | --> $DIR/invalid_repr.rs:2:8 3 | | 4 | 2 | #[repr(C)] 5 | | ^ 6 | 7 | error: Signed types in a repr are not supported. 8 | --> $DIR/invalid_repr.rs:10:8 9 | | 10 | 10 | #[repr(i32)] 11 | | ^^^ 12 | 13 | error: #[repr(usize)] is not supported. Use u32 or u64 instead. 14 | --> $DIR/invalid_repr.rs:18:8 15 | | 16 | 18 | #[repr(usize)] 17 | | ^^^^^ 18 | -------------------------------------------------------------------------------- /test_suite/ui/literal_out_of_range.rs: -------------------------------------------------------------------------------- 1 | //#[enumflags2::bitflags] 2 | #[repr(u64)] 3 | #[derive(Copy, Clone)] 4 | enum Foo { 5 | BigNumber = 0xdeadbeefcafebabe1337, 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /test_suite/ui/literal_out_of_range.stderr: -------------------------------------------------------------------------------- 1 | error: literal out of range for `u64` 2 | --> ui/literal_out_of_range.rs:5:17 3 | | 4 | 5 | BigNumber = 0xdeadbeefcafebabe1337, 5 | | ^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: the literal `0xdeadbeefcafebabe1337` (decimal `1051570404360395033547575`) does not fit into the type `u64` and will become `13758438582043677495u64` 8 | = help: consider using the type `u128` instead 9 | = note: `#[deny(overflowing_literals)]` on by default 10 | -------------------------------------------------------------------------------- /test_suite/ui/multiple_bits.rs: -------------------------------------------------------------------------------- 1 | #[enumflags2::bitflags] 2 | #[repr(u8)] 3 | #[derive(Copy, Clone)] 4 | enum Foo { 5 | SingleBit = 1, 6 | MultipleBits = 6, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /test_suite/ui/multiple_bits.stderr: -------------------------------------------------------------------------------- 1 | error: Flags must have exactly one set bit 2 | --> $DIR/multiple_bits.rs:6:5 3 | | 4 | 6 | MultipleBits = 6, 5 | | ^^^^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /test_suite/ui/multiple_bits_deferred.rs: -------------------------------------------------------------------------------- 1 | const THREE: u8 = 3; 2 | 3 | #[enumflags2::bitflags] 4 | #[derive(Copy, Clone)] 5 | #[repr(u8)] 6 | enum Foo { 7 | Three = THREE, 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /test_suite/ui/multiple_bits_deferred.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `AssertionFailed: ExactlyOneBitSet` is not satisfied 2 | --> ui/multiple_bits_deferred.rs:7:5 3 | | 4 | 7 | Three = THREE, 5 | | ^^^^^^^^^^^^^ the trait `ExactlyOneBitSet` is not implemented for `AssertionFailed` 6 | | 7 | = help: the trait `ExactlyOneBitSet` is implemented for `AssertionSucceeded` 8 | -------------------------------------------------------------------------------- /test_suite/ui/not_enum.rs: -------------------------------------------------------------------------------- 1 | #[enumflags2::bitflags] 2 | #[derive(Copy, Clone)] 3 | struct Foo(u16); 4 | 5 | #[enumflags2::bitflags] 6 | const WTF: u8 = 42; 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /test_suite/ui/not_enum.stderr: -------------------------------------------------------------------------------- 1 | error: expected enum for #[bitflags], found struct 2 | --> ui/not_enum.rs:3:1 3 | | 4 | 3 | struct Foo(u16); 5 | | ^^^^^^ 6 | 7 | error: expected one of: `struct`, `enum`, `union` 8 | --> ui/not_enum.rs:6:1 9 | | 10 | 6 | const WTF: u8 = 42; 11 | | ^^^^^ 12 | -------------------------------------------------------------------------------- /test_suite/ui/overlapping_flags.rs: -------------------------------------------------------------------------------- 1 | #[enumflags2::bitflags] 2 | #[repr(u8)] 3 | #[derive(Copy, Clone)] 4 | enum Foo { 5 | SomeFlag = 1 << 0, 6 | OverlappingFlag = 1 << 0, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /test_suite/ui/overlapping_flags.stderr: -------------------------------------------------------------------------------- 1 | error[E0081]: discriminant value `1` assigned more than once 2 | --> ui/overlapping_flags.rs:4:1 3 | | 4 | 4 | enum Foo { 5 | | ^^^^^^^^ 6 | 5 | SomeFlag = 1 << 0, 7 | | ------ `1` assigned here 8 | 6 | OverlappingFlag = 1 << 0, 9 | | ------ `1` assigned here 10 | -------------------------------------------------------------------------------- /test_suite/ui/shift_out_of_range.rs: -------------------------------------------------------------------------------- 1 | #[enumflags2::bitflags] 2 | #[repr(u64)] 3 | #[derive(Copy, Clone)] 4 | enum Foo { 5 | BigNumber = 1 << 69, 6 | } 7 | 8 | #[enumflags2::bitflags] 9 | #[repr(u16)] 10 | #[derive(Copy, Clone)] 11 | enum Bar { 12 | BigNumber = 1 << 20, 13 | } 14 | 15 | #[enumflags2::bitflags] 16 | #[repr(u16)] 17 | #[derive(Copy, Clone)] 18 | enum Baz { 19 | BigNumber = (1 << 10) << 10, 20 | } 21 | 22 | fn main() {} 23 | -------------------------------------------------------------------------------- /test_suite/ui/shift_out_of_range.stderr: -------------------------------------------------------------------------------- 1 | error: Flag value out of range for u64 2 | --> ui/shift_out_of_range.rs:5:5 3 | | 4 | 5 | BigNumber = 1 << 69, 5 | | ^^^^^^^^^^^^^^^^^^^ 6 | 7 | error: Flag value out of range for u16 8 | --> ui/shift_out_of_range.rs:12:5 9 | | 10 | 12 | BigNumber = 1 << 20, 11 | | ^^^^^^^^^^^^^^^^^^^ 12 | 13 | error: Flag value out of range for u16 14 | --> ui/shift_out_of_range.rs:19:5 15 | | 16 | 19 | BigNumber = (1 << 10) << 10, 17 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 18 | 19 | error[E0080]: attempt to shift left by `69_i32`, which would overflow 20 | --> ui/shift_out_of_range.rs:5:17 21 | | 22 | 5 | BigNumber = 1 << 69, 23 | | ^^^^^^^ evaluation of constant value failed here 24 | 25 | error[E0080]: attempt to shift left by `20_i32`, which would overflow 26 | --> ui/shift_out_of_range.rs:12:17 27 | | 28 | 12 | BigNumber = 1 << 20, 29 | | ^^^^^^^ evaluation of constant value failed here 30 | -------------------------------------------------------------------------------- /test_suite/ui/sneaky_make_bitflags.rs: -------------------------------------------------------------------------------- 1 | use enumflags2::{bitflags, make_bitflags}; 2 | 3 | #[bitflags] 4 | #[repr(u8)] 5 | #[derive(Copy, Clone, Debug)] 6 | enum Test { 7 | A = 1, 8 | B = 2, 9 | } 10 | 11 | impl Test { 12 | const C: u8 = 69; 13 | } 14 | 15 | fn main() { 16 | let x = make_bitflags!(Test::{C}); 17 | dbg!(x); 18 | } 19 | -------------------------------------------------------------------------------- /test_suite/ui/sneaky_make_bitflags.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> ui/sneaky_make_bitflags.rs:16:13 3 | | 4 | 16 | let x = make_bitflags!(Test::{C}); 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | | 7 | | expected `Test`, found `u8` 8 | | expected due to this 9 | | 10 | = note: this error originates in the macro `make_bitflags` (in Nightly builds, run with -Z macro-backtrace for more info) 11 | -------------------------------------------------------------------------------- /test_suite/ui/with_fields.rs: -------------------------------------------------------------------------------- 1 | #[enumflags2::bitflags] 2 | #[repr(u8)] 3 | #[derive(Copy, Clone)] 4 | enum Foo { 5 | Bar(u32), 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /test_suite/ui/with_fields.stderr: -------------------------------------------------------------------------------- 1 | error: Bitflag variants cannot contain additional data 2 | --> $DIR/with_fields.rs:5:8 3 | | 4 | 5 | Bar(u32), 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /test_suite/ui/with_generics.rs: -------------------------------------------------------------------------------- 1 | #[enumflags2::bitflags] 2 | #[repr(u8)] 3 | enum Foo { 4 | Bar, 5 | } 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /test_suite/ui/with_generics.stderr: -------------------------------------------------------------------------------- 1 | error: bitflags cannot be generic 2 | --> ui/with_generics.rs:3:9 3 | | 4 | 3 | enum Foo { 5 | | ^^^ 6 | 7 | error[E0392]: type parameter `A` is never used 8 | --> ui/with_generics.rs:3:10 9 | | 10 | 3 | enum Foo { 11 | | ^ unused type parameter 12 | | 13 | = help: consider removing `A`, referring to it in a field, or using a marker such as `PhantomData` 14 | = help: if you intended `A` to be a const parameter, use `const A: /* Type */` instead 15 | -------------------------------------------------------------------------------- /test_suite/ui/zero_disciminant.rs: -------------------------------------------------------------------------------- 1 | #[enumflags2::bitflags] 2 | #[repr(u8)] 3 | #[derive(Copy, Clone)] 4 | enum Foo { 5 | Zero = 0, 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /test_suite/ui/zero_disciminant.stderr: -------------------------------------------------------------------------------- 1 | error: Flags must have exactly one set bit 2 | --> $DIR/zero_disciminant.rs:5:5 3 | | 4 | 5 | Zero = 0, 5 | | ^^^^^^^^ 6 | -------------------------------------------------------------------------------- /test_suite/ui/zero_discriminant_deferred.rs: -------------------------------------------------------------------------------- 1 | const ZERO: u8 = 0; 2 | 3 | #[enumflags2::bitflags] 4 | #[derive(Copy, Clone)] 5 | #[repr(u8)] 6 | enum Foo { 7 | Zero = ZERO, 8 | } 9 | 10 | #[enumflags2::bitflags] 11 | #[derive(Copy, Clone)] 12 | #[repr(u8)] 13 | enum Bar { 14 | Overflown = (ZERO + 2) << 7, 15 | } 16 | 17 | fn main() {} 18 | -------------------------------------------------------------------------------- /test_suite/ui/zero_discriminant_deferred.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `AssertionFailed: ExactlyOneBitSet` is not satisfied 2 | --> ui/zero_discriminant_deferred.rs:7:5 3 | | 4 | 7 | Zero = ZERO, 5 | | ^^^^^^^^^^^ the trait `ExactlyOneBitSet` is not implemented for `AssertionFailed` 6 | | 7 | = help: the trait `ExactlyOneBitSet` is implemented for `AssertionSucceeded` 8 | 9 | error[E0277]: the trait bound `AssertionFailed: ExactlyOneBitSet` is not satisfied 10 | --> ui/zero_discriminant_deferred.rs:14:5 11 | | 12 | 14 | Overflown = (ZERO + 2) << 7, 13 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `ExactlyOneBitSet` is not implemented for `AssertionFailed` 14 | | 15 | = help: the trait `ExactlyOneBitSet` is implemented for `AssertionSucceeded` 16 | -------------------------------------------------------------------------------- /test_suite/ui_tests.rs: -------------------------------------------------------------------------------- 1 | use glob::glob; 2 | 3 | #[test] 4 | fn ui() { 5 | let t = trybuild::TestCases::new(); 6 | for test in glob("ui/*.rs").unwrap() { 7 | let path = test.unwrap(); 8 | if path == std::path::Path::new("ui/must_use_warning.rs") { 9 | t.pass(path) 10 | } else { 11 | t.compile_fail(path) 12 | } 13 | } 14 | } 15 | --------------------------------------------------------------------------------