Skip to content

feat: detect firmware vendor transitions within a BBL file#41

Merged
nerdCopter merged 6 commits into
masterfrom
feat/bbl-firmware-transition-detection
Jun 9, 2026
Merged

feat: detect firmware vendor transitions within a BBL file#41
nerdCopter merged 6 commits into
masterfrom
feat/bbl-firmware-transition-detection

Conversation

@nerdCopter

@nerdCopter nerdCopter commented Jun 7, 2026

Copy link
Copy Markdown
Owner

AI Generated pull-request

Summary

  • Detects when a single BBL file contains sessions from multiple firmware vendors (e.g. Betaflight sessions left on flash before reflashing to EmuFlight) and prints a WARNING with per-vendor session ranges and the hint to erase flash before reflashing
  • Corrects output filename prefixes for sessions whose firmware vendor does not match the BBL filename prefix — EMUF_BLACKBOX_LOG_...01.csv for a Betaflight session in an EMUF_* file becomes BTFL_BLACKBOX_LOG_...01.csv, preventing EMUF_BTFL_* or BTFL_BTFL_* duplicates

New public API (export module)

  • firmware_prefix_for_revision(revision: &str) -> Option<&'static str> — maps a firmware revision string to its canonical filename prefix (EMUF_, BTFL_, INAV_)
  • corrected_session_base_name(bbl_path: &Path, firmware_revision: &str) -> Option<String> — returns a corrected base name when the session vendor differs from the BBL filename prefix, or None when they match

All export functions (export_to_csv, export_to_gpx, export_to_event, compute_export_paths) gain an optional base_name_override: Option<&str> parameter. Existing callers pass None — no behaviour change.

Known firmware prefixes are defined in one const table (KNOWN_FIRMWARE_PREFIXES). Adding support for a new firmware (Quicksilver, Kiss, etc.) requires adding a single (&str, &str) entry.

Example output on EMUF_BLACKBOX_LOG_STELLARH7DEV_20260531_172007.BBL (mixed BF + EF flash)

Exported headers to: BTFL_BLACKBOX_LOG_STELLARH7DEV_20260531_172007.01.headers.csv
Exported flight data to: BTFL_BLACKBOX_LOG_STELLARH7DEV_20260531_172007.01.csv
...
Exported headers to: EMUF_BLACKBOX_LOG_STELLARH7DEV_20260531_172007.04.headers.csv
Exported flight data to: EMUF_BLACKBOX_LOG_STELLARH7DEV_20260531_172007.04.csv

WARNING: Firmware transition detected in EMUF_BLACKBOX_LOG_STELLARH7DEV_20260531_172007.BBL
  Sessions 001-003: Betaflight (Betaflight 2025.12.0-beta (a6f6daf05) STM32H743)
  Sessions 004-005: EmuFlight (EmuFlight 0.4.3 (4d3ad55) STELLARH7DEV)
  The flash was not erased before reflashing. Consider excluding earlier sessions.

Test plan

  • cargo test — all unit + integration tests pass (4 new tests for firmware_prefix_for_revision and corrected_session_base_name)
  • cargo clippy -- -D warnings — clean
  • Manual test against EMUF_BLACKBOX_LOG_STELLARH7DEV_20260531_172007.BBL — sessions 1–3 renamed to BTFL_*, sessions 4–5 kept as EMUF_*, warning printed

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Export files now use firmware-vendor-aware naming so generated filenames better reflect device vendor.
    • CLI detects and warns when multiple firmware vendors appear across sessions and summarizes per-session vendor/revision ranges.
  • Documentation

    • Examples and docs updated to match the revised export call signature.
  • Tests

    • Unit and integration tests updated/added to validate vendor-prefix detection, corrected export naming, and revised export call signatures.

When a BBL file contains sessions from multiple firmware vendors (e.g.
Betaflight sessions left on flash before reflashing to EmuFlight),
bbl_parser now detects the transition and:

- Prints a WARNING listing session ranges per vendor with the suggestion
  to erase flash before reflashing
- Corrects the output filename prefix for sessions whose firmware vendor
  does not match the BBL filename prefix (e.g. EMUF_* file + Betaflight
  session → output named BTFL_*), preventing EMUF_BTFL_* or BTFL_BTFL_*
  duplicate prefixes

