1 unstable release
0.1.0 | Jun 23, 2024 |
---|
#992 in Rust patterns
15KB
213 lines
byteflags
byteflags
implements a bitflags-like structure using a full byte for each field instead of a bit. This allows the user to maintain the ergonomics of a bitflag structure while encoding additional information within.
Usage
Add this to your Cargo.toml
:
[dependencies]
byteflags = "0.1.0"
and this to your source code:
use byteflags::*;
Example
Generate a flags structure:
use byteflags::*;
byteflags! {
/// The structure definition contains a set of named flags
struct MyFlags {
A = "Flag A",
B = "Flag B",
C = "Flag C",
D = "Flag D",
}
}
fn main() {
/// The resulting structure can be used similarly to bitflags
/// Constant values
assert_eq!(
MyFlags::A,
MyFlags { A: 1, B: 0, C: 0, D: 0 }
);
/// Union
assert_eq!(
(MyFlags::A.union(MyFlags::B)),
MyFlags { A: 1, B: 1, C: 0, D: 0 }
);
/// Difference
assert_eq!(
(MyFlags { A: 0, B: 1, C: 1, D: 1} - MyFlags {A: 0, B: 1, C: 0, D: 1 }),
MyFlags { A: 0, B: 0, C: 1, D: 0}
);
/// Because each flag is a u8, you can do some arithmetic as well, including but not limited to:
/// Addition
assert_eq!(
MyFlags::A + MyFlags::A + MyFlags::B,
MyFlags { A: 2, B: 1, C: 0, D: 0 }
);
/// Multiplication by u8
assert_eq!(
(MyFlags { A: 2, B: 1, C: 0, D: 0 } * 3),
MyFlags { A: 6, B: 3, C: 0, D: 0 }
);
/// Division by u8
assert_eq!(
(MyFlags { A: 60, B: 15, C: 0, D: 0 } / 3),
MyFlags { A: 20, B: 5, C: 0, D: 0 }
);
/// Multiplication by other byteflag struct
assert_eq!(
(MyFlags { A: 1, B: 2, C: 3, D: 4 } * MyFlags { A: 0, B: 1, C: 2, D: 3 }),
MyFlags { A: 0, B: 2, C: 6, D: 12 }
);
/// Subtractive Assignment
let mut flags = MyFlags { A: 2, B: 2, C: 1, D: 0, };
flags -= MyFlags::C;
assert_eq!(
flags,
MyFlags { A: 2, B: 2, C: 0, D: 0 }
);
/// There are a couple other bells and whistles, such as:
/// Conversion and serialization
let flags = MyFlags { A: 0, B: 1, C: 10, D: 100 };
assert_eq!(flags.to_vec(), vec![0, 1, 10, 100]);
assert_eq!(serde_json::to_string(&flags).unwrap(), "[0,1,10,100]".to_string());
assert_eq!(flags, serde_json::from_str::<MyFlags>("[0,1,10,100]").unwrap());
/// contains() and match
let d = MyFlags::D;
assert_eq!(
match d {
MyFlags::A => "No",
MyFlags::B => "Nope",
MyFlags::C => "Wrong",
MyFlags::D => "This one!",
_ => "",
},
"This one!"
);
let flags = MyFlags { A: 0, B: 1, C: 10, D: 100 };
assert!(flags.contains(&MyFlags::B));
assert!(!flags.contains(&MyFlags::A));
/// Display with labels
assert_eq!(
format!("{}", MyFlags::A),
"Flag A"
);
assert_eq!(
format!("{}", MyFlags::A + MyFlags::B),
"Flag A + Flag B"
);
/// Access a flag via index
assert_eq!(
MyFlags::ALL_CONSTS[2],
MyFlags::C
);
/// Get random flag with the "rand" feature
/// Probabilities are weighted according to the flag value
let weights = MyFlags{ A: 255, B: 127, C: 0, D: 0};
let random_value = weights.get_random();
// 66% chance of being MyFlags::A, 33% chance of being MyFlags::B
}
Opinionated Design Choices
These design choices might not be immediately obvious. If you require a different behavior, please feel free to submit a pull request.
use byteflags::*;
byteflags! {
struct MyFlags {
A = "Flag A",
B = "Flag B",
}
}
fn main() {
/// All arithmetic is saturating
assert_eq!(
(MyFlags{ A: 200, B: 0 } + MyFlags{ A: 200, B: 0 }),
MyFlags{ A: u8::MAX, B: 0 }
);
assert_eq!(
(MyFlags::A - (MyFlags::A + MyFlags::A)),
MyFlags::empty()
);
assert_eq!(
(MyFlags{ A: 128, B: 1 } * 128),
MyFlags{ A: u8::MAX, B: 128 }
);
/// contains() only checks if a value is nonzero
assert_eq!(
(MyFlags::A).contains(&MyFlags{ A: 100, B: 0 }),
true
);
/// Display also only checks if a value is nonzero
assert_eq!(
format!("{}", MyFlags{ A: 5, B: 10 }),
"Flag A + Flag B"
);
/// all() gives values of 1
assert_eq!(
MyFlags::all(),
MyFlags{ A: 1, B: 1 }
);
}
Dependencies
~0.7–1.6MB
~35K SLoC