Skip to content

refactor(rust): modularize tc_helper, add typed errors, surface task …#649

Open
BrawlerXull wants to merge 1 commit into
CCExtractor:mainfrom
BrawlerXull:feat/rust-ffi-overhaul
Open

refactor(rust): modularize tc_helper, add typed errors, surface task …#649
BrawlerXull wants to merge 1 commit into
CCExtractor:mainfrom
BrawlerXull:feat/rust-ffi-overhaul

Conversation

@BrawlerXull

@BrawlerXull BrawlerXull commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

…attributes

Overhauls the Rust FFI bridge (Deliverable 2):

Build config

  • Pin Cargo.toml to edition 2021 for broader Rust/flutter_rust_bridge compatibility; add the thiserror dependency.
  • Add rust/build.rs: rerun-on-source-change tracking, target-platform detection (exposed via the TC_HELPER_PLATFORM compile-time env), and an opt-in (FRB_CODEGEN=1) binding-regeneration trigger.

Modularization & typed errors

  • Extract the duplicated storage-open boilerplate into storage.rs, the task serializer into serialize.rs, and a thiserror-based TcHelperError into utils/error.rs. api.rs is now a thin FFI layer of delegators.
  • Remove every .unwrap() panic; each FFI entry point returns Result<_, String> so flutter_rust_bridge surfaces failures to Dart as catchable exceptions with a real message, instead of relying on panics.

Surfaced task attributes

  • The serializer now emits annotations, dependencies (depends), is_blocked, is_blocking, and recur, which the Dart TaskForReplica model previously anticipated but never received. TaskForReplica gains those fields + parsing (reusing the existing Annotation model).
  • urgency is intentionally NOT surfaced: TaskChampion 2.0.3 does not compute or store an urgency value on Task, so there is nothing authoritative to expose.

Regenerated the flutter_rust_bridge bindings for the new signatures (getAllTasksJson stays Future; the mutating calls are now Future, throwing on error — all existing Dart callers ignored the old int return).

Deferred (needs mentor coordination + CI cross-compilation, not doable here): purging the tracked android jniLibs *.so binaries. There is currently no Android cargo/cargo-ndk integration, so Gradle bundles the committed .so directly; removing them before automated cross-compilation exists would break Android builds.

cargo build + cargo test: pass. flutter analyze: 0 errors. Full test suite unchanged at +317 -20 (pre-existing headless failures only). Runtime FFI behaviour still needs validation via an on-device build.

Description

Please include a summary of the change and which issue is fixed. List any dependencies that are required for this change.

Fixes #(issue_no)

Replace issue_no with the issue number which is fixed in this PR

Screenshots

Checklist

  • Tests have been added or updated to cover the changes
  • Documentation has been updated to reflect the changes
  • Code follows the established coding style guidelines
  • All tests are passing

Summary by CodeRabbit

  • New Features

    • Task details now show additional metadata: blocking/blocking state, dependencies, recurrence, and annotations (with richer surfaced values after loading replica-backed tasks).
    • Task list/details more reliably reflect the full task data following sync and update operations.
  • Bug Fixes

    • Fixed replica-backed task fields incorrectly defaulting to empty values when details are loaded.
    • Improved add/edit/delete and tag handling for invalid or partially specified task data, reducing unexpected failures.

…attributes

Overhauls the Rust FFI bridge (Deliverable 2):

Build config
- Pin Cargo.toml to edition 2021 for broader Rust/flutter_rust_bridge
  compatibility; add the thiserror dependency.
- Add rust/build.rs: rerun-on-source-change tracking, target-platform
  detection (exposed via the TC_HELPER_PLATFORM compile-time env), and an
  opt-in (FRB_CODEGEN=1) binding-regeneration trigger.

Modularization & typed errors
- Extract the duplicated storage-open boilerplate into storage.rs, the task
  serializer into serialize.rs, and a thiserror-based TcHelperError into
  utils/error.rs. api.rs is now a thin FFI layer of delegators.
- Remove every .unwrap() panic; each FFI entry point returns Result<_, String>
  so flutter_rust_bridge surfaces failures to Dart as catchable exceptions with
  a real message, instead of relying on panics.

