feed-parser 2.0.0

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

# 使い方

## 3つのパーサ

対応する形式ごとにモジュールが分かれており,いずれも同じ入口を公開しています.

```rust
pub fn parse(text: &str) -> ParseResult<Vec<Feed>>
```

| モジュール | 形式 | エントリ要素 |
|--|--|--|
| `feed_parser::parsers::rss1` | RSS 1.0 (RDF) | `<item>` |
| `feed_parser::parsers::rss2` | RSS 2.0 | `<item>` |
| `feed_parser::parsers::atom` | Atom | `<entry>` |

`parse` は文書全体を受け取り,エントリ要素ごとに `Feed` を1つずつ,文書中の順序で返します.
チャンネルレベルのメタデータは返りません.結果が表すのはエントリです.

```rust
use feed_parser::parsers::{Feed, atom};

let feeds: Vec<Feed> = atom::parse(atom_document)?;
for feed in &feeds {
    println!("{} — {}", feed.title, feed.link);
}
# Ok::<(), feed_parser::parsers::errors::ParseError>(())
```

形式の自動判定は行いません.配信元に合ったモジュールを選ぶか,順に試して最初に成功したものを
採用してください.

## `Feed`

```rust
pub struct Feed {
    pub title: String,
    pub link: String,
    pub description: Option<String>,
    pub summary: Option<String>,
    pub updated: Option<String>,
    pub publish_date: Option<String>,
    pub creator: Option<String>,
    pub date: Option<String>,
    pub other: Option<String>,
}
```

`title` と `link` は必須です.どちらかを欠くエントリは `Feed` にできず,パーサは
[`ParseError::MissingField`](errors.ja.md) を返します.それ以外は任意で,配信元が省略した
場合は `None` になります.

日付はフィードが配信した文字列のまま保持します.RSS 2.0 の RFC 822,Atom の ISO 8601,
そのどちらにも従わない配信元と,形式は統一されていないため,このクレートは特定の解釈を
押し付けません.実際のタイムスタンプが必要な場合は,呼び出し側で `chrono` や `time` を
使って解析してください.

## 各形式と `Feed` の対応

パーサは形式ごとの要素名を上記のフィールド名へ書き換えるため,配信元によらず同じ `Feed` が
得られます.

| 元の要素 | `Feed` のフィールド | 形式 |
|--|--|--|
| `<title>` | `title` | 全形式 |
| `<link>` | `link` | RSS 1.0, RSS 2.0 |
| `<link rel="alternate" type="text/html" href="...">` | `link` | Atom |
| `<description>` | `description` | 全形式 |
| `<summary>` | `summary` | 全形式 |
| `<updated>` | `updated` | 全形式 |
| `<pubDate>` | `publish_date` | RSS 1.0, RSS 2.0, Atom |
| `<published>` | `publish_date` | Atom |
| `<dc:creator>` | `creator` | 全形式 |
| `<dc:date>` | `date` | 全形式 |

Atom は1つのエントリに複数の `<link>` を持つため,パーサはエントリ自体を指すもの,すなわち
`rel="alternate"` かつ `type="text/html"` のものを選びます.異なる関係やメディアタイプを
宣言したリンク(エンクロージャ,自己参照,別表現)は読み飛ばします.

## テキスト要素に埋め込まれた未エスケープの HTML

フィードは `<title>`,`<description>`,`<summary>`,`<content>` に生の HTML を
エスケープせず埋め込むことが多く,その結果 XML として不正な文書になります.各パーサは
読み込みの前にこれらの要素の内容をエスケープし,マークアップがテキストとして残るようにします.

```rust
use feed_parser::parsers::rss2;

let rss_data = r#"
<rss version="2.0">
    <channel>
        <item>
            <title>Rust 1.85 <b>released</b></title>
            <link>http://www.example.com/item1.html</link>
        </item>
    </channel>
</rss>
"#;

let feeds = rss2::parse(rss_data).unwrap();
assert_eq!(feeds[0].title, "Rust 1.85 <b>released</b>");
```

`CDATA` セクションの内容は前後の空白も含めてそのまま保持され,要素まわりのインデントは
取り除かれます.Atom ではさらにテキストノードの HTML 実体参照をデコードします.Atom の
配信元は内容を二重エスケープしていることが多いためです.

このエスケープは同一行の範囲で一致します.これらの要素の中で開始タグと終了タグが別の行に
またがるマークアップはそのまま残り,実際のマークアップとしてリーダに渡るため,文字列では
なく [`ParseError::DeserializeError`](errors.ja.md) になります.同じ1行という制約は
`CDATA` にも当てはまり,他のテキストと同じ行に書かれたセクションは `<![CDATA[` と `]]>`
のマーカーごとエスケープされて値に含まれます.