New public API in export module:
- `firmware_prefix_for_revision(revision)` — maps a firmware revision
  string to its canonical filename prefix (EMUF_, BTFL_, INAV_)
- `corrected_session_base_name(bbl_path, firmware_revision)` — returns
  a corrected base name when the session vendor differs from the BBL
  filename prefix, or None when they match

All export functions (`export_to_csv`, `export_to_gpx`, `export_to_event`,
`compute_export_paths`) accept an optional `base_name_override` parameter.
Existing callers pass `None` to preserve prior behaviour.

Known firmware prefixes are defined in one place (`KNOWN_FIRMWARE_PREFIXES`)
and extended by adding a single `(&str, &str)` entry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0bf86144-ce15-4ff2-920f-5c8d2e157653

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds firmware vendor detection and session-aware base-name correction to export paths. A new base_name_override: Option<&str> parameter extends the export API; the CLI tracks firmware revisions per session and computes corrected base names when they differ from the BBL filename prefix, then passes these overrides through to all export functions. Firmware vendor transitions within a file trigger a post-processing warning.

Changes

Firmware-Vendor Export Path Correction

Layer / File(s) Summary
Firmware vendor detection and base-name correction core
src/export.rs
Static firmware prefix mapping and new functions: firmware_prefix_for_revision() and corrected_session_base_name() to detect vendor prefix from firmware revision and compute corrected session base names. Unit tests added for recognized, mismatched, unknown prefixes and fork exemption behavior.
Export API signature updates and path generation
src/export.rs
compute_export_paths() accepts base_name_override: Option<&str> and uses it when provided. export_to_csv(), export_to_gpx(), and export_to_event() signatures extended with base_name_override and forward it to path computation; internal test helper/GPX/event test calls updated.
Main CLI firmware tracking and base-name override usage
src/main.rs
Imports corrected_session_base_name and firmware_prefix_for_revision, accumulates per-session (log_number, firmware_revision), computes base_name_override for each session and passes it to CSV/GPX/Event exports, and emits a grouped firmware-vendor transition warning after processing a file.
Example call site updates
examples/csv_export.rs, examples/event_export.rs, examples/export_demo.rs, examples/gpx_export.rs, examples/multi_export.rs, examples/multi_flight_export.rs
All example export calls updated to pass the new base_name_override parameter as None to match the extended API.
Documentation example and integration test updates
src/lib.rs, tests/export_integration_tests.rs, src/conversion.rs
Rustdoc example updated to call export_to_csv with trailing None. Integration tests updated to pass the additional None arguments for GPX/Event export and compute_export_paths calls. parse_datetime_to_epoch had a minor local-seconds expression simplification.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • nerdCopter/bbl_parser#34: Both PRs modify the export API surface by changing src/export.rs function signatures (export_to_csv, export_to_gpx, export_to_event, compute_export_paths).
  • nerdCopter/bbl_parser#25: Related because both modify crate export APIs and require coordinated call-site signature updates (notably export_to_gpx and shared path/export helpers).
  • nerdCopter/bbl_parser#32: Both PRs touch the export integration tests; this PR updates those tests to the new optional argument signature.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary feature: detecting firmware vendor transitions within a BBL file, which is the core objective of the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/bbl-firmware-transition-detection

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 and usage tips.

Register Quicksilver and Kiss FC in the KNOWN_FIRMWARE_PREFIXES table
so their sessions are correctly identified and prefixed when detected
in mixed-firmware BBL files. Keywords are best-effort guesses based on
firmware names; update if actual revision strings differ.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@nerdCopter nerdCopter marked this pull request as ready for review June 7, 2026 15:26

@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.

Caution

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

⚠️ Outside diff range comments (1)
src/export.rs (1)

145-176: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate base_name_override before path construction (path traversal/write-outside-output-dir risk).

Line 157 and Line 246 trust base_name_override directly in filenames. A caller can pass absolute paths or ../ segments and escape the intended output directory.

