feed-parser 2.0.0

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

# Usage

## The three parsers

Each supported format has its own module, and every one of them exposes the same
entry point:

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

| Module | Format | Entry element |
|--|--|--|
| `feed_parser::parsers::rss1` | RSS 1.0 (RDF) | `<item>` |
| `feed_parser::parsers::rss2` | RSS 2.0 | `<item>` |
| `feed_parser::parsers::atom` | Atom | `<entry>` |

`parse` takes the whole document and returns one `Feed` per entry element, in document
order. Channel-level metadata is not returned; the result describes the entries.

```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>(())
```

The format is not detected automatically. Pick the module that matches the source, or
try them in turn and keep the first success.

## The `Feed` type

```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` and `link` are required: an entry that lacks either cannot become a `Feed`, and
the parser returns [`ParseError::MissingField`](errors.md). Everything else is optional
and absent when the source omits it.

Dates are kept as the raw strings the feed published. Feeds disagree about format —
RFC 822 in RSS 2.0, ISO 8601 in Atom, and plenty of sources follow neither — so the
crate does not impose one interpretation. Parse them with `chrono` or `time` in the
calling code if you need a real timestamp.

## How each format maps onto `Feed`

The parsers rewrite format-specific element names into the field names above, so the
same `Feed` comes out regardless of source.

| Source element | `Feed` field | Formats |
|--|--|--|
| `<title>` | `title` | all |
| `<link>` | `link` | RSS 1.0, RSS 2.0 |
| `<link rel="alternate" type="text/html" href="...">` | `link` | Atom |
| `<description>` | `description` | all |
| `<summary>` | `summary` | all |
| `<updated>` | `updated` | all |
| `<pubDate>` | `publish_date` | RSS 1.0, RSS 2.0, Atom |
| `<published>` | `publish_date` | Atom |
| `<dc:creator>` | `creator` | all |
| `<dc:date>` | `date` | all |

Atom carries several `<link>` elements per entry, so the parsers select the one that
represents the entry itself: `rel="alternate"` with `type="text/html"`. Links that
declare a different relation or media type — enclosures, self-references, alternate
representations — are skipped.

## Unescaped HTML in text elements

Feeds regularly embed raw HTML directly in `<title>`, `<description>`, `<summary>` and
`<content>` without escaping it, which makes the document ill-formed XML. Before
reading, each parser escapes the content of those elements so the markup survives as
text rather than derailing the reader.

```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>");
```

The content of a `CDATA` section is kept verbatim, down to its leading and trailing
whitespace, while the indentation around an element is trimmed. Atom additionally
decodes HTML entities in text nodes, since Atom sources commonly double-escape their
content.

This escaping matches within a single line. Markup that opens on one line and closes on
another inside one of these elements is left alone, reaches the reader as real markup,
and produces [`ParseError::DeserializeError`](errors.md) rather than a string. The same
one-line limit applies to `CDATA`: a section written inline alongside other text on the
same line has its `<![CDATA[` and `]]>` markers escaped into the value.