---
phase: 06-errorhandling
plan: 02
type: execute
wave: 2
depends_on:
  - 06-01
files_modified:
  - src/parser.rs
  - tests/parser_errors.rs
autonomous: true
requirements:
  - ERR-02
  - ERR-01
  - ERR-03
tags:
  - rust
  - errors
  - skip-errors
  - tests
must_haves:
  truths:
    - "调用方可通过 iter().skip_errors() 跳过解析错误，只获取成功解析的 Sqllog"
    - "skip_errors() 返回的迭代器类型是 impl Iterator<Item = Sqllog<'a>>，不是 Result"
    - "调用方仍可通过 iter()（返回 Result）手动控制错误处理"
    - "所有新增测试通过，行号追踪和 skip_errors 行为均被验证"
  artifacts:
    - path: "src/parser.rs"
      provides: "LogIterator::skip_errors() 方法"
      contains: "pub fn skip_errors"
    - path: "tests/parser_errors.rs"
      provides: "覆盖 line_number 和 skip_errors 的新测试"
      contains: "test_skip_errors_filters_invalid_records"
  key_links:
    - from: "LogIterator::skip_errors"
      to: "Iterator::filter_map(Result::ok)"
      via: "内部委托"
      pattern: "filter_map\\(Result::ok\\)"
    - from: "tests/parser_errors.rs"
      to: "ParseError::InvalidFormat"
      via: "match 解构 line_number"
      pattern: "InvalidFormat \\{ line_number,"
---

<objective>
添加 `LogIterator::skip_errors()` 方法（D-04），让调用方能以 `iter().skip_errors()` 跳过错误记录。
同时添加覆盖行号追踪和 skip_errors 行为的全套测试。

Purpose: ERR-02 要求调用方可选择跳过解析错误。D-04（.skip_errors() 便捷方法）和 D-05（不引入 ErrorPolicy enum）锁定决策实现。
Output: `LogIterator::skip_errors()` 方法 + 5 个新测试（覆盖 line_number、skip_errors、Display、std::error::Error trait）。
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/STATE.md
@.planning/ROADMAP.md
@.planning/REQUIREMENTS.md
@.planning/phases/06-errorhandling/06-CONTEXT.md
@CLAUDE.md
@src/parser.rs
@src/error.rs
@src/lib.rs
@tests/parser_errors.rs

<interfaces>
<!-- 关键现有 API 和上下文，供 executor 直接使用 -->

```rust
// src/parser.rs — LogIterator 在 Plan 1 后的状态（增加了 line_number 追踪）
pub struct LogIterator<'a> {
    data: &'a [u8],
    pos: usize,
    encoding: FileEncodingHint,
    line_number: u64,  // 新增于 Plan 1
}

// LogIterator 的 Iterator impl 位于 impl LogIterator 块之外
// 新的 skip_errors() 需要放在 impl LogIterator 块内（与字段同块）

// tests/parser_errors.rs 现有内容（简化）：
// use dm_database_parser_sqllog::LogParser;
// use std::io::Write;
// use tempfile::NamedTempFile;
//
// 两个现有测试：
// iterator_yields_error_for_invalid_first_line_then_ok
// iterator_skips_empty_record_slice_between_valid_records

// 构造一个有效记录的函数模式（来自 tests/parser_coverage.rs 的风格）：
// let good = "2025-11-17 16:09:41.123 (EP[0] sess:1 thrd:2 user:u trxid:3 stmt:4 appname:a) X\n";

// 构造一个无效记录的简单形式（不含时间戳前缀的行会被 iter() 返回错误）：
// let bad = "this is not a record\n";
```
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: 添加 LogIterator::skip_errors() 方法</name>
  <files>src/parser.rs</files>
  <read_first>
    - src/parser.rs (LogIterator 结构体定义行、现有 Iterator impl 块、结构体字段对齐方式、doc comment 风格)
    - .planning/phases/06-errorhandling/06-CONTEXT.md (D-04: skip_errors 签名 D-05: 不引入 ErrorPolicy enum; `<specifics>` 节签名建议)
  </read_first>
  <action>
    在 `src/parser.rs` 中为 `LogIterator<'a>` 添加一个公开方法。

    方法位置：**在 `impl LogIterator` 块内**（而非 `impl Iterator for LogIterator` 块内）。具体来说，LogIterator 结构体定义后紧跟着 `impl LogIterator` 块，`skip_errors()` 方法应放在此块内。定位方式：在文件中搜索 `impl<'a> LogIterator` 找到正确的 impl 块。

    方法签名：`pub fn skip_errors(self) -> impl Iterator<Item = Sqllog<'a>> + 'a`

    实现体：`self.filter_map(Result::ok)`

    方法上方需要 doc comment，包含：
    - 描述方法作用：返回一个跳过解析错误的迭代器
    - 使用场景示例（简短，1-2 行代码示意）
    - 注意说明：par_iter() 不支持此方法（par_iter 返回 Rayon ParallelIterator 而非 LogIterator）
    - 行号追踪说明：skip_errors 不改变行号行为；仍通过原始 iter() 获取错误信息
  </action>
  <verify>
    <automated>cargo build 2>&1 | grep -q "error" || echo "BUILD_OK"</automated>
  </verify>
  <acceptance_criteria>
    - `grep -n "pub fn skip_errors" src/parser.rs` 至少命中 1 行
    - `grep "filter_map(Result::ok)" src/parser.rs` 至少命中 1 行
    - `cargo build` 退出码 0
    - `cargo clippy --all-targets -- -D warnings` 退出码 0（无新增 clippy 警告）
  </acceptance_criteria>
  <done>
    `LogIterator::skip_errors()` 方法存在，返回 `impl Iterator<Item = Sqllog>`。编译通过，clippy 无警告。
  </done>
