diff options
Diffstat (limited to 'third_party/rust/flagset/README.md')
-rw-r--r-- | third_party/rust/flagset/README.md | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/third_party/rust/flagset/README.md b/third_party/rust/flagset/README.md new file mode 100644 index 0000000000..d66e250b1a --- /dev/null +++ b/third_party/rust/flagset/README.md @@ -0,0 +1,227 @@ +[![Build Status](https://github.com/enarx/flagset/workflows/test/badge.svg)](https://github.com/enarx/flagset/actions) +![Rust Version 1.36+](https://img.shields.io/badge/rustc-v1.36%2B-blue.svg) +[![Crate](https://img.shields.io/crates/v/flagset.svg)](https://crates.io/crates/flagset) +[![Docs](https://docs.rs/flagset/badge.svg)](https://docs.rs/flagset) +![License](https://img.shields.io/crates/l/flagset.svg?style=popout) + +# Welcome to FlagSet! + +FlagSet is a new, ergonomic approach to handling flags that combines the +best of existing crates like `bitflags` and `enumflags` without their +downsides. + +## Existing Implementations + +The `bitflags` crate has long been part of the Rust ecosystem. +Unfortunately, it doesn't feel like natural Rust. The `bitflags` crate +uses a wierd struct format to define flags. Flags themselves are just +integers constants, so there is little type-safety involved. But it doesn't +have any dependencies. It also allows you to define implied flags (otherwise +known as overlapping flags). + +The `enumflags` crate tried to improve on `bitflags` by using enumerations +to define flags. This was a big improvement to the natural feel of the code. +Unfortunately, there are some design flaws. To generate the flags, +procedural macros were used. This implied two separate crates plus +additional dependencies. Further, `enumflags` specifies the size of the +flags using a `repr($size)` attribute. Unfortunately, this attribute +cannot resolve type aliases, such as `c_int`. This makes `enumflags` a +poor fit for FFI, which is the most important place for a flags library. +The `enumflags` crate also disallows overlapping flags and is not +maintained. + +FlagSet improves on both of these by adopting the `enumflags` natural feel +and the `bitflags` mode of flag generation; as well as additional API usage +niceties. FlagSet has no dependencies and is extensively documented and +tested. It also tries very hard to prevent you from making mistakes by +avoiding external usage of the integer types. FlagSet is also a zero-cost +abstraction: all functions are inlineable and should reduce to the core +integer operations. FlagSet also does not depend on stdlib, so it can be +used in `no_std` libraries and applications. + +## Defining Flags + +Flags are defined using the `flags!` macro: + +```rust +use flagset::{FlagSet, flags}; +use std::os::raw::c_int; + +flags! { + enum FlagsA: u8 { + Foo, + Bar, + Baz, + } + + enum FlagsB: c_int { + Foo, + Bar, + Baz, + } +} +``` + +Notice that a flag definition looks just like a regular enumeration, with +the addition of the field-size type. The field-size type is required and +can be either a type or a type alias. Both examples are given above. + +Also note that the field-size type specifies the size of the corresponding +`FlagSet` type, not size of the enumeration itself. To specify the size of +the enumeration, use the `repr($size)` attribute as specified below. + +## Flag Values + +Flags often need values assigned to them. This can be done implicitly, +where the value depends on the order of the flags: + +```rust +use flagset::{FlagSet, flags}; + +flags! { + enum Flags: u16 { + Foo, // Implicit Value: 0b0001 + Bar, // Implicit Value: 0b0010 + Baz, // Implicit Value: 0b0100 + } +} +``` + +Alternatively, flag values can be defined explicitly, by specifying any +`const` expression: + +```rust +use flagset::{FlagSet, flags}; + +flags! { + enum Flags: u16 { + Foo = 0x01, // Explicit Value: 0b0001 + Bar = 2, // Explicit Value: 0b0010 + Baz = 0b0100, // Explicit Value: 0b0100 + } +} +``` + +Flags can also overlap or "imply" other flags: + +```rust +use flagset::{FlagSet, flags}; + +flags! { + enum Flags: u16 { + Foo = 0b0001, + Bar = 0b0010, + Baz = 0b0110, // Implies Bar + All = (Flags::Foo | Flags::Bar | Flags::Baz).bits(), + } +} +``` + +## Specifying Attributes + +Attributes can be used on the enumeration itself or any of the values: + +```rust +use flagset::{FlagSet, flags}; + +flags! { + #[derive(PartialOrd, Ord)] + enum Flags: u8 { + Foo, + #[deprecated] + Bar, + Baz, + } +} +``` + +## Collections of Flags + +A collection of flags is a `FlagSet<T>`. If you are storing the flags in +memory, the raw `FlagSet<T>` type should be used. However, if you want to +receive flags as an input to a function, you should use +`impl Into<FlagSet<T>>`. This allows for very ergonomic APIs: + +```rust +use flagset::{FlagSet, flags}; + +flags! { + enum Flags: u8 { + Foo, + Bar, + Baz, + } +} + +struct Container(FlagSet<Flags>); + +impl Container { + fn new(flags: impl Into<FlagSet<Flags>>) -> Container { + Container(flags.into()) + } +} + +assert_eq!(Container::new(Flags::Foo | Flags::Bar).0.bits(), 0b011); +assert_eq!(Container::new(Flags::Foo).0.bits(), 0b001); +assert_eq!(Container::new(None).0.bits(), 0b000); +``` + +## Operations + +Operations can be performed on a `FlagSet<F>` or on individual flags: + +| Operator | Assignment Operator | Meaning | +|----------|---------------------|------------------------| +| \| | \|= | Union | +| & | &= | Intersection | +| ^ | ^= | Toggle specified flags | +| - | -= | Difference | +| % | %= | Symmetric difference | +| ! | | Toggle all flags | + +## Optional Serde support + +[Serde] support can be enabled with the 'serde' feature flag. You can then serialize and +deserialize `FlagSet<T>` to and from any of the [supported formats]: + + ```rust + use flagset::{FlagSet, flags}; + + flags! { + enum Flags: u8 { + Foo, + Bar, + } + } + + let flagset = Flags::Foo | Flags::Bar; + let json = serde_json::to_string(&flagset).unwrap(); + let flagset: FlagSet<Flags> = serde_json::from_str(&json).unwrap(); + assert_eq!(flagset.bits(), 0b011); + ``` + +For serialization and deserialization of flags enum itself, you can use the [`serde_repr`] crate +(or implement `serde::ser::Serialize` and `serde:de::Deserialize` manually), combined with the +appropriate `repr` attribute: + + ```rust + use flagset::{FlagSet, flags}; + use serde_repr::{Serialize_repr, Deserialize_repr}; + + flags! { + #[repr(u8)] + #[derive(Deserialize_repr, Serialize_repr)] + enum Flags: u8 { + Foo, + Bar, + } + } + + let json = serde_json::to_string(&Flags::Foo).unwrap(); + let flag: Flags = serde_json::from_str(&json).unwrap(); + assert_eq!(flag, Flags::Foo); + ``` + +[Serde]: https://serde.rs/ +[supported formats]: https://serde.rs/#data-formats +[`serde_repr`]: https://crates.io/crates/serde_repr |