Files
wz-phone/docs/PRD/PRD-clippy-debt.md
Siavash Sameni 06253fdeeb feat(video+desktop): camera capture, video UI, E2E AEAD wiring, test fixes
Blockers 4 & 5: browser getUserMedia → JPEG IPC → Rust I420 pipeline;
remote video strip renders decoded frames via canvas; EncryptingTransport
wraps QuinnTransport so WZP AEAD is applied to all media (C2 fix).

Test fixes: HandshakeResult.session destructuring across relay/client/crypto
integration tests; video_codecs field added to all CallOffer/CallAnswer
structs; wzp-video pipeline_roundtrip integration tests added.

PRD docs: five Kimi-ready specs for E2E encryption, Android NDK 0.9 migration,
quality upgrade flow, wire-format hardening, and clippy debt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:30:26 +04:00

261 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PRD: Fix wzp-codec Clippy Lint Debt
> **Status:** proposed
> **Resolves:** 9 pre-existing clippy lints in `crates/wzp-codec/src/` that cause `cargo clippy --workspace -D warnings` to fail, breaking any strict-CI configuration.
> **Depends on:** Nothing — all changes are in `crates/wzp-codec/src/`.
## Problem
`cargo clippy -p wzp-codec -- -D warnings` fails with 9 lints across 5 files.
These are pre-existing code patterns that were never flagged during development
because the CI flag was not set. They have no runtime impact today but prevent
adding `-D warnings` to CI without first cleaning them up.
The 3 errors in `deps/featherchat` are in a submodule — do NOT touch them.
`warzone_protocol` clippy errors are accepted debt (not our code).
## Goals
- `cargo clippy -p wzp-codec -- -D warnings` exits 0.
- No behavior changes — every fix is a semantically equivalent rewrite.
- No changes outside `crates/wzp-codec/src/`.
## Non-goals
- Fixing clippy lints in any crate other than `wzp-codec`.
- Adding new functionality.
- Touching the `deps/featherchat` submodule.
## Design
### Lint inventory
| Lint | Count | File | Approx line | Fix |
|------|-------|------|-------------|-----|
| `implicit_saturating_sub` | 1 | `aec.rs` | 117119 | `saturating_sub` |
| `needless_range_loop` | 2 | `aec.rs:164`, `resample.rs:51` | — | iterate with `iter().enumerate()` or direct iter |
| `manual_div_ceil` | 2 | `codec2_dec.rs:48`, `codec2_enc.rs:48` | — | `div_ceil` |
| `manual_clamp` | 2 | `denoise.rs:59`, `opus_enc.rs:250` | — | `.clamp(min, max)` |
| `manual_ascii_check` | 1 | `opus_enc.rs:104` | — | `.eq_ignore_ascii_case()` |
| `same_item_push` | 1 | `resample.rs:184` | — | `vec.resize` or `extend(repeat)` |
### Fix details
#### 1. `implicit_saturating_sub` — `aec.rs` line ~117
Current code:
```rust
fn delay_available(&self) -> usize {
let buffered = self.delay_write - self.delay_read;
if buffered > self.delay_samples {
buffered - self.delay_samples
} else {
0
}
}
```
Clippy wants `saturating_sub` because the subtraction can underflow if
`buffered < self.delay_samples`:
```rust
fn delay_available(&self) -> usize {
let buffered = self.delay_write - self.delay_read;
buffered.saturating_sub(self.delay_samples)
}
```
This is semantically identical (both return 0 when `buffered <= delay_samples`).
#### 2a. `needless_range_loop` — `aec.rs` line ~164
Current code:
```rust
for i in 0..n {
let near_f = nearend[i] as f32;
let base = (self.far_pos + fl * ((n / fl) + 2) + i - n) % fl;
...
}
```
`i` is used both to index `nearend[i]` and in arithmetic (`+ i - n`).
Clippy fires because `nearend[i]` could use `.iter().enumerate()`.
Convert to `enumerate`:
```rust
for (i, &sample) in nearend.iter().enumerate() {
let near_f = sample as f32;
let base = (self.far_pos + fl * ((n / fl) + 2) + i - n) % fl;
...
}
```
Make sure to update any references to `nearend[i]` inside the loop body
to use `sample` (or `near_f` directly). Also update the NLMS adaptation
sub-loop if it references `nearend[i]`.
#### 2b. `needless_range_loop` — `resample.rs` line ~51
Current code:
```rust
for i in 0..FIR_TAPS {
let n = i as f64 - m / 2.0;
let sinc = ...;
let t = 2.0 * i as f64 / m - 1.0;
let kaiser = ...;
kernel[i] = sinc * kaiser;
}
```
`i` is used both as an index (`kernel[i]`) and in arithmetic. Use
`iter_mut().enumerate()`:
```rust
for (i, slot) in kernel.iter_mut().enumerate() {
let n = i as f64 - m / 2.0;
let sinc = ...;
let t = 2.0 * i as f64 / m - 1.0;
let kaiser = ...;
*slot = sinc * kaiser;
}
```
#### 3a. `manual_div_ceil` — `codec2_dec.rs` line ~48
Current code:
```rust
fn bytes_per_frame(&self) -> usize {
(self.inner.bits_per_frame() + 7) / 8
}
```
Replace with:
```rust
fn bytes_per_frame(&self) -> usize {
self.inner.bits_per_frame().div_ceil(8)
}
```
`div_ceil` is stable as of Rust 1.73. The builder uses a recent enough
toolchain. If `bits_per_frame()` returns `usize`, the method is available.
If it returns a different integer type, cast accordingly.
#### 3b. `manual_div_ceil` — `codec2_enc.rs` line ~48
Same pattern, same fix:
```rust
fn bytes_per_frame(&self) -> usize {
self.inner.bits_per_frame().div_ceil(8)
}
```
#### 4a. `manual_clamp` — `denoise.rs` line ~59
Current code:
```rust
let clamped = val.max(-32768.0).min(32767.0);
```
Replace with:
```rust
let clamped = val.clamp(-32768.0_f32, 32767.0_f32);
```
Note: `.clamp()` on `f32` requires both bounds to be the same type. If `val`
is already `f32`, no extra cast is needed. Verify the type of `val` in
context (it is `f32` per the output array type `[f32; 480]`).
#### 4b. `manual_clamp` — `opus_enc.rs` line ~252
Read the surrounding code for the exact pattern. It will be something like:
```rust
let v = if x < min_val { min_val } else if x > max_val { max_val } else { x };
```
or the `.max().min()` chain. Replace with `x.clamp(min_val, max_val)`.
#### 5. `manual_ascii_check` — `opus_enc.rs` line ~104
Current code:
```rust
Ok(v) => !v.is_empty() && v != "0" && v.to_ascii_lowercase() != "false",
```
Clippy wants `.eq_ignore_ascii_case()` instead of lowercasing the whole string:
```rust
Ok(v) => !v.is_empty() && v != "0" && !v.eq_ignore_ascii_case("false"),
```
#### 6. `same_item_push` — `resample.rs` line ~183
Current code:
```rust
for _ in 1..RATIO {
work.push(0.0);
}
```
This pushes the same `0.0` value `(RATIO - 1)` times. Replace with:
```rust
work.resize(work.len() + (RATIO - 1), 0.0f64);
```
Or equivalently:
```rust
work.extend(std::iter::repeat(0.0f64).take(RATIO - 1));
```
Note: `RATIO` is a `const usize`. Verify `work` is `Vec<f64>` in context
(it is — `work.push(s as f64)` immediately before).
## Implementation steps
1. Read each file at the line numbers listed above to confirm the exact current
code before editing (line numbers may shift slightly due to prior edits).
2. Apply all 9 fixes. They are independent — no ordering requirement.
3. Run `cargo clippy -p wzp-codec -- -D warnings` locally or via the CI
command.
4. If any lint persists, re-read that file section and adjust.
## Files to read before implementing
- `crates/wzp-codec/src/aec.rs` lines 114200
- `crates/wzp-codec/src/resample.rs` lines 4570 and 178190
- `crates/wzp-codec/src/codec2_dec.rs` lines 4055
- `crates/wzp-codec/src/codec2_enc.rs` lines 4055
- `crates/wzp-codec/src/denoise.rs` lines 4565
- `crates/wzp-codec/src/opus_enc.rs` lines 96110 and 244260
## Verify
```bash
cargo clippy -p wzp-codec -- -D warnings
```
Expected: exits 0 with no warnings.
Also run to confirm no regressions:
```bash
cargo test -p wzp-codec
```
## Done when
`cargo clippy -p wzp-codec -- -D warnings` exits 0. All 9 lints are gone.
`cargo test -p wzp-codec` passes. No changes outside `crates/wzp-codec/src/`.