🔧 Suggested hardening
+use std::path::Component;
+
+fn sanitize_base_name_override(base_name_override: Option<&str>) -> Option<&str> {
+    let raw = base_name_override?;
+    let p = Path::new(raw);
+    if p.is_absolute() {
+        return None;
+    }
+    if p.components().any(|c| {
+        matches!(
+            c,
+            Component::ParentDir | Component::RootDir | Component::Prefix(_)
+        )
+    }) {
+        return None;
+    }
+    p.file_name().and_then(|s| s.to_str())
+}
+
 pub fn compute_export_paths(
@@
-    let base_name = base_name_override.unwrap_or_else(|| extract_base_name(input_path));
+    let base_name = sanitize_base_name_override(base_name_override)
+        .unwrap_or_else(|| extract_base_name(input_path));
@@
 pub fn export_to_csv(
@@
-    let base_name = base_name_override.unwrap_or_else(|| extract_base_name(input_path));
+    let base_name = sanitize_base_name_override(base_name_override)
+        .unwrap_or_else(|| extract_base_name(input_path));

Also applies to: 240-271

🤖 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/export.rs` around lines 145 - 176, compute_export_paths currently trusts
base_name_override directly which allows absolute paths or path traversal;
validate and sanitize base_name_override before constructing filenames: when
base_name_override is Some, convert it to a Path and extract only the final
file-stem/file-name component (e.g. via
Path::new(base_name_override).file_name() or file_stem) and reject or fallback
if it contains separators, parent segments, or is absolute; ensure the sanitized
base name contains no path separators or leading dots and only safe characters
(or else use extract_base_name(input_path)); keep using
export_options.output_dir/output_dir for directory placement so callers cannot
escape the intended output directory.
🧹 Nitpick comments (2)
src/export.rs (1)

985-1005: ⚡ Quick win

Add coverage for the newly added QUIC/KISS prefix mappings.

KNOWN_FIRMWARE_PREFIXES includes Quicksilver/Kiss, but test_firmware_prefix_for_revision currently verifies only EMUF/BTFL/INAV plus unknown cases.

✅ Minimal test additions
     assert_eq!(firmware_prefix_for_revision("INAV 7.1.2"), Some("INAV_"));
+    assert_eq!(
+        firmware_prefix_for_revision("Quicksilver 0.1.0"),
+        Some("QUIC_")
+    );
+    assert_eq!(firmware_prefix_for_revision("Kiss Ultra"), Some("KISS_"));
     assert_eq!(firmware_prefix_for_revision(""), None);
     assert_eq!(firmware_prefix_for_revision("Unknown firmware"), None);
🤖 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/export.rs` around lines 985 - 1005, The test
test_firmware_prefix_for_revision is missing assertions for the newly added
QUIC/KISS mappings; update this test to include cases that exercise the QUIC and
KISS entries in KNOWN_FIRMWARE_PREFIXES by calling firmware_prefix_for_revision
with representative revision strings (e.g., "Quicksilver ..." and "KISS ..." or
uppercase variants) and asserting they return the expected prefixes (e.g.,
"QUIC_" and "KISS_"); ensure you add both typical and uppercase/lowercase
variations similar to the existing EMUF/BTFL checks so the mapping logic is
covered.
src/main.rs (1)

942-949: ⚡ Quick win

Avoid duplicating vendor mapping in CLI warning output.

This match duplicates prefix/vendor knowledge already maintained in src/export.rs. Future prefix additions can silently drift and print Unknown here.

🤖 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/main.rs` around lines 942 - 949, The inline match that sets vendor_name
duplicates the prefix->vendor mapping maintained elsewhere; replace this block
by calling the shared lookup in export.rs (e.g., a function like
vendor_for_prefix or get_vendor_by_prefix) instead of re-implementing the
mapping. Update the code that currently computes vendor_name (using prefix) to
call that shared function, and ensure the shared function is made public if
necessary so the CLI warning uses the single source of truth for prefix->vendor
mappings.
🤖 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/export.rs`:
- Around line 145-176: compute_export_paths currently trusts base_name_override
directly which allows absolute paths or path traversal; validate and sanitize
base_name_override before constructing filenames: when base_name_override is
Some, convert it to a Path and extract only the final file-stem/file-name
component (e.g. via Path::new(base_name_override).file_name() or file_stem) and
reject or fallback if it contains separators, parent segments, or is absolute;
ensure the sanitized base name contains no path separators or leading dots and
only safe characters (or else use extract_base_name(input_path)); keep using
export_options.output_dir/output_dir for directory placement so callers cannot
escape the intended output directory.

---

Nitpick comments:
In `@src/export.rs`:
- Around line 985-1005: The test test_firmware_prefix_for_revision is missing
assertions for the newly added QUIC/KISS mappings; update this test to include
cases that exercise the QUIC and KISS entries in KNOWN_FIRMWARE_PREFIXES by
calling firmware_prefix_for_revision with representative revision strings (e.g.,
"Quicksilver ..." and "KISS ..." or uppercase variants) and asserting they
return the expected prefixes (e.g., "QUIC_" and "KISS_"); ensure you add both
typical and uppercase/lowercase variations similar to the existing EMUF/BTFL
checks so the mapping logic is covered.

In `@src/main.rs`:
- Around line 942-949: The inline match that sets vendor_name duplicates the
prefix->vendor mapping maintained elsewhere; replace this block by calling the
shared lookup in export.rs (e.g., a function like vendor_for_prefix or
get_vendor_by_prefix) instead of re-implementing the mapping. Update the code
that currently computes vendor_name (using prefix) to call that shared function,
and ensure the shared function is made public if necessary so the CLI warning
uses the single source of truth for prefix->vendor mappings.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 63d03f66-064b-49af-885f-60be10440394

📥 Commits

Reviewing files that changed from the base of the PR and between 91bab03 and 3293564.

📒 Files selected for processing (11)
  • examples/csv_export.rs
  • examples/event_export.rs
  • examples/export_demo.rs
  • examples/gpx_export.rs
  • examples/multi_export.rs
  • examples/multi_flight_export.rs
  • src/conversion.rs
  • src/export.rs
  • src/lib.rs
  • src/main.rs
  • tests/export_integration_tests.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Do not remove or modify comments unless the related code is changed; only add comments that explain code functionality, no AI instructional comments
Never embed or call external binaries from Rust code
Ensure cargo build --release has no errors or warnings
Only commit if: cargo clippy --all-targets --all-features -- -D warnings, cargo fmt --all -- --check, cargo test --verbose, cargo test --features=cli --verbose, and cargo build --release all pass with no errors or warnings
BEFORE ANY CODE CHANGES: Run cargo clippy --all-targets --all-features -- -D warnings to catch all issues
BEFORE ANY CODE CHANGES: Run cargo fmt --all -- --check to ensure formatting compliance
If cargo fmt --all -- --check fails, IMMEDIATELY run cargo fmt --all to fix formatting
Code must pass cargo fmt --all -- --check without any formatting issues
Never skip clippy checks or formatting checks; never allow warnings to pass
ALWAYS run cargo fmt --all after making ANY code changes before moving to next steps
After running cargo fmt --all, ALWAYS verify with cargo fmt --all -- --check before proceeding

Files:

  • src/conversion.rs
  • src/lib.rs
  • src/main.rs
  • src/export.rs
src/{main,lib}.rs

📄 CodeRabbit inference engine (AGENTS.md)

Maintain CLI source as src/main.rs and library core as src/lib.rs

Files:

  • src/lib.rs
  • src/main.rs
src/{lib.rs,parser/**/*.rs,export.rs}

📄 CodeRabbit inference engine (AGENTS.md)

Core logic in src/lib.rs with parser modules (src/parser/) and export functions (src/export.rs) shared by both library and CLI

Files:

  • src/lib.rs
  • src/export.rs
src/lib.rs

📄 CodeRabbit inference engine (AGENTS.md)

Public API must include: parse_bbl_file(), parse_bbl_bytes(), BBLLog, ExportOptions, export_to_csv(), export_to_gpx(), export_to_event(), conversion utilities, parser helpers

Files:

  • src/lib.rs
src/main.rs

📄 CodeRabbit inference engine (AGENTS.md)

CLI (src/main.rs) uses library export functions (export_to_csv, export_to_gpx, export_to_event) with CLI-specific status messages as thin wrapper with zero public functions

Files:

  • src/main.rs
src/export.rs

📄 CodeRabbit inference engine (AGENTS.md)

The CSV output must precisely match the format and header order of blackbox_decode CSV files

Files:

  • src/export.rs
🧠 Learnings (6)
📓 Common learnings
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 0
File: :0-0
Timestamp: 2025-12-08T21:15:02.434Z
Learning: Repo nerdCopter/bbl_parser: For outside-diff review notes, prefer a compact, single-block comment with flat bullets (file:line → note), no nested <details> or admonitions; overflow should be linked via gist. Maintainer: nerdCopter.
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: LICENSE_COMMERCIAL:1-4
Timestamp: 2025-08-29T19:52:05.099Z
Learning: nerdCopter prefers to avoid publishing personal information in license files for privacy and security reasons, as they are an individual maintainer rather than a company.
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: README.md:520-521
Timestamp: 2025-08-29T19:53:41.354Z
Learning: nerdCopter uses AGPL-3.0-or-later licensing for the bbl_parser project with a dual-licensing approach that includes a separate commercial license option.
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: CONTRIBUTING.md:9-14
Timestamp: 2025-08-21T20:25:45.741Z
Learning: nerdCopter prefers to keep CLA language general using "project maintainer" rather than specifying a legal entity name, as they are an individual maintainer without an associated company.
Learnt from: nerdCopter
Repo: nerdCopter/bbl_parser PR: 2
File: LICENSE_COMMERCIAL:0-0
Timestamp: 2025-08-29T20:15:04.624Z
Learning: nerdCopter prefers clear positive indicators (✅) when describing fixes rather than using ❌ symbols which can be confusing when describing what was corrected in a positive context.
📚 Learning: 2025-12-19T21:17:26.889Z
Learnt from: CR
Repo: nerdCopter/bbl_parser PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T21:17:26.889Z
Learning: Applies to src/main.rs : CLI (`src/main.rs`) uses library export functions (`export_to_csv`, `export_to_gpx`, `export_to_event`) with CLI-specific status messages as thin wrapper with zero public functions

Applied to files:

  • examples/csv_export.rs
  • examples/multi_flight_export.rs
  • examples/gpx_export.rs
  • src/lib.rs
  • examples/event_export.rs
  • examples/multi_export.rs
  • examples/export_demo.rs
  • tests/export_integration_tests.rs
  • src/main.rs
  • src/export.rs
📚 Learning: 2025-12-19T21:17:26.889Z
Learnt from: CR
Repo: nerdCopter/bbl_parser PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T21:17:26.889Z
Learning: Applies to src/lib.rs : Public API must include: `parse_bbl_file()`, `parse_bbl_bytes()`, `BBLLog`, `ExportOptions`, `export_to_csv()`, `export_to_gpx()`, `export_to_event()`, conversion utilities, parser helpers

Applied to files:

  • examples/csv_export.rs
  • examples/multi_flight_export.rs
  • examples/gpx_export.rs
  • src/lib.rs
  • examples/event_export.rs
  • examples/multi_export.rs
  • examples/export_demo.rs
  • tests/export_integration_tests.rs
  • src/main.rs
  • src/export.rs
📚 Learning: 2025-12-19T21:17:26.889Z
Learnt from: CR
Repo: nerdCopter/bbl_parser PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T21:17:26.889Z
Learning: Applies to src/export.rs : The CSV output must precisely match the format and header order of blackbox_decode CSV files

Applied to files:

  • examples/csv_export.rs
  • examples/multi_flight_export.rs
  • src/lib.rs
  • examples/multi_export.rs
  • examples/export_demo.rs
  • tests/export_integration_tests.rs
  • src/main.rs
📚 Learning: 2025-12-19T21:17:26.889Z
Learnt from: CR
Repo: nerdCopter/bbl_parser PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T21:17:26.889Z
Learning: Applies to Cargo.toml : Feature flags `csv`, `cli`, `json`, `serde` control optional dependencies; default: `csv` + `cli`

Applied to files:

  • examples/csv_export.rs
📚 Learning: 2025-12-19T21:17:26.889Z
Learnt from: CR
Repo: nerdCopter/bbl_parser PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-19T21:17:26.889Z
Learning: Applies to src/{lib.rs,parser/**/*.rs,export.rs} : Core logic in `src/lib.rs` with parser modules (`src/parser/`) and export functions (`src/export.rs`) shared by both library and CLI

Applied to files:

  • examples/csv_export.rs
  • src/lib.rs
  • src/main.rs
🔇 Additional comments (13)
src/conversion.rs (1)

346-346: LGTM!

src/export.rs (2)

86-131: LGTM!


467-578: LGTM!

Also applies to: 629-638, 917-926

src/main.rs (2)

9-903: LGTM!


908-941: LGTM!

Also applies to: 950-960

examples/gpx_export.rs (1)

43-52: LGTM!

examples/multi_export.rs (1)

70-71: LGTM!

Also applies to: 79-88, 105-112

examples/multi_flight_export.rs (1)

63-64: LGTM!

examples/csv_export.rs (1)

57-57: LGTM!

examples/event_export.rs (1)

50-50: LGTM!

examples/export_demo.rs (1)

87-87: LGTM!

Also applies to: 112-112, 131-131

src/lib.rs (1)

39-39: LGTM!

tests/export_integration_tests.rs (1)

39-39: LGTM!

Also applies to: 88-88, 127-127, 156-156, 188-188, 249-249

@nerdCopter

Copy link
Copy Markdown
Owner Author

@coderabbitai review.

  • i removed QuickSilver and Kiss because they just set headers to Betaflight
  • i think this code might only repair log 2+, not log 1. how to fix? e.g. if log 1 was betaflight, log 2 and beyond are emuflight, but the log-file was exported from emuflight the file is named EMUF_*.BBL i would expect log 1 to be "fixed" by replacing to BTFL_*.CSV and log 2+ correctly stay EMUF_*.CSV

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cat src/main.rs

Length of output: 42877


🏁 Script executed:

#!/bin/bash
cat src/export.rs

Length of output: 35544



@nerdCopter, good notes on QuickSilver/Kiss — since they report Betaflight headers they'd always be classified as BTFL_, so removing those stub entries is correct.

On the "log 1 not repaired" concern: after tracing through the code, log 1 is actually handled correctly. Here's why:

In parse_bbl_file_streaming (src/main.rs), corrected_session_base_name is called inside the main loop for every log, including log_index = 0 (log 1):

for (log_index, &start_pos) in log_positions.iter().enumerate() {
    // ...
    let (should_skip, reason) = should_skip_export(&log, export_options.force_export);
    if should_skip { continue; }

    // ← called for log 1 (log_index=0) too
    let base_name_override =
        corrected_session_base_name(file_path, &log.header.firmware_revision);

For your exact scenario (EMUF_QUAD.BBL, log 1 = Betaflight, logs 2+ = EmuFlight):

Log BBL prefix Session prefix corrected_session_base_name returns Output file
1 EMUF_ BTFL_ Some("BTFL_QUAD") BTFL_QUAD.01.csv
2+ EMUF_ EMUF_ None (match → no change) EMUF_QUAD.02.csv

One edge case worth noting: if log 1 is filtered out by should_skip_export (e.g. < 5 s duration), no export file is written for it at all — which is fine since there's nothing to rename.

One minor real concern I do see: the transition warning message always says "Consider excluding earlier sessions", which assumes old firmware always appears first. That holds for the reflash scenario (flash not erased before upgrade), but if someone reflashed back to an older firmware, the message could be misleading. Not a blocker but worth a TODO comment.

Triggering the full review now.

✅ Action performed

Full review finished.

@nerdCopter

nerdCopter commented Jun 8, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai, what is better verbiage to fix your reported (output unified-diff format):

One minor real concern I do see: the transition warning message always says "Consider excluding earlier sessions", which assumes old firmware always appears first. That holds for the reflash scenario (flash not erased before upgrade), but if someone reflashed back to an older firmware, the message could be misleading. Not a blocker but worth a TODO comment.

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n "earlier sessions\|firmware transition\|Consider\|WARNING\|vendor" src/main.rs -A2 -B2

Length of output: 347


🏁 Script executed:

#!/bin/bash
rg -n "WARNING\|transition\|erase\|earlier\|session" src/main.rs -A2 -B2

Length of output: 327


🏁 Script executed:

#!/bin/bash
grep -n "WARNING\|transition\|erase\|earlier\|Consider\|firmware" src/main.rs | head -60

Length of output: 1688


🏁 Script executed:

#!/bin/bash
sed -n '908,965p' src/main.rs

Length of output: 1983



@nerdCopter, here's a direction-neutral rewrite in unified-diff format:

--- a/src/main.rs
+++ b/src/main.rs
@@ -954,7 +954,8 @@ fn print_firmware_transition_warning(file_path: &Path, session_firmware: &[(usiz
         }
     }
-    println!("  The flash was not erased before reflashing. Consider excluding earlier sessions.");
+    println!("  This typically occurs when flash is not erased before reflashing.");
+    println!("  Consider excluding sessions from the unintended firmware vendor listed above.");
 }

Why this is better:

Old Problem
"The flash was not erased…" States it as definite fact; softening to "typically occurs" covers edge cases.
"Consider excluding earlier sessions" Assumes old firmware is always first — breaks when someone reflashes back to an older firmware (old sessions would appear later).

The new phrasing refers to the vendor name (already printed above in the grouped list) rather than a positional word, so it stays correct regardless of which direction the firmware transition went.

nerdCopter and others added 2 commits June 9, 2026 07:49
BossHobby Configurator confirmed QUIC_ as the standard filename prefix
for Quicksilver blackbox exports (BossHobby/Configurator@39b8715).
Keyword "quicksilver" matches the expected firmware revision header.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add FORK_REVISION_MAP constant mapping fork filename prefixes to the base
firmware revision prefix they intentionally report.  Quicksilver (QUIC_)
writes Betaflight revision headers for Blackbox Explorer compatibility, so
QUIC_* files must not be renamed to BTFL_* even though the revision string
matches betaflight.

Add test_corrected_session_base_name_fork_exemption to cover this case.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@nerdCopter

Copy link
Copy Markdown
Owner Author

@coderabbitai review

AI Generated comment

Local coderabbit review --agent flagged one finding: export_to_gpx having two trailing None arguments in integration tests vs one in other export calls. This is a false positiveexport_to_gpx intentionally has two optional parameters (log_start_datetime: Option<&str> and base_name_override: Option<&str>). GPX export needs the datetime for track timestamps; the other export functions do not, so they only carry base_name_override. Both None values in the test callsite are correct.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check export_to_gpx signature
rg -n "pub fn export_to_gpx\|pub fn export_to_csv\|pub fn export_to_event" src/export.rs -A5

echo "---TESTS---"
rg -n "export_to_gpx\|export_to_csv\|export_to_event" tests/export_integration_tests.rs -B1 -A2

Length of output: 507


🏁 Script executed:

#!/bin/bash
# Find relevant files first
fd "export.rs" --type f
fd "export_integration" --type f

echo "---EXPORT SIGNATURES---"
grep -n "pub fn export_to_gpx\|pub fn export_to_csv\|pub fn export_to_event" src/export.rs || true

echo "---TEST CALLS---"
grep -n "export_to_gpx\|export_to_csv\|export_to_event" tests/export_integration_tests.rs || true

Length of output: 1046


🏁 Script executed:

#!/bin/bash
# Check full signatures for all three export functions
sed -n '262,275p' src/export.rs
echo "---"
sed -n '490,505p' src/export.rs
echo "---"
sed -n '581,595p' src/export.rs

Length of output: 1653



@nerdCopter, confirmed — the local agent finding is a false positive. The signatures are:

Function Optional params Expected None count
export_to_csv base_name_override: Option<&str> 1
export_to_gpx log_start_datetime: Option<&str>, base_name_override: Option<&str> 2
export_to_event base_name_override: Option<&str> 1

export_to_gpx (Line 490 of src/export.rs) genuinely takes two trailing optional parameters — log_start_datetime is unique to GPX because it's needed for track timestamps. Both None values at the test callsites (Lines 39 and 249 of tests/export_integration_tests.rs) are correct and intentional. Everything looks good — triggering the full review now.


✏️ Learnings added
Learnt from: nerdCopter
URL: https://github.com/nerdCopter/bbl_parser/pull/41

Timestamp: 2026-06-09T18:31:16.628Z
Learning: In `src/export.rs` (bbl_parser project, Rust), `export_to_gpx` has two trailing optional parameters — `log_start_datetime: Option<&str>` and `base_name_override: Option<&str>` — because GPX export needs datetime for track timestamps. The other export functions (`export_to_csv`, `export_to_event`) only have one trailing optional parameter (`base_name_override: Option<&str>`). Two `None` arguments at `export_to_gpx` callsites are intentional and correct, not a mistake.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nerdCopter

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Add sanitize_base_name_override() to strip path separators and parent
segments from base_name_override before file path construction, preventing
path traversal on the public library API (export_to_csv, compute_export_paths).

Add vendor_name_for_prefix() as the single source of truth for
prefix->display-name mapping; remove the duplicate match block in
print_firmware_transition_warning (main.rs) in favour of the shared function.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@nerdCopter

Copy link
Copy Markdown
Owner Author

@coderabbitai, AI generated comment

Addressing review findings from #41 (review) — resolved in ce9a1b4:

Fixed — Outside diff / Major: base_name_override path traversal risk. Added sanitize_base_name_override() which extracts only the final path component via Path::new(raw).file_name(), stripping any ../ segments or absolute path prefix. Applied at both compute_export_paths and export_to_csv call sites.

Fixed — Nitpick 2: Duplicated vendor name match in main.rs. Extracted as pub fn vendor_name_for_prefix() in export.rs; print_firmware_transition_warning now calls the shared function.

Skipped — Nitpick 1 (QUIC_/KISS_ test coverage): Stale. KISS_ was reverted from the codebase. QUIC_'s keyword is "quicksilver" but Quicksilver intentionally writes Betaflight in revision headers — that keyword never matches real files, so a test asserting Some("QUIC_") would only validate a hypothetical code path.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== sanitize_base_name_override ==="
rg -n "sanitize_base_name_override" src/export.rs src/main.rs -A8

echo ""
echo "=== vendor_name_for_prefix ==="
rg -n "vendor_name_for_prefix" src/export.rs src/main.rs -A10

echo ""
echo "=== base_name_override usages in compute_export_paths and export_to_csv ==="
rg -n "sanitize_base_name_override\|base_name_override" src/export.rs -B2 -A2 | head -60

Length of output: 3765



@nerdCopter, both fixes look correctly implemented:

sanitize_base_name_override (src/export.rs:89-92) — Path::new(raw).file_name().and_then(|s| s.to_str()) cleanly strips any ../ traversal and absolute-path prefix. Applied at both compute_export_paths (line 199) and export_to_csv (line 289).

vendor_name_for_prefix (src/export.rs:96-104) — properly pub, imported in main.rs, and called at line 942 to replace the duplicate match.

✅ Skipped Nitpick 1 — the rationale is sound. Since Quicksilver always emits Betaflight in its revision header, firmware_prefix_for_revision would never return Some("QUIC_") for real files, making such a test purely synthetic.

One remaining item — the warning text at src/main.rs:952 still reads the old phrasing:

"  The flash was not erased before reflashing. Consider excluding earlier sessions."

The direction-neutral patch from earlier in this thread was not yet applied. If you'd like to land it, the diff is:

-    println!("  The flash was not erased before reflashing. Consider excluding earlier sessions.");
+    println!("  This typically occurs when flash is not erased before reflashing.");
+    println!("  Consider excluding sessions from the unintended firmware vendor listed above.");

✏️ Learnings added
Learnt from: nerdCopter
URL: https://github.com/nerdCopter/bbl_parser/pull/41

Timestamp: 2026-06-09T18:53:10.080Z
Learning: In `src/export.rs` (bbl_parser project, Rust), `sanitize_base_name_override(base_name_override: Option<&str>) -> Option<&str>` (private fn, line ~89) strips path-traversal sequences from `base_name_override` by extracting only the final path component via `Path::new(raw).file_name().and_then(|s| s.to_str())`. It is applied inside both `compute_export_paths` and `export_to_csv` before the base name is used.

Learnt from: nerdCopter
URL: https://github.com/nerdCopter/bbl_parser/pull/41

Timestamp: 2026-06-09T18:53:10.080Z
Learning: In `src/export.rs` (bbl_parser project, Rust), `pub fn vendor_name_for_prefix(prefix: &str) -> &'static str` (line ~96) maps known filename prefixes ("EMUF_", "BTFL_", "INAV_", "QUIC_") to human-readable vendor names and falls back to "Unknown". It is the single canonical source for vendor name display used by `print_firmware_transition_warning` in `src/main.rs`.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@nerdCopter

Copy link
Copy Markdown
Owner Author

@coderabbitai, i did not like your recommended verbiage, so i did not implement.

-    println!("  The flash was not erased before reflashing. Consider excluding earlier sessions.");
+    println!("  This typically occurs when flash is not erased before reflashing.");
+    println!("  Consider excluding sessions from the unintended firmware vendor listed above.");

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@nerdCopter, understood — your call entirely. The original phrasing stays as-is; no further action needed on that point.

🐇 (˘・_・˘)

@nerdCopter nerdCopter merged commit efdf45f into master Jun 9, 2026
8 of 9 checks passed
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