const fn bit(word: u32, position: u32) -> bool {
word & (1 << (position - 1)) != 0
}
const fn mask(position: u32) -> u32 {
1 << (position - 1)
}
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Permissions {
pub print: bool,
pub modify: bool,
pub copy: bool,
pub annotate: bool,
pub fill_form: bool,
pub extract: bool,
pub assemble: bool,
pub print_high_quality: bool,
}
impl Permissions {
pub const ALL: Self = Self {
print: true,
modify: true,
copy: true,
annotate: true,
fill_form: true,
extract: true,
assemble: true,
print_high_quality: true,
};
pub const NONE: Self = Self {
print: false,
modify: false,
copy: false,
annotate: false,
fill_form: false,
extract: false,
assemble: false,
print_high_quality: false,
};
#[must_use]
pub const fn from_bits(bits: u32) -> Self {
Self {
print: bit(bits, 3),
modify: bit(bits, 4),
copy: bit(bits, 5),
annotate: bit(bits, 6),
fill_form: bit(bits, 9),
extract: bit(bits, 10),
assemble: bit(bits, 11),
print_high_quality: bit(bits, 12),
}
}
#[must_use]
pub const fn bits(self) -> u32 {
let mut word = 0;
if self.print {
word |= mask(3);
}
if self.modify {
word |= mask(4);
}
if self.copy {
word |= mask(5);
}
if self.annotate {
word |= mask(6);
}
if self.fill_form {
word |= mask(9);
}
if self.extract {
word |= mask(10);
}
if self.assemble {
word |= mask(11);
}
if self.print_high_quality {
word |= mask(12);
}
word
}
}
#[cfg(test)]
mod tests {
use super::Permissions;
const TABLE_22: [(u32, &str); 8] = [
(3, "print"),
(4, "modify"),
(5, "copy"),
(6, "annotate"),
(9, "fill_form"),
(10, "extract"),
(11, "assemble"),
(12, "print_high_quality"),
];
fn field(p: Permissions, name: &str) -> bool {
match name {
"print" => p.print,
"modify" => p.modify,
"copy" => p.copy,
"annotate" => p.annotate,
"fill_form" => p.fill_form,
"extract" => p.extract,
"assemble" => p.assemble,
"print_high_quality" => p.print_high_quality,
other => panic!("no field {other}"),
}
}
#[test]
fn each_table_22_bit_grants_exactly_its_own_permission() {
for (position, name) in TABLE_22 {
let word = 1u32 << (position - 1);
let p = Permissions::from_bits(word);
for (_, other) in TABLE_22 {
assert_eq!(
field(p, other),
other == name,
"bit {position} ({name}) should grant only {name}, but {other} read \
{}",
field(p, other)
);
}
}
}
#[test]
fn every_bit_set_is_all() {
assert_eq!(Permissions::from_bits(0xFFFF_FFFF), Permissions::ALL);
}
#[test]
fn no_bit_set_is_none() {
assert_eq!(Permissions::from_bits(0), Permissions::NONE);
}
#[test]
fn reserved_bits_grant_nothing() {
let named: u32 = TABLE_22
.iter()
.fold(0, |acc, (position, _)| acc | (1 << (position - 1)));
assert_eq!(Permissions::from_bits(!named), Permissions::NONE);
}
#[test]
fn bits_round_trips_the_named_bits_and_drops_the_rest() {
let named: u32 = TABLE_22
.iter()
.fold(0, |acc, (position, _)| acc | (1 << (position - 1)));
assert_eq!(Permissions::ALL.bits(), named);
assert_eq!(Permissions::NONE.bits(), 0);
assert_eq!(Permissions::from_bits(0xFFFF_FFFF).bits(), named);
for (position, _) in TABLE_22 {
let word = 1u32 << (position - 1);
assert_eq!(Permissions::from_bits(word).bits(), word, "bit {position}");
}
}
#[test]
fn the_worked_example_p_4092_grants_everything_named() {
assert_eq!(Permissions::from_bits(4092), Permissions::ALL);
}
}