Surfaced task attributes
- The serializer now emits annotations, dependencies (depends), is_blocked,
  is_blocking, and recur, which the Dart TaskForReplica model previously
  anticipated but never received. TaskForReplica gains those fields + parsing
  (reusing the existing Annotation model).
- urgency is intentionally NOT surfaced: TaskChampion 2.0.3 does not compute or
  store an urgency value on Task, so there is nothing authoritative to expose.

Regenerated the flutter_rust_bridge bindings for the new signatures
(getAllTasksJson stays Future<String>; the mutating calls are now Future<void>,
throwing on error — all existing Dart callers ignored the old int return).

Deferred (needs mentor coordination + CI cross-compilation, not doable here):
purging the tracked android jniLibs *.so binaries. There is currently no
Android cargo/cargo-ndk integration, so Gradle bundles the committed .so
directly; removing them before automated cross-compilation exists would break
Android builds.

cargo build + cargo test: pass. flutter analyze: 0 errors. Full test suite
unchanged at +317 -20 (pre-existing headless failures only). Runtime FFI
behaviour still needs validation via an on-device build.
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds typed Rust error handling and replica/task serialization helpers, rewrites task API calls to return Result, and updates the Dart bridge plus TaskForReplica to carry new replica-backed fields.

Changes

Rust API and Dart bridge taskflow updates

Layer / File(s) Summary
Error, storage, build, and module wiring
rust/src/utils/error.rs, rust/src/utils/mod.rs, rust/src/storage.rs, rust/src/lib.rs, rust/Cargo.toml, rust/build.rs
Adds TcHelperError, open_replica, new module exports, the thiserror dependency, edition 2021, and build script support for platform detection and optional code generation.
Task JSON serialization
rust/src/serialize.rs
Adds task_to_json, deriving annotations, depends, tags, is_blocked, and is_blocking into the JSON shape returned to Flutter.
Rust task API rewrites
rust/src/api.rs
Changes task entry points to Result return types, uses the new replica and serializer helpers, tightens UUID and tag parsing, and updates task/sync handling and tests.
Generated Rust FFI updates
rust/src/frb_generated.rs
Updates generated wire handlers to propagate string errors and removes AnyhowException/i8 SSE codec implementations.
Generated Dart bridge updates
lib/rust_bridge/api.dart, lib/rust_bridge/frb_generated.dart, lib/rust_bridge/frb_generated.io.dart, lib/rust_bridge/frb_generated.web.dart
Updates Dart bridge wrappers to Future<void> for task mutations and removes the corresponding error and integer codec helpers.
TaskForReplica fields and controller wiring
lib/app/v3/champion/models/task_for_replica.dart, lib/app/modules/taskc_details/controllers/taskc_details_controller.dart
Adds replica-backed task fields and JSON handling to TaskForReplica, and initializes those fields in the task details controller.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FlutterApp as Flutter app
  participant DartBridge as Dart bridge
  participant RustApi as Rust api.rs
  participant Storage as open_replica
  participant Serializer as task_to_json

  FlutterApp->>DartBridge: getAllTasksJson(taskdbDirPath)
  DartBridge->>RustApi: crateApiGetAllTasksJson(...)
  RustApi->>Storage: open_replica(taskdb_dir_path)
  Storage-->>RustApi: Replica or TcHelperError
  RustApi->>Serializer: task_to_json(task)
  Serializer-->>RustApi: JSON Value
  RustApi-->>DartBridge: Result<String, String>
  DartBridge-->>FlutterApp: JSON payload

  FlutterApp->>DartBridge: updateTask(uuidSt, taskdbDirPath, map)
  DartBridge->>RustApi: crateApiUpdateTask(...)
  RustApi->>Storage: open_replica(taskdb_dir_path)
  RustApi->>RustApi: parse UUID, apply tag/status updates, commit
  RustApi-->>DartBridge: Result<(), String>
  DartBridge-->>FlutterApp: void completion
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main Rust bridge refactor and typed-error/task-surface changes, though it is truncated at the end.
Description check ✅ Passed The description is detailed and relevant, but it omits the required Fixes issue number section and does not follow the template fully.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
lib/app/modules/taskc_details/controllers/taskc_details_controller.dart (1)

263-324: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

saveTask drops depends, recur, and annotations when saving a replica task.

