Skip to content

Use bitflags crate for ergonomics#77

Open
The-Nice-One wants to merge 1 commit into
mainfrom
feature/bitflags-support
Open

Use bitflags crate for ergonomics#77
The-Nice-One wants to merge 1 commit into
mainfrom
feature/bitflags-support

Conversation

@The-Nice-One
Copy link
Copy Markdown
Member

@The-Nice-One The-Nice-One commented May 7, 2026

Summary by CodeRabbit

  • New Features

    • Added bitflag-based configuration types for table structures (pixmap, character, color, and font tables).
    • Introduced new public bitflag types for managing table modifiers, configuration, and link settings.
  • Breaking Changes

    • Replaced boolean fields with bitflag fields in core table structs.
    • Added #[non_exhaustive] markers to public types, restricting exhaustive pattern matching.
  • Chores

    • Updated dependencies: pinned serde to version 1 with explicit feature support and added bitflags 2.11.1.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

📝 Walkthrough

Walkthrough

This PR refactors table configuration from individual boolean fields to typed bitflags. Cargo.toml adds the bitflags dependency and a corresponding Serde feature. Core table structs in src/core/mod.rs replace booleans with PixmapTableConfigurationFlags, CharacterTableModifierFlags, and related types, while adding #[non_exhaustive] annotations. Deserialization and serialization code across character, color, font, and pixmap tables are updated to use .from_bits_retain() and .contains() / .bits() instead of individual bit checks and field writes.

Changes

Dependency & Core Structure Refactor

Layer / File(s) Summary
Dependency Addition
Cargo.toml
bitflags v2.11.1 added to dependencies; serde pinned to version "1"; new serde feature defined to enable bitflags/serde.
Flag Type Definitions
src/core/mod.rs (lines 28–95)
Eight new public bitflags types introduced: PixmapTableConfigurationFlags, PixmapTableLinkFlags, CharacterTableModifierFlags, CharacterTableConfigurationFlags, CharacterTableLinkFlags, ColorTableModifierFlags, ColorTableConfigurationFlags, FontTableLinkFlags. All derive Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash and conditional Serde support.
Struct Field Consolidation
src/core/mod.rs (lines 112–230)
PixmapTable adds configuration_flags and link_flags fields; CharacterTable replaces three boolean "use_*" toggles with modifier_flags, configuration_flags, and link_flags; ColorTable replaces use_color_type boolean with modifier_flags and configuration_flags; FontTable adds link_flags. Multiple structs/enums receive #[non_exhaustive] marker.
Character Table Flag Integration
src/core/tables/character/mod.rs (lines 17–216)
Import statements updated; conditional field reads in deserialize and serialize now gate on modifier_flags.contains(...) checks instead of boolean field presence.
Character Deserialization
src/core/tables/character/deserialize.rs (lines 19–131)
next_modifer_flags, next_configurations, and next_table_links now parse raw bytes into typed CharacterTable*Flags via from_bits_retain(...) and derive boolean decisions via contains(...) for tagging.
Character Serialization
src/core/tables/character/serialize.rs (lines 43–144)
push_modifier_flags, push_configurations, and push_table_links emit flag bytes directly from self.*_flags.bits() and compute tagging booleans using contains(...).
Color Table Flag Integration
src/core/tables/color/mod.rs (lines 17–400)
Imports updated; deserialization now parses modifier and configuration bytes into bitflags; per-color conditional logic and serialization updated to use modifier_flags.contains(UseColorType) and configuration_flags.contains(ConstantAlpha) instead of direct boolean fields.
Font Table Flag Integration
src/core/tables/font/deserialize.rs (lines 19–65)
next_table_links parses link byte into FontTableLinkFlags and derives link_character_tables from contains(...).
Font Table Serialization
src/core/tables/font/serialize.rs (lines 68–82)
push_table_links emits link flags from self.link_flags.bits() and updates tag value to use contains(FontTableLinkFlags::LinkCharacterTables).
Pixmap Table Flag Integration
src/core/tables/pixmap/deserialize.rs (lines 18–149)
Imports grouped; configuration and link bytes are decoded into PixmapTableConfigurationFlags and PixmapTableLinkFlags via from_bits_retain, with boolean decisions derived via contains(...).
Pixmap Table Serialization
src/core/tables/pixmap/serialize.rs (lines 23–157)
Configuration and link flag bytes emitted directly from self.*_flags.bits(); tagging logic updated to use contains(...) for bitflag membership instead of Option checks.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 ✨
Boolean bits have had their day,
Flags now organize the way!
Pixmaps, characters, colors too,
Dancing in configurations new.
Bitwise clarity takes the stage! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Use bitflags crate for ergonomics' clearly and directly describes the main change: introducing the bitflags crate dependency and refactoring flag handling across the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/bitflags-support

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/core/tables/character/mod.rs (1)