</task>

<task type="auto">
  <name>Task 2: 新增 line_number 和 skip_errors 测试</name>
  <files>tests/parser_errors.rs</files>
  <read_first>
    - tests/parser_errors.rs (现有导入、测试风格、`#[cfg(not(miri))]` 门控模式、NamedTempFile 使用方式、断言风格)
    - src/error.rs (ParseError 变体构造方式)
    - .planning/phases/06-errorhandling/06-CONTEXT.md (D-04: skip_errors 行为; D-02: 行号从 1 开始)
  </read_first>
  <action>
    在 `tests/parser_errors.rs` 文件末尾追加 5 个测试。在文件顶部现有 import 行中添加 `ParseError` 导入。

    **Import 修改：**
    将第 1 行的 `use dm_database_parser_sqllog::LogParser;`
    改为 `use dm_database_parser_sqllog::{LogParser, ParseError};`

    **以下测试追加到文件末尾：**

    1. `test_skip_errors_filters_invalid_records`（#[cfg(not(miri))]）：
       - 写入 3 行：有效记录、无效记录（不含时间戳前缀的行）、有效记录
       - 调用 `parser.iter().skip_errors()` 收集结果
       - 断言收集到 2 个 Sqllog，且它们的 body 各自包含 "VALID_A" 和 "VALID_B"（或类似标识文本）
       - 断言 `skip_errors` 返回的不是 Result 类型

    2. `test_error_contains_correct_line_number`（#[cfg(not(miri))]）：
       - 写入 2 行：有效记录、无效记录
       - 调用 `parser.iter()` 逐条获取
       - 第 1 条 `unwrap()` 确认有效
       - 第 2 条 `unwrap_err()` 获取错误
       - match 解构 `ParseError::InvalidFormat { line_number, .. }` 断言 `line_number == 2`
       - 对其他变体分支 panic（"Expected InvalidFormat"）

    3. `test_line_number_after_multiple_valid_records`（#[cfg(not(miri))]）：
       - 写入 4 行：3 条有效记录 + 1 条无效记录
       - 调用 `parser.iter()` 解析
       - 第 4 条的错误中 `line_number == 4`（前 3 行有效记录各占 1 行，第 4 行无效）

    4. `test_parse_error_impl_std_error`（无 #[cfg(not(miri))] 门控）：
       - 在函数体内使用 `fn assert_error<E: std::error::Error>() {}` 辅助函数
       - 调用 `assert_error::<ParseError>()`
       - 编译通过即证明实现了 std::error::Error

    5. `test_error_display_contains_line_number`（无 #[cfg(not(miri))] 门控）：
       - 直接构造一个 `ParseError::InvalidFormat { raw: "test".to_string(), line_number: 42 }`
       - 断言 `err.to_string()` 包含字符串 `"42"` 和字符串 `"line"`
    
    注意测试 4 和 5 不需要 `#[cfg(not(miri))]` 因为它们不构造 mmap。

    每个测试函数上方添加对应的 doc comment（简述测试目的）。
  </action>
  <verify>
    <automated>cargo test --test parser_errors 2>&1 | tail -20</automated>
  </verify>
  <acceptance_criteria>
    - `cargo test --test parser_errors` 输出显示 7 个测试通过（原 2 个 + 新 5 个），退出码 0
    - `grep -c "fn test_skip_errors_filters_invalid_records" tests/parser_errors.rs` 输出为 1
    - `grep -c "fn test_error_contains_correct_line_number" tests/parser_errors.rs` 输出为 1
    - `grep -c "fn test_line_number_after_multiple_valid_records" tests/parser_errors.rs` 输出为 1
    - `grep -c "fn test_parse_error_impl_std_error" tests/parser_errors.rs` 输出为 1
    - `grep -c "fn test_error_display_contains_line_number" tests/parser_errors.rs` 输出为 1
    - `grep "ParseError" tests/parser_errors.rs | head -1` 包含 `use dm_database_parser_sqllog::ParseError`
    - 每个 `#[cfg(not(miri))]` 测试内部使用 `NamedTempFile` + `LogParser::from_path`
    - 非 miri 测试不包含 `NamedTempFile` 或 `LogParser`
    - `cargo test --test parser_errors` 不会产生 `error` 输出（panic 信息除外，panic 是预期的测试失败）
  </acceptance_criteria>
  <done>
    5 个新测试覆盖行号追踪正确性（单条、跨多条）、skip_errors 行为、std::error::Error trait、Display 格式。全部通过。
  </done>
