#flags #bitflags #bitmask

byteflags

一个宏,用于生成具有 u8 标志值的类似位标志的结构

1 个不稳定版本

0.1.0 2024年6月23日

#599 in 数据结构

MIT 许可证

15KB
213

byteflags

Rust Latest version Documentation License

byteflags 实现了一个类似 bitflags 的结构,使用一个完整的字节而不是一个位来表示每个字段。这允许用户在保持位标志结构易用性的同时,在结构中编码额外的信息。

用法

将以下内容添加到您的 Cargo.toml

[dependencies]
byteflags = "0.1.0"

并将以下内容添加到您的源代码

use byteflags::*;

示例

生成一个标志结构

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
}

有争议的设计选择

这些设计选择可能不会立即明显。如果您需要不同的行为,请随时提交一个 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 }
    );
}

依赖项

~0.7–1.7MB
~37K SLoC