180-214: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard against panics when modifier flags and record payload diverge.

On Line 184, Line 199, and Line 214, unwrap() can panic if a flag is set but a Character record lacks the corresponding value. Prefer returning a serialization error after validating each flagged field is present.

Proposed fix
+        for character in &self.characters {
+            if self.modifier_flags.contains(CharacterTableModifierFlags::UseAdvanceX)
+                && character.advance_x.is_none()
+            {
+                return Err(SerializeError::InvalidTableState);
+            }
+            if self.modifier_flags.contains(CharacterTableModifierFlags::UsePixmapIndex)
+                && character.pixmap_index.is_none()
+            {
+                return Err(SerializeError::InvalidTableState);
+            }
+            if self
+                .modifier_flags
+                .contains(CharacterTableModifierFlags::UsePixmapTableIndex)
+                && character.pixmap_table_index.is_none()
+            {
+                return Err(SerializeError::InvalidTableState);
+            }
+        }
+
         for (index, character) in self.characters.iter().enumerate() {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/tables/character/mod.rs` around lines 180 - 214, The code currently
calls unwrap() on character.advance_x, character.pixmap_index, and
character.pixmap_table_index when the corresponding modifier_flags
(CharacterTableModifierFlags::UseAdvanceX, ::UsePixmapIndex,
::UsePixmapTableIndex) are set, which can panic if the record lacks the value;
change each unwrap to validate the Option is Some and, on None, return a
serialization error (or map to the existing error type) instead of
panicking—locate the blocks that call
engine.bytes.push(character.advance_x.unwrap()) /
character.pixmap_index.unwrap() / character.pixmap_table_index.unwrap() and
replace with safe checks that produce a clear error including which
field/table/record failed.
src/core/tables/font/serialize.rs (1)

69-84: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate link_flags against character_table_indexes before serialization.

If LinkCharacterTables is set but character_table_indexes is None (or vice versa), the encoded link byte and payload structure disagree.

Proposed fix
+        if self
+            .link_flags
+            .contains(FontTableLinkFlags::LinkCharacterTables)
+            != self.character_table_indexes.is_some()
+        {
+            return Err(SerializeError::InvalidTableState);
+        }
+
         engine.bytes.push(self.link_flags.bits());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/tables/font/serialize.rs` around lines 69 - 84, The serializer
writes link_flags.bits() and then unconditionally serializes
character_table_indexes, which can produce inconsistent wire format when
FontTableLinkFlags::LinkCharacterTables is set but character_table_indexes is
None (or vice versa); update the serialization in serialize.rs (the function
that pushes engine.bytes and handles character_table_indexes) to validate that
self.link_flags.contains(FontTableLinkFlags::LinkCharacterTables) iff
self.character_table_indexes.is_some(), return or propagate an error (or panic
with a clear message) when the two are inconsistent, and only emit the
character_table_indexes payload when the flag is present so the encoded link
byte and payload structure always agree (refer to symbols: link_flags,
character_table_indexes, FontTableLinkFlags::LinkCharacterTables,
engine.bytes.push).
src/core/tables/character/serialize.rs (1)

77-96: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent invalid character-table encoding when flags and optional fields are out of sync.

On Line 77 and Line 131, flags are written from bitfields, but value/link payloads are still emitted from Option presence. Also on Line 85, tagging uses is_some() instead of the emitted flag. These can diverge and produce inconsistent output/tagging.

Proposed fix
+        if self
+            .configuration_flags
+            .contains(crate::core::CharacterTableConfigurationFlags::ConstantClusterCodePoints)
+            != self.constant_cluster_codepoints.is_some()
+        {
+            return Err(SerializeError::InvalidTableState);
+        }
+        if self
+            .link_flags
+            .contains(CharacterTableLinkFlags::LinkPixmapTables)
+            != self.pixmap_table_indexes.is_some()
+        {
+            return Err(SerializeError::InvalidTableState);
+        }
+
         engine.bytes.push(self.configuration_flags.bits()); // Configuration flags byte
@@
-                value: self.constant_cluster_codepoints.is_some(),
+                value: self
+                    .configuration_flags
+                    .contains(crate::core::CharacterTableConfigurationFlags::ConstantClusterCodePoints),

Also applies to: 131-146

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/tables/character/serialize.rs` around lines 77 - 96, The encoding
can become inconsistent because flags are written from
self.configuration_flags.bits() while payloads and tagging are gated by Option
presence; update serialize logic so payloads (e.g., writing
constant_cluster_codepoints) are only emitted when the corresponding flag bit in
self.configuration_flags is set (not just Option::is_some()), and update tagging
(TagKind::CharacterTableUseConstantClusterCodepoints) and any checks that
currently use self.constant_cluster_codepoints.is_some() to instead derive
presence from the same flag bit you wrote via self.configuration_flags.bits();
ensure you also use that flag when computing configuration_values_start and when
emitting any other optional fields referenced later (the code paths around
engine.bytes.push(self.configuration_flags.bits()), the
constant_cluster_codepoints emission, and the tagging calls).
src/core/tables/pixmap/serialize.rs (1)

54-119: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Enforce flag-to-payload consistency before writing pixmap table bytes.

configuration_flags.bits() and link_flags.bits() are now authoritative, but payload emission still depends on Option fields. If these diverge, the output stream becomes structurally invalid for deserialization.

Proposed fix
+        if self
+            .configuration_flags
+            .contains(PixmapTableConfigurationFlags::ConstantWidth)
+            != self.constant_width.is_some()
+        {
+            return Err(SerializeError::InvalidTableState);
+        }
+        if self
+            .configuration_flags
+            .contains(PixmapTableConfigurationFlags::ConstantHeight)
+            != self.constant_height.is_some()
+        {
+            return Err(SerializeError::InvalidTableState);
+        }
+        if self
+            .configuration_flags
+            .contains(PixmapTableConfigurationFlags::ConstantBitsPerPixel)
+            != self.constant_bits_per_pixel.is_some()
+        {
+            return Err(SerializeError::InvalidTableState);
+        }
+
         engine.bytes.push(self.configuration_flags.bits());
@@
+        if self
+            .link_flags
+            .contains(PixmapTableLinkFlags::LinkColorTables)
+            != self.color_table_indexes.is_some()
+        {
+            return Err(SerializeError::InvalidTableState);
+        }
+
         engine.bytes.push(self.link_flags.bits());

Also applies to: 145-210

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/tables/pixmap/serialize.rs` around lines 54 - 119, Before emitting
payload bytes, validate that PixmapTableConfigurationFlags (configuration_flags)
match the presence of the corresponding Option fields and fail early if they
diverge: ensure that if PixmapTableConfigurationFlags::ConstantWidth is set then
self.constant_width is Some (and vice versa), same for ConstantHeight and
ConstantBitsPerPixel; if a mismatch occurs return an error (or propagate a
Serialize error) instead of writing bytes. Update the emit logic around
engine.bytes.push (and engine.tags.tag_byte) to only write bytes when the flag
is set (not merely when the Option is Some), and apply the same validation for
link_flags/payload pairs in the other region (lines ~145-210) so flags are
authoritative and serialization cannot produce structurally invalid output.
src/core/tables/color/mod.rs (1)

286-303: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Enforce flag/value invariants before serializing to avoid malformed bytes and panics.

Line 399 writes config flags from self.configuration_flags, but Line 407/416 behavior is driven by self.constant_alpha. If those diverge, the output layout shifts. Also, Line 290 and Line 303 can panic via unwrap() when required per-record fields are missing.

💡 Suggested fix
 pub(crate) fn push_configurations<T: TagWriter>(&self, engine: &mut SerializeEngine<T>) {
@@
-    engine.bytes.push(self.configuration_flags.bits()); // configuration flags
+    let use_constant_alpha = self
+        .configuration_flags
+        .contains(ColorTableConfigurationFlags::ConstantAlpha);
+    engine.bytes.push(self.configuration_flags.bits());

@@
-            vec![TagKind::ColorTableUseConstantAlpha {
+            vec![TagKind::ColorTableUseConstantAlpha {
                 table_index: engine.tagging_data.current_table_index,
-                value: self.constant_alpha.is_some(),
+                value: use_constant_alpha,
             }],
             engine.bytes.byte_index(),
         );

@@
-        if let Some(constant_alpha) = self.constant_alpha {
-            engine.bytes.push(constant_alpha);
+        if use_constant_alpha {
+            let constant_alpha = self
+                .constant_alpha
+                .ok_or(SerializeError::InvalidColorTableState)?;
+            engine.bytes.push(constant_alpha);
             #[cfg(feature = "tagging")]
             engine.tags.tag_byte(
                 TagKind::ColorTableConstantAlpha {
                     table_index: engine.tagging_data.current_table_index,
                     value: constant_alpha,
                 },
                 engine.bytes.byte_index(),
             );
+        } else if self.constant_alpha.is_some() {
+            return Err(SerializeError::InvalidColorTableState);
         }
 }
 for (index, color) in self.colors.iter().enumerate() {
@@
-    if self
-        .modifier_flags
-        .contains(ColorTableModifierFlags::UseColorType)
-    {
-        engine.bytes.push(color.color_type.unwrap() as u8);
+    if self
+        .modifier_flags
+        .contains(ColorTableModifierFlags::UseColorType)
+    {
+        let color_type = color
+            .color_type
+            .ok_or(SerializeError::InvalidColorTableState)?;
+        engine.bytes.push(color_type as u8);
@@
-    if self.constant_alpha.is_none() {
-        engine.bytes.push(color.custom_alpha.unwrap());
+    if !self
+        .configuration_flags
+        .contains(ColorTableConfigurationFlags::ConstantAlpha)
+    {
+        let custom_alpha = color
+            .custom_alpha
+            .ok_or(SerializeError::InvalidColorTableState)?;
+        engine.bytes.push(custom_alpha);

Also applies to: 399-417

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/tables/color/mod.rs` around lines 286 - 303, The serializer
currently unwraps per-record fields and assumes flag/value invariants (e.g.,
using ColorTableModifierFlags::UseColorType then color.color_type.unwrap(), and
checking self.constant_alpha vs. color.custom_alpha.unwrap()), which can panic
or produce malformed layouts; update the serialization in ColorTable (mod.rs) to
first validate invariants and handle missing/invalid cases: when
self.modifier_flags.contains(ColorTableModifierFlags::UseColorType) only read
and push color.color_type after checking color.color_type.is_some() (otherwise
return an error or skip with a clear log), and similarly ensure
self.constant_alpha.is_none() implies color.custom_alpha.is_some() before
unwrapping (otherwise return an explicit error), so no unwrap() is used and the
output layout remains consistent when writing bytes via engine.bytes.push and
tagging via engine.tags.tag_byte.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/core/tables/character/mod.rs`:
- Around line 180-214: The code currently calls unwrap() on character.advance_x,
character.pixmap_index, and character.pixmap_table_index when the corresponding
modifier_flags (CharacterTableModifierFlags::UseAdvanceX, ::UsePixmapIndex,
::UsePixmapTableIndex) are set, which can panic if the record lacks the value;
change each unwrap to validate the Option is Some and, on None, return a
serialization error (or map to the existing error type) instead of
panicking—locate the blocks that call
engine.bytes.push(character.advance_x.unwrap()) /
character.pixmap_index.unwrap() / character.pixmap_table_index.unwrap() and
replace with safe checks that produce a clear error including which
field/table/record failed.

In `@src/core/tables/character/serialize.rs`:
- Around line 77-96: The encoding can become inconsistent because flags are
written from self.configuration_flags.bits() while payloads and tagging are
gated by Option presence; update serialize logic so payloads (e.g., writing
constant_cluster_codepoints) are only emitted when the corresponding flag bit in
self.configuration_flags is set (not just Option::is_some()), and update tagging
(TagKind::CharacterTableUseConstantClusterCodepoints) and any checks that
currently use self.constant_cluster_codepoints.is_some() to instead derive
presence from the same flag bit you wrote via self.configuration_flags.bits();
ensure you also use that flag when computing configuration_values_start and when
emitting any other optional fields referenced later (the code paths around
engine.bytes.push(self.configuration_flags.bits()), the
constant_cluster_codepoints emission, and the tagging calls).

In `@src/core/tables/color/mod.rs`:
- Around line 286-303: The serializer currently unwraps per-record fields and
assumes flag/value invariants (e.g., using ColorTableModifierFlags::UseColorType
then color.color_type.unwrap(), and checking self.constant_alpha vs.
color.custom_alpha.unwrap()), which can panic or produce malformed layouts;
update the serialization in ColorTable (mod.rs) to first validate invariants and
handle missing/invalid cases: when
self.modifier_flags.contains(ColorTableModifierFlags::UseColorType) only read
and push color.color_type after checking color.color_type.is_some() (otherwise
return an error or skip with a clear log), and similarly ensure
self.constant_alpha.is_none() implies color.custom_alpha.is_some() before
unwrapping (otherwise return an explicit error), so no unwrap() is used and the
output layout remains consistent when writing bytes via engine.bytes.push and
tagging via engine.tags.tag_byte.

In `@src/core/tables/font/serialize.rs`:
- Around line 69-84: The serializer writes link_flags.bits() and then
unconditionally serializes character_table_indexes, which can produce
inconsistent wire format when FontTableLinkFlags::LinkCharacterTables is set but
character_table_indexes is None (or vice versa); update the serialization in
serialize.rs (the function that pushes engine.bytes and handles
character_table_indexes) to validate that
self.link_flags.contains(FontTableLinkFlags::LinkCharacterTables) iff
self.character_table_indexes.is_some(), return or propagate an error (or panic
with a clear message) when the two are inconsistent, and only emit the
character_table_indexes payload when the flag is present so the encoded link
byte and payload structure always agree (refer to symbols: link_flags,
character_table_indexes, FontTableLinkFlags::LinkCharacterTables,
engine.bytes.push).

In `@src/core/tables/pixmap/serialize.rs`:
- Around line 54-119: Before emitting payload bytes, validate that
PixmapTableConfigurationFlags (configuration_flags) match the presence of the
corresponding Option fields and fail early if they diverge: ensure that if
PixmapTableConfigurationFlags::ConstantWidth is set then self.constant_width is
Some (and vice versa), same for ConstantHeight and ConstantBitsPerPixel; if a
mismatch occurs return an error (or propagate a Serialize error) instead of
writing bytes. Update the emit logic around engine.bytes.push (and
engine.tags.tag_byte) to only write bytes when the flag is set (not merely when
the Option is Some), and apply the same validation for link_flags/payload pairs
in the other region (lines ~145-210) so flags are authoritative and
serialization cannot produce structurally invalid output.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b376ff1a-09a4-437b-a701-b42726aed5c8

📥 Commits

Reviewing files that changed from the base of the PR and between e8b6079 and c04dcd1.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • Cargo.toml
  • src/core/mod.rs
  • src/core/tables/character/deserialize.rs
  • src/core/tables/character/mod.rs
  • src/core/tables/character/serialize.rs
  • src/core/tables/color/mod.rs
  • src/core/tables/font/deserialize.rs
  • src/core/tables/font/serialize.rs
  • src/core/tables/pixmap/deserialize.rs
  • src/core/tables/pixmap/serialize.rs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant