{"id":50306134,"url":"https://github.com/qjerome/flaglet","last_synced_at":"2026-05-28T16:30:41.932Z","repository":{"id":360655464,"uuid":"1251108565","full_name":"qjerome/flaglet","owner":"qjerome","description":"Bitflag API from a plain enum — no DSL, no wrapper type you don't own","archived":false,"fork":false,"pushed_at":"2026-05-27T09:32:03.000Z","size":32,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-27T11:11:28.165Z","etag":null,"topics":["bitflags","bitmask","enum","flags","no-std","proc-macro","proc-macro-attribute","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/qjerome.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-BSD-2-Clause","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-27T08:50:21.000Z","updated_at":"2026-05-27T09:32:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/qjerome/flaglet","commit_stats":null,"previous_names":["qjerome/flaglet"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/qjerome/flaglet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qjerome%2Fflaglet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qjerome%2Fflaglet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qjerome%2Fflaglet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qjerome%2Fflaglet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qjerome","download_url":"https://codeload.github.com/qjerome/flaglet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qjerome%2Fflaglet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33617717,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bitflags","bitmask","enum","flags","no-std","proc-macro","proc-macro-attribute","rust"],"created_at":"2026-05-28T16:30:39.212Z","updated_at":"2026-05-28T16:30:41.917Z","avatar_url":"https://github.com/qjerome.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/qjerome/flaglet/rust.yml?style=for-the-badge)](https://github.com/qjerome/flaglet/actions/workflows/rust.yml)\n[![Crates.io Version](https://img.shields.io/crates/v/flaglet?style=for-the-badge)](https://crates.io/crates/flaglet)\n[![docs.rs](https://img.shields.io/docsrs/flaglet?style=for-the-badge\u0026logo=docs.rs\u0026color=blue)](https://docs.rs/flaglet)\n[![License](https://img.shields.io/crates/l/flaglet?style=for-the-badge\u0026color=purple)](https://crates.io/crates/flaglet)\n\n\u003c!-- cargo-rdme start --\u003e\n\n# flaglet 🏳️\n\nA proc-macro attribute that turns a plain Rust enum into a fully-featured\ntyped bitflag API — without a DSL, without a wrapper type you don't own,\nand with first-class support for any derivable trait.\n\n## Why flaglet?\n\nflaglet exists to stay out of your way — one attribute, and the rest is\nplain Rust.\n\n- **Plain enum syntax.** Just annotate a regular enum — no custom DSL, no\n  derive macro, no boilerplate. Your variants stay ordinary Rust.\n- **A named companion type you own.** `#[flags]` on `Permissions` enum generates\n  `PermissionsFlags`: a concrete struct in your crate that you can `impl`.\n- **Derive forwarding.** Any `#[derive(...)]` placed on the enum is\n  automatically forwarded to both the enum and the generated flags struct.\n- **Auto-sized backing integer.** The macro picks the smallest integer type\n  that fits your variants; override it with `#[flags(u64)]` when needed.\n- **`no_std` out of the box.** No allocator required.\n\n## Installation\n\n```toml\n[dependencies]\nflaglet = \"0.1\"\n```\n\n## Quickstart\n\n```rust\nuse flaglet::flags;\n\n#[flags]\npub enum Permissions {\n    Read    = 1 \u003c\u003c 0,\n    Write   = 1 \u003c\u003c 1,\n    Execute = 1 \u003c\u003c 2,\n}\n\nlet mut perms = Permissions::flags(); // entry point on the enum itself\nperms |= Permissions::Read;\nperms |= Permissions::Write;\n\nassert!(perms.contains(Permissions::Read));\nassert!(!perms.contains(Permissions::Execute));\n```\n\n## Usage\n\n### The `#[flags]` attribute\n\nApply `#[flags]` to any enum whose variants are unit variants with explicit\npower-of-2 discriminants. The macro generates a companion struct named\n`{EnumName}Flags` in the same scope.\n\n```rust\nuse flaglet::flags;\nuse core::mem::size_of;\n\n#[flags]\npub enum Permission {\n    Read    = 0b001,\n    Write   = 0b010,\n    Execute = 0b100,\n}\n// Generates: pub struct PermissionFlags(u8);\nassert!(PermissionFlags::empty().is_empty());\nassert_eq!(size_of::\u003cPermission\u003e(), size_of::\u003cu8\u003e());\nassert_eq!(size_of::\u003cPermissionFlags\u003e(), size_of::\u003cu8\u003e());\n```\n\nThe backing integer type is chosen automatically from the largest\ndiscriminant value (`u8`, `u16`, `u32`, or `u64`). You can override it\nexplicitly:\n\n```rust\nuse flaglet::flags;\nuse core::mem::size_of;\n\n#[flags(u32)]\npub enum Permission {\n    Read    = 0b001,\n    Write   = 0b010,\n    Execute = 0b100,\n}\n// Generates: pub struct PermissionFlags(u32);\nassert!(PermissionFlags::empty().is_empty());\nassert_eq!(size_of::\u003cPermission\u003e(), size_of::\u003cu32\u003e());\nassert_eq!(size_of::\u003cPermissionFlags\u003e(), size_of::\u003cu32\u003e());\n```\n\n### Constructing a flags value\n\n```rust\nuse flaglet::flags;\n\n#[flags]\nenum Permissions {\n    Read    = 1 \u003c\u003c 0,\n    Write   = 1 \u003c\u003c 1,\n    Execute = 1 \u003c\u003c 2,\n}\n\nlet _f = Permissions::flags();                           // empty\nlet _f = Permissions::all();                             // all variants set\nlet _f = Permissions::Read | Permissions::Write;         // combine variants\nlet _f = PermissionsFlags::from_flag(Permissions::Read); // single variant\n\n// Const context — use union() instead of |\nconst RW: PermissionsFlags = PermissionsFlags::empty()\n    .union(Permissions::Read)\n    .union(Permissions::Write);\n\n// From a raw integer (e.g. deserialization, FFI) — unknown bits are masked out\nlet _f = PermissionsFlags::from_bits(0b011_u8);\n```\n\n### Setting and testing flags\n\n`set`, `unset`, `contains`, and `contains_any` accept both a single variant\nand a combined flags value:\n\n```rust\nlet mut f = PermissionsFlags::empty();\nf.set(Permissions::Read);                        // set one flag\nf.set(Permissions::Read | Permissions::Write);   // set multiple flags\nf.unset(Permissions::Write);                     // clear one or more flags\nlet _ = f.is_empty();                            // true if no flags are set\nlet _ = f.bits();                                // raw integer value\n\n// contains: true if ALL specified bits are set\nlet _ = f.contains(Permissions::Read);\nlet _ = f.contains(Permissions::Read | Permissions::Write);\n\n// contains_any: true if AT LEAST ONE specified bit is set\nlet _ = f.contains_any(Permissions::Read | Permissions::Write);\n\n// is_disjoint: true if NO specified bits are set (opposite of contains_any)\nlet _ = f.is_disjoint(Permissions::Execute);\n```\n\n### Bitwise operators\n\nAll standard bitwise operators are implemented between the enum and the\nflags struct, in both mutating and non-mutating forms:\n\n```rust\n// Enum OP Enum → PermissionsFlags (chains freely)\nlet mut f = Permissions::Read | Permissions::Write | Permissions::Execute;\n\n// Mutating\nf |= Permissions::Read;    // set\nf \u0026= Permissions::Write;   // mask\nf ^= Permissions::Execute; // toggle\n\n// Non-mutating (returns a new PermissionsFlags)\nlet g = f | Permissions::Read;\nlet h = f \u0026 Permissions::Write;\nlet i = f ^ Permissions::Execute;\nlet j = !f; // invert all bits\n\n// Flags OP Flags\nlet _k = f | g;\nlet _l = f \u0026 g;\n```\n\n### Equality\n\nCross-type `PartialEq` is implemented in both directions:\n\n```rust\nlet mut f = PermissionsFlags::empty();\nf.set(Permissions::Read);\nassert!(f == Permissions::Read);   // PermissionsFlags == Permissions\nassert!(Permissions::Read == f);   // Permissions == PermissionsFlags\n```\n\nEquality holds only when the flags value has exactly one bit set matching\nthe variant.\n\n### Derive forwarding\n\nAny `#[derive(...)]` on the enum is forwarded to **both** the enum and the\ngenerated flags struct. This includes third-party derives like\n`serde::Serialize` or `rkyv::Archive` — no stubs, no wrappers:\n\n```rust\n#[flags]\n#[derive(Debug, Serialize, Deserialize, Archive)]\npub enum Permissions {\n    Read    = 1 \u003c\u003c 0,\n    Write   = 1 \u003c\u003c 1,\n    Execute = 1 \u003c\u003c 2,\n}\n// Both Permissions and PermissionsFlags implement Debug, Serialize,\n// Deserialize, and Archive.\n```\n\n### Extending the generated type\n\nBecause `PermissionsFlags` is a real struct generated in your crate's\nscope, you can add your own methods to it directly:\n\n```rust\nimpl PermissionsFlags {\n    pub fn read_write() -\u003e Self {\n        Permissions::flags() | Permissions::Read | Permissions::Write\n    }\n\n    pub fn is_read_only(\u0026self) -\u003e bool {\n        self.contains(Permissions::Read) \u0026\u0026 !self.contains(Permissions::Write)\n    }\n}\n```\n\n### Visibility\n\nThe visibility of the enum is forwarded to both the enum and the generated\nflags struct:\n\n```rust\nuse flaglet::flags;\n\n#[flags] pub enum Foo { A = 1 }        // pub enum Foo + pub struct FooFlags\n#[flags] pub(crate) enum Bar { A = 1 } // pub(crate) for both\n#[flags] enum Baz { A = 1 }            // private for both\n```\n\n### `no_std`\n\n`flaglet` is fully `no_std` compatible. All generated code uses `core::ops`\nexclusively. No feature flag required.\n\n## Compile-time validation\n\nThe macro rejects invalid inputs with precise error messages pointing at\nthe offending token:\n\n- Variants with fields (tuple or struct variants) are rejected.\n- Variants without an explicit discriminant are rejected.\n- Discriminants that are zero or not a power of 2 are rejected.\n- Discriminants that exceed `u64::MAX` are rejected.\n- Discriminant expressions must be integer literals or `1 \u003c\u003c N` form.\n\n## License\n\nLicensed under either of [GPL-3.0-or-later](https://choosealicense.com/licenses/gpl-3.0/)\nor [BSD-2-Clause](https://choosealicense.com/licenses/bsd-2-clause/) at your option.\n\n\u003c!-- cargo-rdme end --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqjerome%2Fflaglet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqjerome%2Fflaglet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqjerome%2Fflaglet/lists"}