├── .github ├── CODEOWNERS ├── dependabot.yml └── workflows │ ├── automerge.yml │ ├── commisery.yml │ ├── dco.yml │ ├── enarxbot.yml │ ├── lint.yml │ └── test.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── rust-toolchain.toml └── src └── lib.rs /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Each line is a file pattern followed by one or more owners. 2 | # Owners will be automatically notified about new PRs and 3 | # an owner's approval is required to merge to protected branches. 4 | * @enarx/codeowners 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" -------------------------------------------------------------------------------- /.github/workflows/automerge.yml: -------------------------------------------------------------------------------- 1 | name: Auto merge 2 | on: 3 | pull_request_target 4 | 5 | jobs: 6 | auto-merge: 7 | permissions: 8 | pull-requests: write 9 | contents: write 10 | uses: enarx/.github/.github/workflows/automerge.yml@main 11 | -------------------------------------------------------------------------------- /.github/workflows/commisery.yml: -------------------------------------------------------------------------------- 1 | name: Commisery 2 | on: 3 | pull_request: 4 | types: [edited, opened, synchronize, reopened] 5 | 6 | permissions: 7 | contents: read 8 | pull-requests: write 9 | issues: write 10 | 11 | jobs: 12 | commit-message: 13 | uses: enarx/.github/.github/workflows/commisery.yml@main 14 | -------------------------------------------------------------------------------- /.github/workflows/dco.yml: -------------------------------------------------------------------------------- 1 | name: DCO Check 2 | on: [pull_request] 3 | permissions: read-all 4 | jobs: 5 | dco: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: tisonkun/actions-dco@f1024cd563550b5632e754df11b7d30b73be54a5 # v1.1 9 | -------------------------------------------------------------------------------- /.github/workflows/enarxbot.yml: -------------------------------------------------------------------------------- 1 | name: enarxbot 2 | 3 | on: 4 | check_run: 5 | check_suite: 6 | create: 7 | delete: 8 | deployment: 9 | deployment_status: 10 | fork: 11 | gollum: 12 | issue_comment: 13 | issues: 14 | label: 15 | milestone: 16 | page_build: 17 | project: 18 | project_card: 19 | project_column: 20 | public: 21 | pull_request_target: 22 | types: 23 | - assigned 24 | - unassigned 25 | - labeled 26 | - unlabeled 27 | - opened 28 | - edited 29 | - closed 30 | - reopened 31 | - synchronize 32 | - ready_for_review 33 | - locked 34 | - unlocked 35 | - review_requested 36 | - review_request_removed 37 | push: 38 | registry_package: 39 | release: 40 | status: 41 | watch: 42 | schedule: 43 | - cron: '*/15 * * * *' 44 | workflow_dispatch: 45 | 46 | jobs: 47 | enarxbot: 48 | runs-on: ubuntu-latest 49 | env: 50 | BOT_TOKEN: ${{ secrets.BOT_TOKEN }} 51 | name: enarxbot 52 | steps: 53 | - uses: enarx/bot@master 54 | pull-request-responsibility: 55 | runs-on: ubuntu-latest 56 | env: 57 | BOT_TOKEN: ${{ secrets.BOT_TOKEN }} 58 | name: pull-request-responsibility 59 | steps: 60 | - uses: actions-automation/pull-request-responsibility@main 61 | with: 62 | actions: "request,assign,copy-labels-linked,merge" 63 | reviewers: "reviews" 64 | num_to_request: 3 65 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: lint 3 | jobs: 4 | fmt: 5 | name: cargo fmt 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | - name: Setup Rust toolchain 10 | run: rustup show && rustup update && rustup component add rustfmt 11 | - name: cargo fmt 12 | run: cargo fmt --all -- --check 13 | 14 | clippy: 15 | name: cargo clippy 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Setup Rust toolchain 20 | run: rustup show && rustup update && rustup component add clippy 21 | - name: cargo clippy 22 | run: cargo clippy --all-features --tests -- -D warnings 23 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: test 3 | jobs: 4 | test: 5 | name: ${{ matrix.toolchain }} (${{ matrix.profile.name }}) 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | - name: Install Rust 10 | run: rustup toolchain install ${{ matrix.toolchain }} 11 | - name: cargo test 12 | run: cargo +${{ matrix.toolchain }} test --all-features ${{ matrix.profile.flag }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | toolchain: 17 | - nightly 18 | - beta 19 | - stable 20 | - 1.65.0 21 | profile: 22 | - name: debug 23 | - name: release 24 | flag: --release 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flagset" 3 | version = "0.4.7" 4 | authors = ["Nathaniel McCallum "] 5 | edition = "2021" 6 | rust-version = "1.65" 7 | 8 | license = "Apache-2.0" 9 | keywords = ["flags", "bitflags", "enum", "enumflags"] 10 | readme = "README.md" 11 | repository = "https://github.com/enarx/flagset" 12 | homepage = "https://github.com/enarx/flagset" 13 | documentation = "https://docs.rs/flagset" 14 | description = "Data types and a macro for generating enumeration-based bit flags" 15 | 16 | [features] 17 | std = [] 18 | 19 | [dependencies.serde] 20 | version = "1.0" 21 | optional = true 22 | features = [ "serde_derive" ] 23 | 24 | [dev-dependencies.serde_json] 25 | version = "1.0" 26 | 27 | [dev-dependencies.serde_repr] 28 | version = "0.1" 29 | 30 | [package.metadata.docs.rs] 31 | all-features = true 32 | rustdoc-args = ["--cfg", "docsrs"] 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://github.com/enarx/flagset/workflows/test/badge.svg)](https://github.com/enarx/flagset/actions) 2 | ![Rust Version 1.65+](https://img.shields.io/badge/rustc-v1.65%2B-blue.svg) 3 | [![Crate](https://img.shields.io/crates/v/flagset.svg)](https://crates.io/crates/flagset) 4 | [![Docs](https://docs.rs/flagset/badge.svg)](https://docs.rs/flagset) 5 | ![License](https://img.shields.io/crates/l/flagset.svg?style=popout) 6 | 7 | # Welcome to FlagSet! 8 | 9 | FlagSet is a new, ergonomic approach to handling flags that combines the 10 | best of existing crates like `bitflags` and `enumflags` without their 11 | downsides. 12 | 13 | ## Existing Implementations 14 | 15 | The `bitflags` crate has long been part of the Rust ecosystem. 16 | Unfortunately, it doesn't feel like natural Rust. The `bitflags` crate 17 | uses a wierd struct format to define flags. Flags themselves are just 18 | integers constants, so there is little type-safety involved. But it doesn't 19 | have any dependencies. It also allows you to define implied flags (otherwise 20 | known as overlapping flags). 21 | 22 | The `enumflags` crate tried to improve on `bitflags` by using enumerations 23 | to define flags. This was a big improvement to the natural feel of the code. 24 | Unfortunately, there are some design flaws. To generate the flags, 25 | procedural macros were used. This implied two separate crates plus 26 | additional dependencies. Further, `enumflags` specifies the size of the 27 | flags using a `repr($size)` attribute. Unfortunately, this attribute 28 | cannot resolve type aliases, such as `c_int`. This makes `enumflags` a 29 | poor fit for FFI, which is the most important place for a flags library. 30 | The `enumflags` crate also disallows overlapping flags and is not 31 | maintained. 32 | 33 | FlagSet improves on both of these by adopting the `enumflags` natural feel 34 | and the `bitflags` mode of flag generation; as well as additional API usage 35 | niceties. FlagSet has no dependencies and is extensively documented and 36 | tested. It also tries very hard to prevent you from making mistakes by 37 | avoiding external usage of the integer types. FlagSet is also a zero-cost 38 | abstraction: all functions are inlineable and should reduce to the core 39 | integer operations. FlagSet also does not depend on stdlib, so it can be 40 | used in `no_std` libraries and applications. 41 | 42 | ## Defining Flags 43 | 44 | Flags are defined using the `flags!` macro: 45 | 46 | ```rust 47 | use flagset::{FlagSet, flags}; 48 | use std::os::raw::c_int; 49 | 50 | flags! { 51 | enum FlagsA: u8 { 52 | Foo, 53 | Bar, 54 | Baz, 55 | } 56 | 57 | enum FlagsB: c_int { 58 | Foo, 59 | Bar, 60 | Baz, 61 | } 62 | } 63 | ``` 64 | 65 | Notice that a flag definition looks just like a regular enumeration, with 66 | the addition of the field-size type. The field-size type is required and 67 | can be either a type or a type alias. Both examples are given above. 68 | 69 | Also note that the field-size type specifies the size of the corresponding 70 | `FlagSet` type, not size of the enumeration itself. To specify the size of 71 | the enumeration, use the `repr($size)` attribute as specified below. 72 | 73 | ## Flag Values 74 | 75 | Flags often need values assigned to them. This can be done implicitly, 76 | where the value depends on the order of the flags: 77 | 78 | ```rust 79 | use flagset::{FlagSet, flags}; 80 | 81 | flags! { 82 | enum Flags: u16 { 83 | Foo, // Implicit Value: 0b0001 84 | Bar, // Implicit Value: 0b0010 85 | Baz, // Implicit Value: 0b0100 86 | } 87 | } 88 | ``` 89 | 90 | Alternatively, flag values can be defined explicitly, by specifying any 91 | `const` expression: 92 | 93 | ```rust 94 | use flagset::{FlagSet, flags}; 95 | 96 | flags! { 97 | enum Flags: u16 { 98 | Foo = 0x01, // Explicit Value: 0b0001 99 | Bar = 2, // Explicit Value: 0b0010 100 | Baz = 0b0100, // Explicit Value: 0b0100 101 | } 102 | } 103 | ``` 104 | 105 | Flags can also overlap or "imply" other flags: 106 | 107 | ```rust 108 | use flagset::{FlagSet, flags}; 109 | 110 | flags! { 111 | enum Flags: u16 { 112 | Foo = 0b0001, 113 | Bar = 0b0010, 114 | Baz = 0b0110, // Implies Bar 115 | All = (Flags::Foo | Flags::Bar | Flags::Baz).bits(), 116 | } 117 | } 118 | ``` 119 | 120 | ## Specifying Attributes 121 | 122 | Attributes can be used on the enumeration itself or any of the values: 123 | 124 | ```rust 125 | use flagset::{FlagSet, flags}; 126 | 127 | flags! { 128 | #[derive(PartialOrd, Ord)] 129 | enum Flags: u8 { 130 | Foo, 131 | #[deprecated] 132 | Bar, 133 | Baz, 134 | } 135 | } 136 | ``` 137 | 138 | ## Collections of Flags 139 | 140 | A collection of flags is a `FlagSet`. If you are storing the flags in 141 | memory, the raw `FlagSet` type should be used. However, if you want to 142 | receive flags as an input to a function, you should use 143 | `impl Into>`. This allows for very ergonomic APIs: 144 | 145 | ```rust 146 | use flagset::{FlagSet, flags}; 147 | 148 | flags! { 149 | enum Flags: u8 { 150 | Foo, 151 | Bar, 152 | Baz, 153 | } 154 | } 155 | 156 | struct Container(FlagSet); 157 | 158 | impl Container { 159 | fn new(flags: impl Into>) -> Container { 160 | Container(flags.into()) 161 | } 162 | } 163 | 164 | assert_eq!(Container::new(Flags::Foo | Flags::Bar).0.bits(), 0b011); 165 | assert_eq!(Container::new(Flags::Foo).0.bits(), 0b001); 166 | assert_eq!(Container::new(None).0.bits(), 0b000); 167 | ``` 168 | 169 | ## Operations 170 | 171 | Operations can be performed on a `FlagSet` or on individual flags: 172 | 173 | | Operator | Assignment Operator | Meaning | 174 | |----------|---------------------|------------------------| 175 | | \| | \|= | Union | 176 | | & | &= | Intersection | 177 | | ^ | ^= | Toggle specified flags | 178 | | - | -= | Difference | 179 | | % | %= | Symmetric difference | 180 | | ! | | Toggle all flags | 181 | 182 | ## Optional Serde support 183 | 184 | [Serde] support can be enabled with the 'serde' feature flag. You can then serialize and 185 | deserialize `FlagSet` to and from any of the [supported formats]: 186 | 187 | ```rust 188 | use flagset::{FlagSet, flags}; 189 | 190 | flags! { 191 | enum Flags: u8 { 192 | Foo, 193 | Bar, 194 | } 195 | } 196 | 197 | let flagset = Flags::Foo | Flags::Bar; 198 | let json = serde_json::to_string(&flagset).unwrap(); 199 | let flagset: FlagSet = serde_json::from_str(&json).unwrap(); 200 | assert_eq!(flagset.bits(), 0b011); 201 | ``` 202 | 203 | For serialization and deserialization of flags enum itself, you can use the [`serde_repr`] crate 204 | (or implement `serde::ser::Serialize` and `serde:de::Deserialize` manually), combined with the 205 | appropriate `repr` attribute: 206 | 207 | ```rust 208 | use flagset::{FlagSet, flags}; 209 | use serde_repr::{Serialize_repr, Deserialize_repr}; 210 | 211 | flags! { 212 | #[repr(u8)] 213 | #[derive(Deserialize_repr, Serialize_repr)] 214 | enum Flags: u8 { 215 | Foo, 216 | Bar, 217 | } 218 | } 219 | 220 | let json = serde_json::to_string(&Flags::Foo).unwrap(); 221 | let flag: Flags = serde_json::from_str(&json).unwrap(); 222 | assert_eq!(flag, Flags::Foo); 223 | ``` 224 | 225 | [Serde]: https://serde.rs/ 226 | [supported formats]: https://serde.rs/#data-formats 227 | [`serde_repr`]: https://crates.io/crates/serde_repr 228 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.65" 3 | profile = "minimal" 4 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2019 Red Hat, Inc. 3 | // 4 | // Author: Nathaniel McCallum 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | // 18 | 19 | //! # Welcome to FlagSet! 20 | //! 21 | //! FlagSet is a new, ergonomic approach to handling flags that combines the 22 | //! best of existing crates like `bitflags` and `enumflags` without their 23 | //! downsides. 24 | //! 25 | //! ## Existing Implementations 26 | //! 27 | //! The `bitflags` crate has long been part of the Rust ecosystem. 28 | //! Unfortunately, it doesn't feel like natural Rust. The `bitflags` crate 29 | //! uses a weird struct format to define flags. Flags themselves are just 30 | //! integers constants, so there is little type-safety involved. But it doesn't 31 | //! have any dependencies. It also allows you to define implied flags (otherwise 32 | //! known as overlapping flags). 33 | //! 34 | //! The `enumflags` crate tried to improve on `bitflags` by using enumerations 35 | //! to define flags. This was a big improvement to the natural feel of the code. 36 | //! Unfortunately, there are some design flaws. To generate the flags, 37 | //! procedural macros were used. This implied two separate crates plus 38 | //! additional dependencies. Further, `enumflags` specifies the size of the 39 | //! flags using a `repr($size)` attribute. Unfortunately, this attribute 40 | //! cannot resolve type aliases, such as `c_int`. This makes `enumflags` a 41 | //! poor fit for FFI, which is the most important place for a flags library. 42 | //! The `enumflags` crate also disallows overlapping flags and is not 43 | //! maintained. 44 | //! 45 | //! FlagSet improves on both of these by adopting the `enumflags` natural feel 46 | //! and the `bitflags` mode of flag generation; as well as additional API usage 47 | //! niceties. FlagSet has no dependencies and is extensively documented and 48 | //! tested. It also tries very hard to prevent you from making mistakes by 49 | //! avoiding external usage of the integer types. FlagSet is also a zero-cost 50 | //! abstraction: all functions are inlineable and should reduce to the core 51 | //! integer operations. FlagSet also does not depend on stdlib, so it can be 52 | //! used in `no_std` libraries and applications. 53 | //! 54 | //! ## Defining Flags 55 | //! 56 | //! Flags are defined using the `flags!` macro: 57 | //! 58 | //! ``` 59 | //! use flagset::{FlagSet, flags}; 60 | //! use std::os::raw::c_int; 61 | //! 62 | //! flags! { 63 | //! enum FlagsA: u8 { 64 | //! Foo, 65 | //! Bar, 66 | //! Baz, 67 | //! } 68 | //! 69 | //! enum FlagsB: c_int { 70 | //! Foo, 71 | //! Bar, 72 | //! Baz, 73 | //! } 74 | //! } 75 | //! ``` 76 | //! 77 | //! Notice that a flag definition looks just like a regular enumeration, with 78 | //! the addition of the field-size type. The field-size type is required and 79 | //! can be either a type or a type alias. Both examples are given above. 80 | //! 81 | //! Also note that the field-size type specifies the size of the corresponding 82 | //! `FlagSet` type, not size of the enumeration itself. To specify the size of 83 | //! the enumeration, use the `repr($size)` attribute as specified below. 84 | //! 85 | //! ## Flag Values 86 | //! 87 | //! Flags often need values assigned to them. This can be done implicitly, 88 | //! where the value depends on the order of the flags: 89 | //! 90 | //! ``` 91 | //! use flagset::{FlagSet, flags}; 92 | //! 93 | //! flags! { 94 | //! enum Flags: u16 { 95 | //! Foo, // Implicit Value: 0b0001 96 | //! Bar, // Implicit Value: 0b0010 97 | //! Baz, // Implicit Value: 0b0100 98 | //! } 99 | //! } 100 | //! ``` 101 | //! 102 | //! Alternatively, flag values can be defined explicitly, by specifying any 103 | //! `const` expression: 104 | //! 105 | //! ``` 106 | //! use flagset::{FlagSet, flags}; 107 | //! 108 | //! flags! { 109 | //! enum Flags: u16 { 110 | //! Foo = 0x01, // Explicit Value: 0b0001 111 | //! Bar = 2, // Explicit Value: 0b0010 112 | //! Baz = 0b0100, // Explicit Value: 0b0100 113 | //! } 114 | //! } 115 | //! ``` 116 | //! 117 | //! Flags can also overlap or "imply" other flags: 118 | //! 119 | //! ``` 120 | //! use flagset::{FlagSet, flags}; 121 | //! 122 | //! flags! { 123 | //! enum Flags: u16 { 124 | //! Foo = 0b0001, 125 | //! Bar = 0b0010, 126 | //! Baz = 0b0110, // Implies Bar 127 | //! All = (Flags::Foo | Flags::Bar | Flags::Baz).bits(), 128 | //! } 129 | //! } 130 | //! ``` 131 | //! 132 | //! ## Specifying Attributes 133 | //! 134 | //! Attributes can be used on the enumeration itself or any of the values: 135 | //! 136 | //! ``` 137 | //! use flagset::{FlagSet, flags}; 138 | //! 139 | //! flags! { 140 | //! #[derive(PartialOrd, Ord)] 141 | //! enum Flags: u8 { 142 | //! Foo, 143 | //! #[deprecated] 144 | //! Bar, 145 | //! Baz, 146 | //! } 147 | //! } 148 | //! ``` 149 | //! 150 | //! ## Collections of Flags 151 | //! 152 | //! A collection of flags is a `FlagSet`. If you are storing the flags in 153 | //! memory, the raw `FlagSet` type should be used. However, if you want to 154 | //! receive flags as an input to a function, you should use 155 | //! `impl Into>`. This allows for very ergonomic APIs: 156 | //! 157 | //! ``` 158 | //! use flagset::{FlagSet, flags}; 159 | //! 160 | //! flags! { 161 | //! enum Flags: u8 { 162 | //! Foo, 163 | //! Bar, 164 | //! Baz, 165 | //! } 166 | //! } 167 | //! 168 | //! struct Container(FlagSet); 169 | //! 170 | //! impl Container { 171 | //! fn new(flags: impl Into>) -> Container { 172 | //! Container(flags.into()) 173 | //! } 174 | //! } 175 | //! 176 | //! assert_eq!(Container::new(Flags::Foo | Flags::Bar).0.bits(), 0b011); 177 | //! assert_eq!(Container::new(Flags::Foo).0.bits(), 0b001); 178 | //! assert_eq!(Container::new(None).0.bits(), 0b000); 179 | //! ``` 180 | //! 181 | //! ## Operations 182 | //! 183 | //! Operations can be performed on a `FlagSet` or on individual flags: 184 | //! 185 | //! | Operator | Assignment Operator | Meaning | 186 | //! |----------|---------------------|------------------------| 187 | //! | \| | \|= | Union | 188 | //! | & | &= | Intersection | 189 | //! | ^ | ^= | Toggle specified flags | 190 | //! | - | -= | Difference | 191 | //! | % | %= | Symmetric difference | 192 | //! | ! | | Toggle all flags | 193 | //! 194 | #![cfg_attr( 195 | feature = "serde", 196 | doc = r#" 197 | 198 | ## Optional Serde support 199 | 200 | [Serde] support can be enabled with the 'serde' feature flag. You can then serialize and 201 | deserialize `FlagSet` to and from any of the [supported formats]: 202 | 203 | ``` 204 | use flagset::{FlagSet, flags}; 205 | 206 | flags! { 207 | enum Flags: u8 { 208 | Foo, 209 | Bar, 210 | } 211 | } 212 | 213 | let flagset = Flags::Foo | Flags::Bar; 214 | let json = serde_json::to_string(&flagset).unwrap(); 215 | let flagset: FlagSet = serde_json::from_str(&json).unwrap(); 216 | assert_eq!(flagset.bits(), 0b011); 217 | ``` 218 | 219 | For serialization and deserialization of flags enum itself, you can use the [`serde_repr`] crate 220 | (or implement `serde::ser::Serialize` and `serde:de::Deserialize` manually), combined with the 221 | appropriate `repr` attribute: 222 | 223 | ``` 224 | use flagset::{FlagSet, flags}; 225 | use serde_repr::{Serialize_repr, Deserialize_repr}; 226 | 227 | flags! { 228 | #[repr(u8)] 229 | #[derive(Deserialize_repr, Serialize_repr)] 230 | enum Flags: u8 { 231 | Foo, 232 | Bar, 233 | } 234 | } 235 | 236 | let json = serde_json::to_string(&Flags::Foo).unwrap(); 237 | let flag: Flags = serde_json::from_str(&json).unwrap(); 238 | assert_eq!(flag, Flags::Foo); 239 | ``` 240 | 241 | [Serde]: https://serde.rs/ 242 | [supported formats]: https://serde.rs/#data-formats 243 | [`serde_repr`]: https://crates.io/crates/serde_repr 244 | "# 245 | )] 246 | #![allow(unknown_lints)] 247 | #![warn(clippy::all)] 248 | #![cfg_attr(not(feature = "std"), no_std)] 249 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 250 | 251 | use core::fmt::{Debug, Formatter, Result}; 252 | use core::ops::*; 253 | 254 | /// Error type returned when creating a new flagset from bits is invalid or undefined. 255 | /// ``` 256 | /// use flagset::{FlagSet, flags}; 257 | /// 258 | /// flags! { 259 | /// pub enum Flag: u16 { 260 | /// Foo = 0b0001, 261 | /// Bar = 0b0010, 262 | /// Baz = 0b0100, 263 | /// Qux = 0b1010, // Implies Bar 264 | /// } 265 | /// } 266 | /// 267 | /// assert_eq!(FlagSet::::new(0b01101), Err(flagset::InvalidBits)); // Invalid 268 | /// assert_eq!(FlagSet::::new(0b10101), Err(flagset::InvalidBits)); // Unknown 269 | /// ``` 270 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 271 | pub struct InvalidBits; 272 | 273 | impl core::fmt::Display for InvalidBits { 274 | #[inline] 275 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 276 | write!(f, "invalid bits") 277 | } 278 | } 279 | 280 | #[cfg(feature = "std")] 281 | impl std::error::Error for InvalidBits {} 282 | 283 | #[doc(hidden)] 284 | pub trait Flags: 285 | Copy 286 | + Clone 287 | + Debug 288 | + PartialEq 289 | + Eq 290 | + BitAnd> 291 | + BitOr> 292 | + BitXor> 293 | + Sub> 294 | + Rem> 295 | + Not> 296 | + Into> 297 | + 'static 298 | { 299 | type Type: Copy 300 | + Clone 301 | + Debug 302 | + PartialEq 303 | + Eq 304 | + Default 305 | + BitAnd 306 | + BitAndAssign 307 | + BitOr 308 | + BitOrAssign 309 | + BitXor 310 | + BitXorAssign 311 | + Not; 312 | 313 | /// The zero value for this type (empty flagset). 314 | const ZERO: Self::Type; 315 | 316 | /// A slice containing all the possible flag values. 317 | const LIST: &'static [Self]; 318 | 319 | /// Creates an empty `FlagSet` of this type 320 | #[inline] 321 | fn none() -> FlagSet { 322 | FlagSet::empty() 323 | } 324 | } 325 | 326 | #[repr(C)] 327 | #[derive(Copy, Clone, Eq, Hash)] 328 | pub struct FlagSet(F::Type); 329 | 330 | #[doc(hidden)] 331 | #[derive(Copy, Clone)] 332 | pub struct Iter(FlagSet, usize); 333 | 334 | impl Iterator for Iter { 335 | type Item = F; 336 | 337 | #[inline] 338 | fn next(&mut self) -> Option { 339 | while self.1 < F::LIST.len() { 340 | let next = F::LIST[self.1]; 341 | self.1 += 1; 342 | 343 | if self.0.contains(next) { 344 | return Some(next); 345 | } 346 | } 347 | 348 | None 349 | } 350 | } 351 | 352 | impl IntoIterator for FlagSet { 353 | type Item = F; 354 | type IntoIter = Iter; 355 | 356 | /// Iterate over the flags in the set. 357 | /// 358 | /// **NOTE**: The order in which the flags are iterated is undefined. 359 | /// 360 | /// ``` 361 | /// use flagset::{FlagSet, flags}; 362 | /// 363 | /// flags! { 364 | /// enum Flag: u8 { 365 | /// Foo = 0b001, 366 | /// Bar = 0b010, 367 | /// Baz = 0b100 368 | /// } 369 | /// } 370 | /// 371 | /// let set = Flag::Foo | Flag::Bar; 372 | /// let mut iter = set.into_iter(); 373 | /// assert_eq!(iter.next(), Some(Flag::Foo)); 374 | /// assert_eq!(iter.next(), Some(Flag::Bar)); 375 | /// assert_eq!(iter.next(), None); 376 | /// ``` 377 | #[inline] 378 | fn into_iter(self) -> Self::IntoIter { 379 | Iter(self, 0) 380 | } 381 | } 382 | 383 | impl Debug for FlagSet { 384 | #[inline] 385 | fn fmt(&self, f: &mut Formatter) -> Result { 386 | write!(f, "FlagSet(")?; 387 | for (i, flag) in self.into_iter().enumerate() { 388 | write!(f, "{}{:?}", if i > 0 { " | " } else { "" }, flag)?; 389 | } 390 | write!(f, ")") 391 | } 392 | } 393 | 394 | impl>> PartialEq for FlagSet { 395 | #[inline] 396 | fn eq(&self, rhs: &R) -> bool { 397 | self.0 == (*rhs).into().0 398 | } 399 | } 400 | 401 | impl AsRef for FlagSet { 402 | #[inline] 403 | fn as_ref(&self) -> &F::Type { 404 | &self.0 405 | } 406 | } 407 | 408 | impl From>> for FlagSet { 409 | /// Converts from `Option>` to `FlagSet`. 410 | /// 411 | /// Most notably, this allows for the use of `None` in many places to 412 | /// substitute for manually creating an empty `FlagSet`. See below. 413 | /// 414 | /// ``` 415 | /// use flagset::{FlagSet, flags}; 416 | /// 417 | /// flags! { 418 | /// enum Flag: u8 { 419 | /// Foo = 0b001, 420 | /// Bar = 0b010, 421 | /// Baz = 0b100 422 | /// } 423 | /// } 424 | /// 425 | /// fn convert(v: impl Into>) -> u8 { 426 | /// v.into().bits() 427 | /// } 428 | /// 429 | /// assert_eq!(convert(Flag::Foo | Flag::Bar), 0b011); 430 | /// assert_eq!(convert(Flag::Foo), 0b001); 431 | /// assert_eq!(convert(None), 0b000); 432 | /// ``` 433 | #[inline] 434 | fn from(value: Option>) -> FlagSet { 435 | value.unwrap_or_default() 436 | } 437 | } 438 | 439 | impl Default for FlagSet { 440 | /// Creates a new, empty FlagSet. 441 | /// 442 | /// ``` 443 | /// use flagset::{FlagSet, flags}; 444 | /// 445 | /// flags! { 446 | /// enum Flag: u8 { 447 | /// Foo = 0b001, 448 | /// Bar = 0b010, 449 | /// Baz = 0b100 450 | /// } 451 | /// } 452 | /// 453 | /// let set = FlagSet::::default(); 454 | /// assert!(set.is_empty()); 455 | /// assert!(!set.is_full()); 456 | /// assert!(!set.contains(Flag::Foo)); 457 | /// assert!(!set.contains(Flag::Bar)); 458 | /// assert!(!set.contains(Flag::Baz)); 459 | /// ``` 460 | #[inline] 461 | fn default() -> Self { 462 | Self::empty() 463 | } 464 | } 465 | 466 | impl Not for FlagSet { 467 | type Output = Self; 468 | 469 | /// Calculates the complement of the current set. 470 | /// 471 | /// In common parlance, this returns the set of all possible flags that are 472 | /// not in the current set. 473 | /// 474 | /// ``` 475 | /// use flagset::{FlagSet, flags}; 476 | /// 477 | /// flags! { 478 | /// #[derive(PartialOrd, Ord)] 479 | /// enum Flag: u8 { 480 | /// Foo = 1 << 0, 481 | /// Bar = 1 << 1, 482 | /// Baz = 1 << 2 483 | /// } 484 | /// } 485 | /// 486 | /// let set = !FlagSet::from(Flag::Foo); 487 | /// assert!(!set.is_empty()); 488 | /// assert!(!set.is_full()); 489 | /// assert!(!set.contains(Flag::Foo)); 490 | /// assert!(set.contains(Flag::Bar)); 491 | /// assert!(set.contains(Flag::Baz)); 492 | /// ``` 493 | #[inline] 494 | fn not(self) -> Self { 495 | FlagSet(!self.0) 496 | } 497 | } 498 | 499 | impl>> BitAnd for FlagSet { 500 | type Output = Self; 501 | 502 | /// Calculates the intersection of the current set and the specified flags. 503 | /// 504 | /// ``` 505 | /// use flagset::{FlagSet, flags}; 506 | /// 507 | /// flags! { 508 | /// #[derive(PartialOrd, Ord)] 509 | /// pub enum Flag: u8 { 510 | /// Foo = 0b001, 511 | /// Bar = 0b010, 512 | /// Baz = 0b100 513 | /// } 514 | /// } 515 | /// 516 | /// let set0 = Flag::Foo | Flag::Bar; 517 | /// let set1 = Flag::Baz | Flag::Bar; 518 | /// assert_eq!(set0 & set1, Flag::Bar); 519 | /// assert_eq!(set0 & Flag::Foo, Flag::Foo); 520 | /// assert_eq!(set1 & Flag::Baz, Flag::Baz); 521 | /// ``` 522 | #[inline] 523 | fn bitand(self, rhs: R) -> Self { 524 | FlagSet(self.0 & rhs.into().0) 525 | } 526 | } 527 | 528 | impl>> BitAndAssign for FlagSet { 529 | /// Assigns the intersection of the current set and the specified flags. 530 | /// 531 | /// ``` 532 | /// use flagset::{FlagSet, flags}; 533 | /// 534 | /// flags! { 535 | /// enum Flag: u64 { 536 | /// Foo = 0b001, 537 | /// Bar = 0b010, 538 | /// Baz = 0b100 539 | /// } 540 | /// } 541 | /// 542 | /// let mut set0 = Flag::Foo | Flag::Bar; 543 | /// let mut set1 = Flag::Baz | Flag::Bar; 544 | /// 545 | /// set0 &= set1; 546 | /// assert_eq!(set0, Flag::Bar); 547 | /// 548 | /// set1 &= Flag::Baz; 549 | /// assert_eq!(set0, Flag::Bar); 550 | /// ``` 551 | #[inline] 552 | fn bitand_assign(&mut self, rhs: R) { 553 | self.0 &= rhs.into().0 554 | } 555 | } 556 | 557 | impl>> BitOr for FlagSet { 558 | type Output = Self; 559 | 560 | /// Calculates the union of the current set with the specified flags. 561 | /// 562 | /// ``` 563 | /// use flagset::{FlagSet, flags}; 564 | /// 565 | /// flags! { 566 | /// #[derive(PartialOrd, Ord)] 567 | /// pub enum Flag: u8 { 568 | /// Foo = 0b001, 569 | /// Bar = 0b010, 570 | /// Baz = 0b100 571 | /// } 572 | /// } 573 | /// 574 | /// let set0 = Flag::Foo | Flag::Bar; 575 | /// let set1 = Flag::Baz | Flag::Bar; 576 | /// assert_eq!(set0 | set1, FlagSet::full()); 577 | /// ``` 578 | #[inline] 579 | fn bitor(self, rhs: R) -> Self { 580 | FlagSet(self.0 | rhs.into().0) 581 | } 582 | } 583 | 584 | impl>> BitOrAssign for FlagSet { 585 | /// Assigns the union of the current set with the specified flags. 586 | /// 587 | /// ``` 588 | /// use flagset::{FlagSet, flags}; 589 | /// 590 | /// flags! { 591 | /// enum Flag: u64 { 592 | /// Foo = 0b001, 593 | /// Bar = 0b010, 594 | /// Baz = 0b100 595 | /// } 596 | /// } 597 | /// 598 | /// let mut set0 = Flag::Foo | Flag::Bar; 599 | /// let mut set1 = Flag::Bar | Flag::Baz; 600 | /// 601 | /// set0 |= set1; 602 | /// assert_eq!(set0, FlagSet::full()); 603 | /// 604 | /// set1 |= Flag::Baz; 605 | /// assert_eq!(set1, Flag::Bar | Flag::Baz); 606 | /// ``` 607 | #[inline] 608 | fn bitor_assign(&mut self, rhs: R) { 609 | self.0 |= rhs.into().0 610 | } 611 | } 612 | 613 | impl>> BitXor for FlagSet { 614 | type Output = Self; 615 | 616 | /// Calculates the current set with the specified flags toggled. 617 | /// 618 | /// This is commonly known as toggling the presence 619 | /// 620 | /// ``` 621 | /// use flagset::{FlagSet, flags}; 622 | /// 623 | /// flags! { 624 | /// enum Flag: u32 { 625 | /// Foo = 0b001, 626 | /// Bar = 0b010, 627 | /// Baz = 0b100 628 | /// } 629 | /// } 630 | /// 631 | /// let set0 = Flag::Foo | Flag::Bar; 632 | /// let set1 = Flag::Baz | Flag::Bar; 633 | /// assert_eq!(set0 ^ set1, Flag::Foo | Flag::Baz); 634 | /// assert_eq!(set0 ^ Flag::Foo, Flag::Bar); 635 | /// ``` 636 | #[inline] 637 | fn bitxor(self, rhs: R) -> Self { 638 | FlagSet(self.0 ^ rhs.into().0) 639 | } 640 | } 641 | 642 | impl>> BitXorAssign for FlagSet { 643 | /// Assigns the current set with the specified flags toggled. 644 | /// 645 | /// ``` 646 | /// use flagset::{FlagSet, flags}; 647 | /// 648 | /// flags! { 649 | /// enum Flag: u16 { 650 | /// Foo = 0b001, 651 | /// Bar = 0b010, 652 | /// Baz = 0b100 653 | /// } 654 | /// } 655 | /// 656 | /// let mut set0 = Flag::Foo | Flag::Bar; 657 | /// let mut set1 = Flag::Baz | Flag::Bar; 658 | /// 659 | /// set0 ^= set1; 660 | /// assert_eq!(set0, Flag::Foo | Flag::Baz); 661 | /// 662 | /// set1 ^= Flag::Baz; 663 | /// assert_eq!(set1, Flag::Bar); 664 | /// ``` 665 | #[inline] 666 | fn bitxor_assign(&mut self, rhs: R) { 667 | self.0 ^= rhs.into().0 668 | } 669 | } 670 | 671 | impl>> Sub for FlagSet { 672 | type Output = Self; 673 | 674 | /// Calculates set difference (the current set without the specified flags). 675 | /// 676 | /// ``` 677 | /// use flagset::{FlagSet, flags}; 678 | /// 679 | /// flags! { 680 | /// pub enum Flag: u8 { 681 | /// Foo = 1, 682 | /// Bar = 2, 683 | /// Baz = 4 684 | /// } 685 | /// } 686 | /// 687 | /// let set0 = Flag::Foo | Flag::Bar; 688 | /// let set1 = Flag::Baz | Flag::Bar; 689 | /// assert_eq!(set0 - set1, Flag::Foo); 690 | /// ``` 691 | #[inline] 692 | fn sub(self, rhs: R) -> Self { 693 | self & !rhs.into() 694 | } 695 | } 696 | 697 | impl>> SubAssign for FlagSet { 698 | /// Assigns set difference (the current set without the specified flags). 699 | /// 700 | /// ``` 701 | /// use flagset::{FlagSet, flags}; 702 | /// 703 | /// flags! { 704 | /// pub enum Flag: u8 { 705 | /// Foo = 1, 706 | /// Bar = 2, 707 | /// Baz = 4 708 | /// } 709 | /// } 710 | /// 711 | /// let mut set0 = Flag::Foo | Flag::Bar; 712 | /// set0 -= Flag::Baz | Flag::Bar; 713 | /// assert_eq!(set0, Flag::Foo); 714 | /// ``` 715 | #[inline] 716 | fn sub_assign(&mut self, rhs: R) { 717 | *self &= !rhs.into(); 718 | } 719 | } 720 | 721 | impl>> Rem for FlagSet { 722 | type Output = Self; 723 | 724 | /// Calculates the symmetric difference between two sets. 725 | /// 726 | /// The symmetric difference between two sets is the set of all flags 727 | /// that appear in one set or the other, but not both. 728 | /// 729 | /// ``` 730 | /// use flagset::{FlagSet, flags}; 731 | /// 732 | /// flags! { 733 | /// pub enum Flag: u8 { 734 | /// Foo = 1, 735 | /// Bar = 2, 736 | /// Baz = 4 737 | /// } 738 | /// } 739 | /// 740 | /// let set0 = Flag::Foo | Flag::Bar; 741 | /// let set1 = Flag::Baz | Flag::Bar; 742 | /// assert_eq!(set0 % set1, Flag::Foo | Flag::Baz); 743 | /// ``` 744 | #[inline] 745 | fn rem(self, rhs: R) -> Self { 746 | let rhs = rhs.into(); 747 | (self - rhs) | (rhs - self) 748 | } 749 | } 750 | 751 | impl>> RemAssign for FlagSet { 752 | /// Assigns the symmetric difference between two sets. 753 | /// 754 | /// The symmetric difference between two sets is the set of all flags 755 | /// that appear in one set or the other, but not both. 756 | /// 757 | /// ``` 758 | /// use flagset::{FlagSet, flags}; 759 | /// 760 | /// flags! { 761 | /// pub enum Flag: u8 { 762 | /// Foo = 1, 763 | /// Bar = 2, 764 | /// Baz = 4 765 | /// } 766 | /// } 767 | /// 768 | /// let mut set0 = Flag::Foo | Flag::Bar; 769 | /// let set1 = Flag::Baz | Flag::Bar; 770 | /// set0 %= set1; 771 | /// assert_eq!(set0, Flag::Foo | Flag::Baz); 772 | /// ``` 773 | #[inline] 774 | fn rem_assign(&mut self, rhs: R) { 775 | *self = *self % rhs 776 | } 777 | } 778 | 779 | impl>> Extend for FlagSet { 780 | /// Add values by iterating over some collection. 781 | /// 782 | /// ``` 783 | /// use flagset::{FlagSet, flags}; 784 | /// 785 | /// flags! { 786 | /// pub enum Flag: u8 { 787 | /// Foo = 1, 788 | /// Bar = 2, 789 | /// Baz = 4 790 | /// } 791 | /// } 792 | /// 793 | /// let flag_vec = vec![Flag::Bar, Flag::Baz]; 794 | /// let mut some_extended_flags = FlagSet::from(Flag::Foo); 795 | /// some_extended_flags.extend(flag_vec); 796 | /// assert_eq!(some_extended_flags, Flag::Foo | Flag::Bar | Flag::Baz); 797 | /// ``` 798 | fn extend(&mut self, iter: T) 799 | where 800 | T: IntoIterator, 801 | { 802 | for item in iter { 803 | *self |= item; 804 | } 805 | } 806 | } 807 | 808 | impl FlagSet { 809 | /// Creates a new set from bits; returning `Err(InvalidBits)` on invalid/unknown bits. 810 | /// 811 | /// ``` 812 | /// use flagset::{FlagSet, flags}; 813 | /// 814 | /// flags! { 815 | /// pub enum Flag: u16 { 816 | /// Foo = 0b0001, 817 | /// Bar = 0b0010, 818 | /// Baz = 0b0100, 819 | /// Qux = 0b1010, // Implies Bar 820 | /// } 821 | /// } 822 | /// 823 | /// assert_eq!(FlagSet::::new(0b00101), Ok(Flag::Foo | Flag::Baz)); 824 | /// assert_eq!(FlagSet::::new(0b01101), Err(flagset::InvalidBits)); // Invalid 825 | /// assert_eq!(FlagSet::::new(0b10101), Err(flagset::InvalidBits)); // Unknown 826 | /// ``` 827 | #[inline] 828 | pub fn new(bits: F::Type) -> core::result::Result { 829 | if Self::new_truncated(bits).0 == bits { 830 | return Ok(FlagSet(bits)); 831 | } 832 | 833 | Err(InvalidBits) 834 | } 835 | 836 | /// Creates a new set from bits; truncating invalid/unknown bits. 837 | /// 838 | /// ``` 839 | /// use flagset::{FlagSet, flags}; 840 | /// 841 | /// flags! { 842 | /// pub enum Flag: u16 { 843 | /// Foo = 0b0001, 844 | /// Bar = 0b0010, 845 | /// Baz = 0b0100, 846 | /// Qux = 0b1010, // Implies Bar 847 | /// } 848 | /// } 849 | /// 850 | /// let set = FlagSet::new_truncated(0b11101); // Has invalid and unknown. 851 | /// assert_eq!(set, Flag::Foo | Flag::Baz); 852 | /// assert_eq!(set.bits(), 0b00101); // Has neither. 853 | /// ``` 854 | #[inline] 855 | pub fn new_truncated(bits: F::Type) -> Self { 856 | let mut set = Self::default(); 857 | 858 | for flag in FlagSet::(bits) { 859 | set |= flag; 860 | } 861 | 862 | set 863 | } 864 | 865 | /// Creates a new set from bits; use of invalid/unknown bits is undefined. 866 | /// 867 | /// ``` 868 | /// use flagset::{FlagSet, flags}; 869 | /// 870 | /// flags! { 871 | /// pub enum Flag: u16 { 872 | /// Foo = 0b0001, 873 | /// Bar = 0b0010, 874 | /// Baz = 0b0100, 875 | /// Qux = 0b1010, // Implies Bar 876 | /// } 877 | /// } 878 | /// 879 | /// // Unknown and invalid bits are retained. Behavior is undefined. 880 | /// const set: FlagSet = unsafe { FlagSet::::new_unchecked(0b11101) }; 881 | /// assert_eq!(set.bits(), 0b11101); 882 | /// ``` 883 | /// 884 | /// # Safety 885 | /// 886 | /// This constructor doesn't check that the bits are valid. If you pass 887 | /// undefined flags, undefined behavior may result. 888 | #[inline] 889 | pub const unsafe fn new_unchecked(bits: F::Type) -> Self { 890 | FlagSet(bits) 891 | } 892 | 893 | /// Creates a new, empty FlagSet. 894 | /// 895 | /// ``` 896 | /// use flagset::{FlagSet, flags}; 897 | /// 898 | /// flags! { 899 | /// enum Flag: u8 { 900 | /// Foo = 0b001, 901 | /// Bar = 0b010, 902 | /// Baz = 0b100 903 | /// } 904 | /// } 905 | /// 906 | /// let set = FlagSet::::empty(); 907 | /// assert!(set.is_empty()); 908 | /// assert!(!set.is_full()); 909 | /// assert!(!set.contains(Flag::Foo)); 910 | /// assert!(!set.contains(Flag::Bar)); 911 | /// assert!(!set.contains(Flag::Baz)); 912 | /// ``` 913 | #[inline] 914 | pub const fn empty() -> Self { 915 | FlagSet(F::ZERO) 916 | } 917 | 918 | /// Creates a new FlagSet containing all possible flags. 919 | /// 920 | /// ``` 921 | /// use flagset::{FlagSet, flags}; 922 | /// 923 | /// flags! { 924 | /// pub enum Flag: u8 { 925 | /// Foo = 1, 926 | /// Bar = 2, 927 | /// Baz = 4 928 | /// } 929 | /// } 930 | /// 931 | /// let set = FlagSet::full(); 932 | /// assert!(!set.is_empty()); 933 | /// assert!(set.is_full()); 934 | /// assert!(set.contains(Flag::Foo)); 935 | /// assert!(set.contains(Flag::Bar)); 936 | /// assert!(set.contains(Flag::Baz)); 937 | /// ``` 938 | #[inline] 939 | pub fn full() -> Self { 940 | let mut set = Self::default(); 941 | for f in F::LIST { 942 | set |= *f 943 | } 944 | set 945 | } 946 | 947 | /// Returns the raw bits of the set. 948 | /// 949 | /// ``` 950 | /// use flagset::{FlagSet, flags}; 951 | /// 952 | /// flags! { 953 | /// pub enum Flag: u16 { 954 | /// Foo = 0b0001, 955 | /// Bar = 0b0010, 956 | /// Baz = 0b0100, 957 | /// } 958 | /// } 959 | /// 960 | /// let set = Flag::Foo | Flag::Baz; 961 | /// assert_eq!(set.bits(), 0b0101u16); 962 | /// ``` 963 | #[inline] 964 | pub fn bits(self) -> F::Type { 965 | self.0 966 | } 967 | 968 | /// Returns true if the FlagSet contains no flags. 969 | /// 970 | /// ``` 971 | /// use flagset::{FlagSet, flags}; 972 | /// 973 | /// flags! { 974 | /// pub enum Flag: u8 { 975 | /// Foo = 1, 976 | /// Bar = 2, 977 | /// Baz = 4 978 | /// } 979 | /// } 980 | /// 981 | /// let mut set = Flag::Foo | Flag::Bar; 982 | /// assert!(!set.is_empty()); 983 | /// 984 | /// set &= Flag::Baz; 985 | /// assert!(set.is_empty()); 986 | /// ``` 987 | #[inline] 988 | pub fn is_empty(self) -> bool { 989 | self == Self::default() 990 | } 991 | 992 | /// Returns true if the FlagSet contains all possible flags. 993 | /// 994 | /// ``` 995 | /// use flagset::{FlagSet, flags}; 996 | /// 997 | /// flags! { 998 | /// pub enum Flag: u8 { 999 | /// Foo = 1, 1000 | /// Bar = 2, 1001 | /// Baz = 4 1002 | /// } 1003 | /// } 1004 | /// 1005 | /// let mut set = Flag::Foo | Flag::Bar; 1006 | /// assert!(!set.is_full()); 1007 | /// 1008 | /// set |= Flag::Baz; 1009 | /// assert!(set.is_full()); 1010 | /// ``` 1011 | #[inline] 1012 | pub fn is_full(self) -> bool { 1013 | self == Self::full() 1014 | } 1015 | 1016 | /// Returns true if the two `FlagSet`s do not share any flags. 1017 | /// 1018 | /// ``` 1019 | /// use flagset::{FlagSet, flags}; 1020 | /// 1021 | /// flags! { 1022 | /// pub enum Flag: u8 { 1023 | /// Foo = 1, 1024 | /// Bar = 2, 1025 | /// Baz = 4 1026 | /// } 1027 | /// } 1028 | /// 1029 | /// let set = Flag::Foo | Flag::Bar; 1030 | /// assert!(!set.is_disjoint(Flag::Foo)); 1031 | /// assert!(!set.is_disjoint(Flag::Foo | Flag::Baz)); 1032 | /// assert!(set.is_disjoint(Flag::Baz)); 1033 | /// ``` 1034 | #[inline] 1035 | pub fn is_disjoint(self, rhs: impl Into>) -> bool { 1036 | self & rhs == Self::default() 1037 | } 1038 | 1039 | /// Returns true if this FlagSet is a superset of the specified flags. 1040 | /// 1041 | /// ``` 1042 | /// use flagset::{FlagSet, flags}; 1043 | /// 1044 | /// flags! { 1045 | /// pub enum Flag: u8 { 1046 | /// Foo = 1, 1047 | /// Bar = 2, 1048 | /// Baz = 4 1049 | /// } 1050 | /// } 1051 | /// 1052 | /// let set = Flag::Foo | Flag::Bar; 1053 | /// assert!(set.contains(Flag::Foo)); 1054 | /// assert!(set.contains(Flag::Foo | Flag::Bar)); 1055 | /// assert!(!set.contains(Flag::Foo | Flag::Bar | Flag::Baz)); 1056 | /// ``` 1057 | #[inline] 1058 | pub fn contains(self, rhs: impl Into>) -> bool { 1059 | let rhs = rhs.into(); 1060 | self & rhs == rhs 1061 | } 1062 | 1063 | /// Removes all flags from the FlagSet. 1064 | /// 1065 | /// ``` 1066 | /// use flagset::{FlagSet, flags}; 1067 | /// 1068 | /// flags! { 1069 | /// pub enum Flag: u8 { 1070 | /// Foo = 1, 1071 | /// Bar = 2, 1072 | /// Baz = 4 1073 | /// } 1074 | /// } 1075 | /// 1076 | /// let mut set = Flag::Foo | Flag::Bar; 1077 | /// assert!(!set.is_empty()); 1078 | /// 1079 | /// set.clear(); 1080 | /// assert!(set.is_empty()); 1081 | /// ``` 1082 | #[inline] 1083 | pub fn clear(&mut self) { 1084 | *self = Self::default(); 1085 | } 1086 | 1087 | /// Clears the current set and returns an iterator of all removed flags. 1088 | /// 1089 | /// ``` 1090 | /// use flagset::{FlagSet, flags}; 1091 | /// 1092 | /// flags! { 1093 | /// pub enum Flag: u8 { 1094 | /// Foo = 1, 1095 | /// Bar = 2, 1096 | /// Baz = 4 1097 | /// } 1098 | /// } 1099 | /// 1100 | /// let mut set = Flag::Foo | Flag::Bar; 1101 | /// let mut iter = set.drain(); 1102 | /// assert!(set.is_empty()); 1103 | /// assert_eq!(iter.next(), Some(Flag::Foo)); 1104 | /// assert_eq!(iter.next(), Some(Flag::Bar)); 1105 | /// assert_eq!(iter.next(), None); 1106 | /// ``` 1107 | #[inline] 1108 | pub fn drain(&mut self) -> Iter { 1109 | let iter = self.into_iter(); 1110 | *self = Self::default(); 1111 | iter 1112 | } 1113 | 1114 | /// Retain only the flags flags specified by the predicate. 1115 | /// 1116 | /// ``` 1117 | /// use flagset::{FlagSet, flags}; 1118 | /// 1119 | /// flags! { 1120 | /// pub enum Flag: u8 { 1121 | /// Foo = 1, 1122 | /// Bar = 2, 1123 | /// Baz = 4 1124 | /// } 1125 | /// } 1126 | /// 1127 | /// let mut set0 = Flag::Foo | Flag::Bar; 1128 | /// set0.retain(|f| f != Flag::Foo); 1129 | /// assert_eq!(set0, Flag::Bar); 1130 | /// ``` 1131 | #[inline] 1132 | pub fn retain(&mut self, func: impl Fn(F) -> bool) { 1133 | for f in self.into_iter() { 1134 | if !func(f) { 1135 | *self -= f 1136 | } 1137 | } 1138 | } 1139 | } 1140 | 1141 | #[cfg(feature = "serde")] 1142 | impl serde::Serialize for FlagSet 1143 | where 1144 | F::Type: serde::ser::Serialize, 1145 | { 1146 | #[inline] 1147 | fn serialize(&self, serializer: S) -> core::result::Result 1148 | where 1149 | S: serde::ser::Serializer, 1150 | { 1151 | self.0.serialize(serializer) 1152 | } 1153 | } 1154 | 1155 | #[cfg(feature = "serde")] 1156 | impl<'de, F: Flags> serde::Deserialize<'de> for FlagSet 1157 | where 1158 | F::Type: serde::de::Deserialize<'de>, 1159 | { 1160 | #[inline] 1161 | fn deserialize(deserializer: D) -> core::result::Result 1162 | where 1163 | D: serde::de::Deserializer<'de>, 1164 | { 1165 | Ok(FlagSet(F::Type::deserialize(deserializer)?)) 1166 | } 1167 | } 1168 | 1169 | /// Define flag value using the `enum` syntax. See below for details. 1170 | /// 1171 | /// Each enumeration value **MUST** have a specified value. 1172 | /// 1173 | /// The width of the bitfield **MUST** also be specified by its integer type. 1174 | /// 1175 | /// It is important to note that the size of the flag enumeration itself is 1176 | /// unrelated to the size of the corresponding `FlagSet` instance. 1177 | /// 1178 | /// It is also worth noting that this macro automatically implements a variety 1179 | /// of standard traits including: 1180 | /// * Copy 1181 | /// * Clone 1182 | /// * Debug 1183 | /// * PartialEq 1184 | /// * Eq 1185 | /// * From<$enum> for $integer 1186 | /// * Not 1187 | /// * BitAnd 1188 | /// * BitOr 1189 | /// * BitXor 1190 | /// * Sub 1191 | /// * Rem 1192 | /// 1193 | /// ``` 1194 | /// use std::mem::{align_of, size_of}; 1195 | /// use flagset::{FlagSet, flags}; 1196 | /// 1197 | /// flags! { 1198 | /// enum FlagEmpty: u32 {} 1199 | /// 1200 | /// enum Flag8: u8 { 1201 | /// Foo = 0b001, 1202 | /// Bar = 0b010, 1203 | /// Baz = 0b100 1204 | /// } 1205 | /// 1206 | /// pub enum Flag16: u16 { 1207 | /// Foo, 1208 | /// Bar, 1209 | /// #[deprecated] 1210 | /// Baz, 1211 | /// } 1212 | /// 1213 | /// #[derive(PartialOrd, Ord)] 1214 | /// enum Flag32: u32 { 1215 | /// Foo = 0b001, 1216 | /// #[deprecated] 1217 | /// Bar = 0b010, 1218 | /// Baz = 0b100 1219 | /// } 1220 | /// 1221 | /// #[repr(u64)] 1222 | /// enum Flag64: u64 { 1223 | /// Foo = 0b001, 1224 | /// Bar = 0b010, 1225 | /// Baz = 0b100 1226 | /// } 1227 | /// 1228 | /// #[repr(u32)] 1229 | /// enum Flag128: u128 { 1230 | /// Foo = 0b001, 1231 | /// Bar = 0b010, 1232 | /// Baz = 0b100 1233 | /// } 1234 | /// } 1235 | /// 1236 | /// assert_eq!(size_of::(), 1); 1237 | /// assert_eq!(size_of::(), 1); 1238 | /// assert_eq!(size_of::(), 1); 1239 | /// assert_eq!(size_of::(), 8); 1240 | /// assert_eq!(size_of::(), 4); 1241 | /// 1242 | /// assert_eq!(align_of::(), 1); 1243 | /// assert_eq!(align_of::(), 1); 1244 | /// assert_eq!(align_of::(), 1); 1245 | /// assert_eq!(align_of::(), align_of::()); 1246 | /// assert_eq!(align_of::(), align_of::()); 1247 | /// 1248 | /// assert_eq!(size_of::>(), size_of::()); 1249 | /// assert_eq!(size_of::>(), size_of::()); 1250 | /// assert_eq!(size_of::>(), size_of::()); 1251 | /// assert_eq!(size_of::>(), size_of::()); 1252 | /// assert_eq!(size_of::>(), size_of::()); 1253 | /// 1254 | /// assert_eq!(align_of::>(), align_of::()); 1255 | /// assert_eq!(align_of::>(), align_of::()); 1256 | /// assert_eq!(align_of::>(), align_of::()); 1257 | /// assert_eq!(align_of::>(), align_of::()); 1258 | /// assert_eq!(align_of::>(), align_of::()); 1259 | /// ``` 1260 | #[macro_export] 1261 | macro_rules! flags { 1262 | () => {}; 1263 | 1264 | // Entry point for enumerations without values. 1265 | ($(#[$m:meta])* $p:vis enum $n:ident: $t:ty { $($(#[$a:meta])* $k:ident),+ $(,)* } $($next:tt)*) => { 1266 | $crate::flags! { $(#[$m])* $p enum $n: $t { $($(#[$a])* $k = (1 << $n::$k as $t)),+ } $($next)* } 1267 | }; 1268 | 1269 | // Entrypoint for enumerations with values. 1270 | ($(#[$m:meta])* $p:vis enum $n:ident: $t:ty { $($(#[$a:meta])*$k:ident = $v:expr),* $(,)* } $($next:tt)*) => { 1271 | $(#[$m])* 1272 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 1273 | $p enum $n { $($(#[$a])* $k),* } 1274 | 1275 | impl $crate::Flags for $n { 1276 | type Type = $t; 1277 | 1278 | const ZERO: Self::Type = 0; 1279 | 1280 | const LIST: &'static [Self] = &[$($n::$k),*]; 1281 | } 1282 | 1283 | impl ::core::convert::From<$n> for $crate::FlagSet<$n> { 1284 | #[inline] 1285 | fn from(value: $n) -> Self { 1286 | unsafe { 1287 | match value { 1288 | $($n::$k => Self::new_unchecked($v)),* 1289 | } 1290 | } 1291 | } 1292 | } 1293 | 1294 | impl ::core::ops::Not for $n { 1295 | type Output = $crate::FlagSet<$n>; 1296 | 1297 | #[inline] 1298 | fn not(self) -> Self::Output { 1299 | !$crate::FlagSet::from(self) 1300 | } 1301 | } 1302 | 1303 | impl>> ::core::ops::BitAnd for $n { 1304 | type Output = $crate::FlagSet<$n>; 1305 | 1306 | #[inline] 1307 | fn bitand(self, rhs: R) -> Self::Output { 1308 | $crate::FlagSet::from(self) & rhs 1309 | } 1310 | } 1311 | 1312 | impl>> ::core::ops::BitOr for $n { 1313 | type Output = $crate::FlagSet<$n>; 1314 | 1315 | #[inline] 1316 | fn bitor(self, rhs: R) -> Self::Output { 1317 | $crate::FlagSet::from(self) | rhs 1318 | } 1319 | } 1320 | 1321 | impl>> ::core::ops::BitXor for $n { 1322 | type Output = $crate::FlagSet<$n>; 1323 | 1324 | #[inline] 1325 | fn bitxor(self, rhs: R) -> Self::Output { 1326 | $crate::FlagSet::from(self) ^ rhs 1327 | } 1328 | } 1329 | 1330 | impl>> ::core::ops::Sub for $n { 1331 | type Output = $crate::FlagSet<$n>; 1332 | 1333 | #[inline] 1334 | fn sub(self, rhs: R) -> Self::Output { 1335 | $crate::FlagSet::from(self) - rhs 1336 | } 1337 | } 1338 | 1339 | impl>> ::core::ops::Rem for $n { 1340 | type Output = $crate::FlagSet<$n>; 1341 | 1342 | #[inline] 1343 | fn rem(self, rhs: R) -> Self::Output { 1344 | $crate::FlagSet::from(self) % rhs 1345 | } 1346 | } 1347 | 1348 | $crate::flags! { $($next)* } 1349 | }; 1350 | } 1351 | --------------------------------------------------------------------------------