Context
Follow-up to PR #12 which successfully completed issue #11 (Phases 1-3). This issue addresses remaining code duplication discovered during PR review.
Reference: PR #12 comment thread
Summary
PR #12 eliminated 471 net lines of duplication by unifying helpers, parse_frame_data, and BBLDataStream. However, ~202 additional lines of frame parser duplication remain in src/main.rs:
- Dead code:
parse_i_frame (31 lines) — never called
- Duplicated:
parse_s_frame (63 lines) — needs verification (has TAG2_3S32 handling)
- Duplicated:
parse_h_frame (43 lines) — nearly identical to crate version
- Duplicated:
parse_e_frame (65 lines) — crate version is MORE comprehensive
Detailed Analysis
1. Dead Code: parse_i_frame (Lines 2299-2329)
Status: 🔴 Dead code — never called
Evidence:
# Function defined but never invoked
$ rg 'parse_i_frame\(' src/main.rs
2299:fn parse_i_frame( # definition only, no call sites
I-frames use the crate's parse_frame_data directly (line 1750):
'I' => {
// ... existing setup ...
parse_frame_data(
&mut stream,
&header.i_frame_def,
&mut i_frame_values,
// ...
)?;
}
Action: Delete lines 2299-2329.
2. Duplication: parse_s_frame (Lines 2330-2392)
Status: ⚠️ Verification required — implementations differ
Key Difference:
- CLI version: Handles
ENCODING_TAG2_3S32 (reads 3 fields at once)
- Crate version (
src/parser/frame.rs:600): Does NOT handle TAG2_3S32
CLI implementation snippet:
ENCODING_TAG2_3S32 => {
let mut values = [0i32; 8];
stream.read_tag2_3s32(&mut values)?;
for j in 0..3 {
if field_index + j < frame_def.fields.len() {
let current_field = &frame_def.fields[field_index + j];
data.insert(current_field.name.clone(), values[j]);
}
}
field_index += 3;
}
Crate implementation (src/parser/frame.rs:600):
fn parse_s_frame(...) -> Result<HashMap<String, i32>> {
let mut data = HashMap::new();
for field in &frame_def.fields {
let value = match field.encoding {
ENCODING_SIGNED_VB => stream.read_signed_vb()?,
ENCODING_UNSIGNED_VB => stream.read_unsigned_vb()? as i32,
ENCODING_NEG_14BIT => stream.read_neg_14bit()?,
ENCODING_NULL => 0,
_ => { /* fallback */ }
};
data.insert(field.name.clone(), value);
}
Ok(data)
}
Migration Strategy:
Option A (Safer): Enhance crate's parse_s_frame first
- Add TAG2_3S32 handling to
src/parser/frame.rs:parse_s_frame
- Match CLI's field iteration logic (
field_index increment by group size)
- Add test to verify S-frame parsing with TAG2_3S32 encoding
- Replace CLI call site (line 1946) with crate import
- Delete CLI implementation (lines 2330-2392)
Option B (Riskier): Verify TAG2_3S32 is never used in S-frames
- Analyze test logs to confirm S-frames never use TAG2_3S32
- If confirmed unused, directly replace with crate version
- Add regression test to catch TAG2_3S32 in S-frames if it appears
Recommended: Option A — preserving existing behavior is safer.
3. Duplication: parse_h_frame (Lines 2393-2435)
Status: ✅ Safe to replace — implementations are equivalent
Comparison:
| Aspect |
CLI (main.rs:2393) |
Crate (gps.rs:22) |
| Structure |
Identical |
Identical |
| ENCODING_NEG_14BIT |
-(sign_extend_14bit(...)) |
read_neg_14bit() (same result) |
| Error handling |
unwrap_or(0) fallback |
unwrap_or(0) fallback |
| Debug output |
Same format |
Same format |
Migration:
- Update
src/main.rs line 1979 call site:
// Before
if let Ok(data) = parse_h_frame(&mut stream, &header.h_frame_def, debug) {
// After
use bbl_parser::parser::gps::parse_h_frame;
if let Ok(data) = parse_h_frame(&mut stream, &header.h_frame_def, debug) {
- Delete CLI implementation (lines 2393-2435)
4. Duplication: parse_e_frame (Lines 2436-2500)
Status: ✅ Safe to replace — crate version is superior
Why Crate Version is Better:
- Handles additional event types: 10, 11, 12, 13, 14
- Includes
parse_inflight_adjustment helper (DRY principle)
- More comprehensive event type coverage
- Better documented with module-level comments
Event Types Comparison:
| Event Type |
CLI Support |
Crate Support |
Notes |
| 0-6 |
✅ |
✅ |
Identical |
| 10-12 |
❌ |
✅ |
UNUSED autotune variants |
| 13 |
❌ |
✅ |
Inflight adjustment (alternate numbering) |
| 14 |
❌ |
✅ |
Logging resume (newer numbering) |
| 30, 255 |
✅ |
✅ |
Identical |
Migration:
- Update
src/main.rs line 2123 call site:
// Before
if let Ok(mut event_frame) = parse_e_frame(&mut stream, debug) {
// After
use bbl_parser::parser::event::parse_e_frame;
if let Ok(mut event_frame) = parse_e_frame(&mut stream, debug) {
- Delete CLI implementation (lines 2436-2500)
Step-by-Step Migration Plan
Phase 4a: Remove Dead Code (Low Risk)
Goal: Delete unused parse_i_frame
# 1. Verify function is never called
rg 'parse_i_frame\(' src/main.rs # Should only show definition
# 2. Delete lines 2299-2329
sed -i '2299,2329d' src/main.rs
# 3. Validate
cargo build
cargo test
Success Criteria: Build passes, all 50 tests pass
Phase 4b: Replace H-frame Parser (Low Risk)
Goal: Use crate's parse_h_frame
# 1. Add import to main.rs (near top with other bbl_parser imports)
# Add: use bbl_parser::parser::gps::parse_h_frame;
# 2. Verify call site uses imported version (line ~1979)
rg -A 3 'parse_h_frame' src/main.rs
# 3. Delete CLI implementation (lines 2393-2435)
# Note: Line numbers shift after previous deletion, recalculate
# 4. Validate
cargo build
cargo test --all
# 5. A/B test: Compare CSV output
cargo run -- --input tests/sample.bbl --output /tmp/before.csv
# (after changes)
cargo run -- --input tests/sample.bbl --output /tmp/after.csv
diff /tmp/before.csv /tmp/after.csv # Should be empty
Success Criteria:
- Build passes
- All tests pass
- CSV outputs identical
Phase 4c: Replace E-frame Parser (Low Risk)
Goal: Use crate's superior parse_e_frame
# 1. Add import to main.rs
# Add: use bbl_parser::parser::event::parse_e_frame;
# 2. Verify call site (line ~2123)
rg -A 3 'parse_e_frame' src/main.rs
# 3. Delete CLI implementation (lines 2436-2500, adjusted for previous deletions)
# 4. Validate
cargo build
cargo test --all
# 5. Verify event parsing works (especially event types 10-14)
cargo run -- --input tests/sample_with_events.bbl --output /tmp/events.csv
# Check for any new event types captured
Success Criteria:
- Build passes
- All tests pass
- Event frames parsed correctly (check stats output)
Phase 4d: Replace S-frame Parser (⚠️ Higher Risk)
Goal: Unify parse_s_frame after verifying TAG2_3S32 handling
Step 1: Investigate TAG2_3S32 Usage
# Check if any test logs use TAG2_3S32 in S-frames
for f in tests/*.bbl; do
echo "Checking $f"
cargo run -- --input "$f" --debug 2>&1 | grep -i 'S-frame.*TAG2_3S32'
done
# Check frame definitions
rg 'TAG2_3S32' src/
Step 2a: If TAG2_3S32 is used (Safer Path)
# 1. Enhance crate's parse_s_frame in src/parser/frame.rs
# Add TAG2_3S32 handling matching CLI implementation:
// In src/parser/frame.rs:parse_s_frame, change to:
fn parse_s_frame(...) -> Result<HashMap<String, i32>> {
let mut data = HashMap::new();
let mut field_index = 0;
while field_index < frame_def.fields.len() {
let field = &frame_def.fields[field_index];
match field.encoding {
ENCODING_SIGNED_VB => { /* existing */ }
ENCODING_UNSIGNED_VB => { /* existing */ }
ENCODING_NEG_14BIT => { /* existing */ }
ENCODING_TAG2_3S32 => {
// NEW: Handle 3 fields at once
let mut values = [0i32; 8];
stream.read_tag2_3s32(&mut values)?;
for j in 0..3 {
if field_index + j < frame_def.fields.len() {
let current_field = &frame_def.fields[field_index + j];
data.insert(current_field.name.clone(), values[j]);
}
}
field_index += 3;
continue;
}
ENCODING_NULL => { /* existing */ }
_ => { /* existing fallback */ }
};
data.insert(field.name.clone(), value);
field_index += 1;
}
Ok(data)
}
# 2. Test enhanced crate function
cargo test
# 3. Replace CLI call site (line ~1946)
# Add import: use bbl_parser::parser::frame::parse_s_frame;
# 4. Delete CLI implementation
# 5. Validate with all test logs
for f in tests/*.bbl; do
cargo run -- --input "$f" --output /tmp/test_output.csv
# Compare with known-good output if available
done
Step 2b: If TAG2_3S32 is NOT used (Simpler Path)
# 1. Add import to main.rs
# Add: use bbl_parser::parser::frame::parse_s_frame;
# 2. Replace call site (line ~1946)
# 3. Delete CLI implementation
# 4. Add regression test to catch TAG2_3S32 if it appears:
# Add test that fails if TAG2_3S32 encoding is found in S-frames
Success Criteria:
- Build passes
- All 50 tests pass
- CSV outputs identical across all test logs
- No S-frame parsing errors in debug output
Validation Checklist
After completing all phases:
Rollback Plan
If any phase fails validation:
# Discard changes and return to current state
git checkout src/main.rs
git checkout src/parser/frame.rs # if modified
cargo test --all # Verify stable state
Expected Benefits
- Code reduction: ~202 lines removed from CLI
- Maintenance: Single source of truth for frame parsing
- Functionality: Gain event types 10-14 in E-frame parsing
- Consistency: All frame parsing uses crate implementations
Dependencies
Notes
- Phases 4a-c are low risk (straightforward replacements)
- Phase 4d (S-frames) requires verification due to TAG2_3S32 handling difference
- Recommend committing each phase separately for easy rollback
- All changes preserve behavior (no algorithm modifications)
References
Context
Follow-up to PR #12 which successfully completed issue #11 (Phases 1-3). This issue addresses remaining code duplication discovered during PR review.
Reference: PR #12 comment thread
Summary
PR #12 eliminated 471 net lines of duplication by unifying helpers,
parse_frame_data, andBBLDataStream. However, ~202 additional lines of frame parser duplication remain insrc/main.rs:parse_i_frame(31 lines) — never calledparse_s_frame(63 lines) — needs verification (has TAG2_3S32 handling)parse_h_frame(43 lines) — nearly identical to crate versionparse_e_frame(65 lines) — crate version is MORE comprehensiveDetailed Analysis
1. Dead Code:
parse_i_frame(Lines 2299-2329)Status: 🔴 Dead code — never called
Evidence:
I-frames use the crate's
parse_frame_datadirectly (line 1750):Action: Delete lines 2299-2329.
2. Duplication:
parse_s_frame(Lines 2330-2392)Status:⚠️ Verification required — implementations differ
Key Difference:
ENCODING_TAG2_3S32(reads 3 fields at once)src/parser/frame.rs:600): Does NOT handle TAG2_3S32CLI implementation snippet:
Crate implementation (src/parser/frame.rs:600):
Migration Strategy:
Option A (Safer): Enhance crate's
parse_s_framefirstsrc/parser/frame.rs:parse_s_framefield_indexincrement by group size)Option B (Riskier): Verify TAG2_3S32 is never used in S-frames
Recommended: Option A — preserving existing behavior is safer.
3. Duplication:
parse_h_frame(Lines 2393-2435)Status: ✅ Safe to replace — implementations are equivalent
Comparison:
-(sign_extend_14bit(...))read_neg_14bit()(same result)unwrap_or(0)fallbackunwrap_or(0)fallbackMigration:
src/main.rsline 1979 call site:4. Duplication:
parse_e_frame(Lines 2436-2500)Status: ✅ Safe to replace — crate version is superior
Why Crate Version is Better:
parse_inflight_adjustmenthelper (DRY principle)Event Types Comparison:
Migration:
src/main.rsline 2123 call site:Step-by-Step Migration Plan
Phase 4a: Remove Dead Code (Low Risk)
Goal: Delete unused
parse_i_frameSuccess Criteria: Build passes, all 50 tests pass
Phase 4b: Replace H-frame Parser (Low Risk)
Goal: Use crate's
parse_h_frameSuccess Criteria:
Phase 4c: Replace E-frame Parser (Low Risk)
Goal: Use crate's superior
parse_e_frameSuccess Criteria:
Phase 4d: Replace S-frame Parser (⚠️ Higher Risk)
Goal: Unify
parse_s_frameafter verifying TAG2_3S32 handlingStep 1: Investigate TAG2_3S32 Usage
Step 2a: If TAG2_3S32 is used (Safer Path)
Step 2b: If TAG2_3S32 is NOT used (Simpler Path)
Success Criteria:
Validation Checklist
After completing all phases:
cargo build— no errorscargo test --all— all 50 tests passcargo clippy— zero warningsrg 'fn parse_[ishe]_frame' src/main.rs— returns nothingtokei src/main.rs— verify ~200 lines removedrg 'parse_[ISPHE]' src/main.rs— only crate imports remainRollback Plan
If any phase fails validation:
Expected Benefits
Dependencies
Notes
References