</task>

</tasks>

<threat_model>
## Trust Boundaries

| Boundary | Description |
|----------|-------------|
| LogIterator -> user code | skip_errors() 返回的迭代器屏蔽了错误信息；用户可能丢失调试所需的错误上下文 |

## STRIDE Threat Register

| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-06-03 | Information Disclosure | skip_errors() | accept | skip_errors 故意丢弃错误信息。用户如需调试应使用原生 iter() 收集 Result 并检查错误。D-05 明确不引入 ErrorPolicy enum，选择最简单的跳过语义。doc comment 警告此限制。 |
| T-06-04 | Tampering | par_iter() + line_number | accept | par_iter() 用 0 表示不可用行号，已在 Plan 1 doc comment 注明。此 plan 的 skip_errors doc comment 也重复说明 par_iter 不支持。 |
| T-06-SC | Tampering | 测试中使用 tempfile | mitigate | tempfile 已是现有 dev-dependency，Cargo.lock 锁定。不新增依赖。 |
</threat_model>

<verification>
- `cargo build` 编译通过
- `cargo test --test parser_errors` 全部 7 个测试通过（原 2 + 新 5）
- `cargo test` 全 workspace 测试通过（不破坏其他测试）
- `cargo clippy --all-targets -- -D warnings` 无新增警告
- `cargo llvm-cov --workspace --all-features --fail-under-lines 90` 覆盖率 >= 90%
</verification>

<success_criteria>
- `LogIterator::skip_errors()` 方法可被公开调用，返回 `impl Iterator<Item = Sqllog<'a>>`
- 误差记录被 skip_errors() 透明跳过
- 行号追踪的 3 个测试全部通过（单条错误行号、跨多条记录行号）
- std::error::Error trait 的编译时验证通过
- Display 格式中包含行号信息已通过测试验证
- 所有现有测试不受影响
</success_criteria>

<output>
After completion, create `.planning/phases/06-errorhandling/06-02-SUMMARY.md` documenting:
- `skip_errors()` 方法签名和实现
- 新增测试列表及其验证目标
- 行号追踪行为实测结果
</output>