The controller initializes these fields from the replica model (lines 85-88) and they are user-editable, but the modifiedTask constructed in saveTask (lines 267-324) does not pass them to the TaskForReplica constructor. User edits to these fields are silently lost on save.

🐛 Proposed fix
       priority: priority.string.isNotEmpty ? priority.string : null,
       project: project.string != 'None' ? project.string : null,
+      depends: depends.isNotEmpty ? depends.toList() : null,
+      recur: recur.string.isNotEmpty ? recur.string : null,
+      annotations: annotations.isNotEmpty ? annotations.toList() : null,
     );
🤖 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 `@lib/app/modules/taskc_details/controllers/taskc_details_controller.dart`
around lines 263 - 324, The saveTask path for TaskForReplica is omitting
user-editable fields, so edits to depends, recur, and annotations are lost when
building modifiedTask. Update the TaskForReplica construction in saveTask to
carry through the current values for those properties, using the same controller
fields that are initialized earlier in taskc_details_controller.dart, alongside
the existing status, description, tags, and date fields.
🧹 Nitpick comments (1)
lib/app/v3/champion/models/task_for_replica.dart (1)

182-183: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

hashCode doesn't include the new fields checked by operator ==.

While the hashCode/equals contract is technically maintained (equal objects will still produce equal hashes), omitting isBlocked, isBlocking, depends, and recur from the hash causes excessive collisions in hash-based collections like Set and Map keys.

♻️ Proposed fix
-  int get hashCode => Object.hash(modified, due, status, description, uuid,
-      priority, tags == null ? 0 : tags.hashCode, start, wait);
+  int get hashCode => Object.hash(
+      modified, due, status, description, uuid, priority,
+      tags == null ? 0 : tags.hashCode, start, wait,
+      isBlocked, isBlocking,
+      depends == null ? 0 : depends.hashCode,
+      recur,
+      annotations == null ? 0 : annotations.hashCode);
🤖 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 `@lib/app/v3/champion/models/task_for_replica.dart` around lines 182 - 183, The
TaskForReplica hashCode implementation is missing the new fields that operator
== compares, which can cause avoidable hash collisions in Sets and Maps. Update
TaskForReplica.hashCode to include isBlocked, isBlocking, depends, and recur
alongside the existing properties so it stays aligned with operator ==. Keep the
change localized to the hashCode getter in TaskForReplica and preserve the
current hashing pattern for the other fields.
🤖 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.

Inline comments:
In `@lib/app/v3/champion/models/task_for_replica.dart`:
- Around line 174-178: `TaskForReplica.operator ==` is missing the `annotations`
field, so equality can incorrectly treat two tasks as the same. Update the
equality check in `TaskForReplica` to include `annotations` alongside the
existing fields (`priority`, `isBlocked`, `isBlocking`, `depends`, `recur`), and
make sure the corresponding `hashCode` logic stays consistent with that change.

---

Outside diff comments:
In `@lib/app/modules/taskc_details/controllers/taskc_details_controller.dart`:
- Around line 263-324: The saveTask path for TaskForReplica is omitting
user-editable fields, so edits to depends, recur, and annotations are lost when
building modifiedTask. Update the TaskForReplica construction in saveTask to
carry through the current values for those properties, using the same controller
fields that are initialized earlier in taskc_details_controller.dart, alongside
the existing status, description, tags, and date fields.

---

Nitpick comments:
In `@lib/app/v3/champion/models/task_for_replica.dart`:
- Around line 182-183: The TaskForReplica hashCode implementation is missing the
new fields that operator == compares, which can cause avoidable hash collisions
in Sets and Maps. Update TaskForReplica.hashCode to include isBlocked,
isBlocking, depends, and recur alongside the existing properties so it stays
aligned with operator ==. Keep the change localized to the hashCode getter in
TaskForReplica and preserve the current hashing pattern for the other fields.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ccbe89b7-140e-4862-8aa3-579e110db3fd

📥 Commits

Reviewing files that changed from the base of the PR and between f058b4a and 09040e1.

