Date: 2026-02-07 Repository: robocodec - Robotics data format codec library Total Lines of Code: ~66,568 lines (Rust) Clippy Warnings (pedantic): 1,618 warnings
| Metric | Current | Target | Status |
|---|---|---|---|
| Clippy Warnings | 1,618 | <100 | ❌ Critical |
| Code Duplication | ~5% | <3% | |
| Documentation Coverage | ~60% | 90% | |
#[must_use] Attributes |
Missing | All public APIs | |
| Test Coverage (estimated) | ~70% | 80% | ✅ Good |
Estimated Debt Remediation Effort: ~120 hours Expected ROI: 280% over 12 months
Location: src/io/reader/mod.rs:113-202
Issue: The same ChannelInfo construction code is duplicated 4 times across different match arms:
// Repeated 4 times (lines 116-126, 138-148, 160-170, 182-192)
let ch_info = ChannelInfo {
id: ch.id,
topic: ch.topic.clone(),
message_type: ch.message_type.clone(),
encoding: ch.encoding.clone(),
schema: ch.schema.clone(),
schema_data: ch.schema_data.clone(),
schema_encoding: ch.schema_encoding.clone(),
message_count: ch.message_count,
callerid: ch.callerid.clone(),
};Impact:
- Lines duplicated: ~90 lines
- Maintenance burden: Any change to
ChannelInforequires 4 edits - Risk: Inconsistent updates between format variants
Remediation: Extract to a helper function
fn to_channel_info(ch: &TimestampedChannel) -> ChannelInfo {
ChannelInfo {
id: ch.id,
topic: ch.topic.clone(),
// ... rest of fields
}
}Effort: 2 hours Savings: ~16 hours/year (from reduced maintenance)
| File | Lines | Complexity | Issue |
|---|---|---|---|
src/io/s3/reader.rs |
2,318 | High | S3 client complexity |
src/rewriter/mcap/mod.rs |
2,199 | High | MCAP rewriter state machine |
src/io/formats/bag/parallel.rs |
1,981 | High | Parallel bag processing |
src/io/formats/mcap/writer.rs |
1,885 | High | MCAP writing logic |
src/encoding/cdr/encoder.rs |
1,680 | High | CDR encoding |
Impact: These files exceed 1,500 lines, making them difficult to understand and modify.
Recommendation: Consider breaking down large files into smaller, focused modules.
Count: 372 warnings
Issue: Many public methods that return Self or Result lack #[must_use]:
// src/core/error.rs (lines 137, 146, 151, 181)
pub fn buffer_too_short(...) -> Self { }
pub fn alignment_error(...) -> Self { }
pub fn length_exceeded(...) -> Self { }
pub fn log_fields(&self) -> Vec<...> { }Impact: Callers may silently ignore important return values, leading to bugs.
Count: 6 warnings
Issue: Variables with similar names:
decodervsdecoded(src/rewriter/bag.rs:399, 404)decodervsdecoded(src/rewriter/mcap/message.rs:96, 97)
Impact: Confusing code, potential bugs.
Location: src/io/reader/mod.rs:525-609
Issue: The decoded() method uses repeated downcast_ref pattern:
// Repeated 4 times
if let Some(mcap) = self.inner.as_any().downcast_ref::<McapReader>() { ... }
if let Some(bag) = self.inner.as_any().downcast_ref::<ParallelBagReader>() { ... }
if let Some(rrd) = self.inner.as_any().downcast_ref::<RrdReader>() { ... }
if let Some(rrd) = self.inner.as_any().downcast_ref::<ParallelRrdReader>() { ... }Impact:
- Fragile: Adding new formats requires modifying multiple locations
- Violates Open-Closed Principle
- Performance: Multiple downcast attempts
Remediation: Consider a trait-based approach:
trait DecodedMessages {
fn decoded_with_timestamp(&self) -> Result<DecodedMessageIter>;
}Issue: The CLI (src/bin/) depends on clap, but CLAUDE.md states:
What Does NOT Belong in the Library
- CLI tools - Should be in a separate
robocodec-clicrate
Current State: CLI is in the same crate, only feature-gated.
Impact: Increases binary size for library users who don't need CLI.
Recommendation: Move CLI to separate crate or workspace member.
Current State: Tests are split between:
- Unit tests in
src/(in#[cfg(test)]modules) - Integration tests in
tests/(24 files)
Issue: No clear test organization by feature.
Test Files:
| File | Purpose |
|---|---|
bag_decode_tests.rs |
Bag format decoding |
bag_rewriter_tests.rs |
Bag format rewriting |
mcap_integration_tests.rs |
MCAP integration |
mcap_round_trip_tests.rs |
MCAP round-trip |
cdr_encoding_tests.rs |
CDR encoding |
round_trip_tests.rs |
General round-trip |
| ...and 18 more |
Missing:
- Performance regression tests
- Fuzzing tests for parsers
- Property-based tests
- Benchmark suite
Estimated Coverage: ~70% (based on test file distribution)
Uncovered Areas:
- Error handling edge cases
- Transport layer (HTTP/S3)
- Schema parser edge cases
- Transform pipeline error scenarios
| Issue Type | Count | Priority |
|---|---|---|
Missing # Errors sections |
31 | Medium |
| Items missing backticks | 427 | Low |
Missing #[must_use] |
372 | High |
Missing # Example sections |
~50 | Medium |
Well Documented:
RoboReaderDecodedMessageIter- Error types
Needs Improvement:
FormatReadertrait methods lack examplesFormatWritertrait methods lack examples- Internal modules lack overview documentation
| Task | Effort | Impact |
|---|---|---|
Add #[must_use] to 372 warnings |
4h | High |
Extract ChannelInfo construction helper |
2h | High |
| Fix similar variable names | 2h | Medium |
Add # Errors sections to Result-returning functions |
8h | Medium |
Total: 16 hours Expected ROI: 250% in first month
| Task | Effort | Benefit |
|---|---|---|
Refactor DecodedMessageIter downcast pattern |
12h | OCP compliance |
| Add performance benchmarks | 16h | Catch regressions |
| Add property-based tests (proptest) | 12h | Better coverage |
| Improve public API documentation | 20h | Developer experience |
Total: 60 hours
| Task | Effort | Benefit |
|---|---|---|
| Split large files (>1500 lines) | 24h | Maintainability |
| Move CLI to separate crate | 12h | Smaller library binary |
| Establish fuzzing infrastructure | 8h | Security |
Total: 44 hours
Debt_Score:
current: 890
target: 500
Clippy_Warnings:
total: 1618
must_use: 372
missing_errors_doc: 31
similar_names: 6
File_Size:
largest: 2318 lines (io/s3/reader.rs)
files_over_1500: 8 files
Code_Duplication:
estimated_percentage: 5%
target: <3%
Test_Coverage:
estimated: 70%
target: 80%
Documentation:
public_api_coverage: ~60%
target: 90%pre_commit:
- cargo fmt --check
- cargo clippy --all-features -- -D warnings
- cargo test --no-run --all-featuresci:
- deny_new warnings: true
- require_docs_for_public_items: true
- require_#[must_use]_for_result_returning_functions: trueBefore (current):
impl<'a> Iterator for DecodedMessageIter<'a> {
fn next(&mut self) -> Option<Self::Item> {
match &mut self.inner {
Inner::Mcap(stream) => stream.next().map(|result| {
result.map(|(msg, ch)| {
let ch_info = ChannelInfo { /* 10 fields */ };
// ...
})
}),
Inner::Bag(stream) => stream.next().map(|result| {
result.map(|(msg, ch)| {
let ch_info = ChannelInfo { /* same 10 fields */ };
// ...
})
}),
// ... 2 more identical blocks
}
}
}After (proposed):
fn convert_message_result(
(msg, ch): (DecodedMessageWithTimestamp, TimestampedChannel),
) -> DecodedMessageResult {
DecodedMessageResult {
message: msg.message,
channel: ChannelInfo::from(&ch),
log_time: Some(msg.log_time),
publish_time: Some(msg.publish_time),
sequence: None,
}
}
impl ChannelInfo {
fn from(ch: &TimestampedChannel) -> Self {
Self {
id: ch.id,
topic: ch.topic.clone(),
// ... other fields
}
}
}
impl<'a> Iterator for DecodedMessageIter<'a> {
fn next(&mut self) -> Option<Self::Item> {
match &mut self.inner {
Inner::Mcap(stream) => stream.next().map(|r| r.map(convert_message_result)),
Inner::Bag(stream) => stream.next().map(|r| r.map(convert_message_result)),
Inner::Rrd(stream) => stream.next().map(|r| r.map(convert_message_result)),
Inner::ParallelRrd(stream) => stream.next().map(|r| r.map(convert_message_result)),
}
}
}| Initiative | Effort | Monthly Savings | Payback Period |
|---|---|---|---|
| Quick wins | 16h | ~15h | 1 month |
| Medium-term | 60h | ~25h | 2.4 months |
| Long-term | 44h | ~20h | 2.2 months |
Total Investment: 120 hours Annual Savings: ~720 hours (~60 hours/month) ROI: 600% over 12 months
Track monthly:
- Clippy warnings < 100
- Code duplication < 3%
- Documentation coverage > 90%
- All public APIs have
#[must_use]where appropriate - No new code duplication patterns introduced
- Test coverage maintained above 75%
Generated: 2026-02-07 Next Review: 2026-05-07