feed-parser 2.0.0

A simple RSS 1.0 / RSS 2.0 / Atom feed parser
Documentation
[English]development.md | **日本語**

# 開発

## コマンド

| コマンド | 説明 |
|--|---|
|`cargo make ci` | CI が実行する内容をすべて実行する.push 前にこれを使う. |
|`cargo make test` | nextest でユニットテストと結合テストを実行する. |
|`cargo make doctest` | doctest を実行する(nextest は実行しない). |
|`cargo make fmt` | ソースを整形する. |
|`cargo make fmt-check` | ファイルを書き換えずに整形を検証する. |
|`cargo make lint` | clippy を実行し,警告をエラーとして扱う. |

以下は CI が実行するコマンドそのものです.push 前に3つとも通っている必要があります.

| コマンド | 説明 |
|--|---|
|`cargo fmt --all --check` | 整形を検証する. |
|`cargo clippy --all-targets --all-features -- -D warnings` | 警告をエラーとして lint する. |
|`cargo test --all-features` | ユニットテスト・結合テスト・doctest を実行する. |

## 構成

| パス | 内容 |
|--|---|
|`src/parsers/{rss1,rss2,atom}/` | 形式ごとのパーサ.各々がユニットテストを持つ. |
|`src/parsers/internal.rs` | 3つのパーサが共有するヘルパ.HTML エスケープ,テキスト連続区間のバッファリング,エントリのデシリアライズ. |
|`src/parsers/errors.rs` | `ParseError``ParseResult`|
|`tests/error_handling.rs` | 不正な入力が panic ではなくエラーになることの検証. |
|`tests/text_content.rs` | テキスト・実体参照・CDATA・空白が解析を経ても保たれることの検証. |

## パーサの仕組み

各パーサは2段階で処理します.

1. **エスケープ**.フィードは `<title>``<description>``<summary>``<content>`   生の HTML を埋め込むことが多く,その結果 XML として不正な文書になります.まず要素ごとの
   正規表現でその内容をエスケープし,リーダにはテキストとして見せます.
2. **書き換えとデシリアライズ**.リーダが文書を走査し,ライタが各エントリを正規化した要素名
   `dc:creator``creator``pubDate``publish_date`)で書き直したうえで,
   `Feed` へデシリアライズします.

イベントループに手を入れるときに間違えやすい点が2つあります.

- **イベント種別を黙って捨てないこと.** quick-xml は実体参照を,前後のテキストとは別の
  `Event::GeneralRef` として報告します.これを無視するワイルドカードの腕があると,エントリ内の
  エスケープされた文字がすべて消えます.しかも既存のテストは気付きません.実際 0.37 から 0.41
  への更新でこれが起きました.
- **リーダの `trim_text` を使わないこと.** 文字データは実体参照の位置で分割されるため,
  断片ごとに trim するとエスケープされた文字の隣の空白が食われます.代わりに `TextRun`  連続区間をバッファし,その外側の端だけを trim します.

このコードを変更するときは,ユニットテストだけを信用せず,テキスト・実体参照・CDATA・
入れ子のマークアップが混在する入力で直前のリリースと出力を比較してください.

## 対応する最小の Rust バージョン(MSRV)

このクレートは `rust-version = "1.85"` を宣言しています.これは edition 2024 が課す下限です.
CI の `msrv` ジョブが push のたびに,まさにそのツールチェーンでビルドします.

### 方針:MSRV は据え置く

**MSRV は現在のエディションが要求する値に固定し,時間が経ったというだけの理由では上げません.**
stable を追いかければ数ヶ月ごとにバンプと CI の修正とリリースノートの記載が必要になりますが,
それで得をする人はいません.据え置けば,何もしなくても互換性は静かに広がっていきます.

引き上げるのは次のいずれかに当てはまるときだけです.

1. クレートを新しいエディションへ移行するとき.
2. 言語機能や `std` の API がそのコストに見合うとき.これは既定ではなく明示的な判断です.
   前例:let-chains(Rust 1.88)は `src/parsers/internal.rs` で入れ子の `if let` を選んで
   見送りました.4行の短縮のために MSRV を3リリース分上げる価値は無いと判断したためです.
3. 依存が自身の MSRV をこちらより上げ,古い版に固定できないとき.実効 MSRV はこのクレートと
   全依存の最大値なので,依存を追加する前に `rust-version` を確認してください.
   feed-parser 2.0.0 時点での最大は quick-xml の 1.79 で,こちらの下限を十分に下回ります.

### 引き上げはメジャーではなくマイナーリリース

`rust-version` を宣言しており,edition 2024 が MSRV を考慮する resolver v3 を選ぶため,
古いツールチェーンの利用者はビルド失敗ではなく,まだ対応している最後のリリースへ解決されます.
したがって MSRV の引き上げは緩やかに着地し,メジャーバージョンを必要としません.変更が
見つけられるよう,変更履歴に記載してください.

### 実際に変更するとき

次の2つは必ず同時に更新してください.宣言とその検証であり,片方だけでは意味を失います.

- `Cargo.toml``rust-version`
- `.github/workflows/unit_test.yml``msrv` ジョブのツールチェーンとジョブ名

「手元の環境でビルドが通る」ことを根拠にしないでください.最新の rustc は古い機能も新しい機能も
同じように受け付けるため,MSRV 違反を検出できません.検出できるのは `msrv` ジョブだけです.
手元で確認するなら `rustup toolchain install 1.85 && cargo +1.85 build --all-features` です.

## リリース手順

1. `Cargo.toml``version` を上げ,[変更履歴]changelog.ja.mdに項目を追加する.
2. `v*` タグを push する.`.github/workflows/release.yml` がテストを実行して公開する.
   これには `CARGO_REGISTRY_TOKEN` のリポジトリシークレットが必要.