⛔ Files ignored due to path filters (1)
  • rust/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • lib/app/modules/taskc_details/controllers/taskc_details_controller.dart
  • lib/app/v3/champion/models/task_for_replica.dart
  • lib/rust_bridge/api.dart
  • lib/rust_bridge/frb_generated.dart
  • lib/rust_bridge/frb_generated.io.dart
  • lib/rust_bridge/frb_generated.web.dart
  • rust/Cargo.toml
  • rust/build.rs
  • rust/src/api.rs
  • rust/src/frb_generated.rs
  • rust/src/lib.rs
  • rust/src/serialize.rs
  • rust/src/storage.rs
  • rust/src/utils/error.rs
  • rust/src/utils/mod.rs
💤 Files with no reviewable changes (1)
  • lib/rust_bridge/frb_generated.io.dart

Comment on lines +174 to +178
other.priority == priority &&
other.isBlocked == isBlocked &&
other.isBlocking == isBlocking &&
_listEquals(other.depends, depends) &&
other.recur == recur;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

annotations is missing from operator ==.

Two TaskForReplica objects with different annotations will be considered equal, which can cause incorrect behavior in collections and diffing logic.

🐛 Proposed fix
         _listEquals(other.depends, depends) &&
-        other.recur == recur;
+        other.recur == recur &&
+        _listEquals(other.annotations, annotations);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
other.priority == priority &&
other.isBlocked == isBlocked &&
other.isBlocking == isBlocking &&
_listEquals(other.depends, depends) &&
other.recur == recur;
other.priority == priority &&
other.isBlocked == isBlocked &&
other.isBlocking == isBlocking &&
_listEquals(other.depends, depends) &&
other.recur == recur &&
_listEquals(other.annotations, annotations);
🤖 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 `@lib/app/v3/champion/models/task_for_replica.dart` around lines 174 - 178,
`TaskForReplica.operator ==` is missing the `annotations` field, so equality can
incorrectly treat two tasks as the same. Update the equality check in
`TaskForReplica` to include `annotations` alongside the existing fields
(`priority`, `isBlocked`, `isBlocking`, `depends`, `recur`), and make sure the
corresponding `hashCode` logic stays consistent with that change.

@MabudAlam

Copy link
Copy Markdown
Member

@CodeRabbit full review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
lib/app/v3/champion/models/task_for_replica.dart (1)

174-178: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

annotations is still missing from operator ==.

This was flagged in a previous review and remains unaddressed. Two TaskForReplica objects with different annotations will compare as equal, which can cause incorrect behavior in collections and diffing logic.

🐛 Proposed fix
         _listEquals(other.depends, depends) &&
-        other.recur == recur;
+        other.recur == recur &&
+        _listEquals(other.annotations, annotations);
🤖 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 `@lib/app/v3/champion/models/task_for_replica.dart` around lines 174 - 178, The
TaskForReplica equality check in operator == is still missing annotations, so
objects with different annotations can compare equal. Update the equality
comparison in TaskForReplica’s operator == to include annotations alongside the
other compared fields, and make sure the corresponding hashCode logic in the
same class stays consistent with that field.
🤖 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.

Duplicate comments:
In `@lib/app/v3/champion/models/task_for_replica.dart`:
- Around line 174-178: The TaskForReplica equality check in operator == is still
missing annotations, so objects with different annotations can compare equal.
Update the equality comparison in TaskForReplica’s operator == to include
annotations alongside the other compared fields, and make sure the corresponding
hashCode logic in the same class stays consistent with that field.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3c6d86ff-560a-4fdc-8bf9-3957ebe796f2

📥 Commits

Reviewing files that changed from the base of the PR and between f058b4a and 09040e1.

⛔ Files ignored due to path filters (1)
  • rust/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • lib/app/modules/taskc_details/controllers/taskc_details_controller.dart
  • lib/app/v3/champion/models/task_for_replica.dart
  • lib/rust_bridge/api.dart
  • lib/rust_bridge/frb_generated.dart
  • lib/rust_bridge/frb_generated.io.dart
  • lib/rust_bridge/frb_generated.web.dart
  • rust/Cargo.toml
  • rust/build.rs
  • rust/src/api.rs
  • rust/src/frb_generated.rs
  • rust/src/lib.rs
  • rust/src/serialize.rs
  • rust/src/storage.rs
  • rust/src/utils/error.rs
  • rust/src/utils/mod.rs
💤 Files with no reviewable changes (1)
  • lib/rust_bridge/frb_generated.io.dart

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.

2 participants