feed-parser 2.0.0

A simple RSS 1.0 / RSS 2.0 / Atom feed parser
Documentation
//! Text content must survive the reader/writer round trip intact.
//!
//! quick-xml reports entity references as their own event and splits the
//! surrounding character data, so escaped characters and the whitespace next to
//! them are easy to lose without noticing. These tests pin down both.

use feed_parser::parsers::{atom, rss1, rss2};

#[test]
fn test_unescaped_html_in_title_is_preserved() {
    let xml = 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(xml).unwrap();
    assert_eq!(feeds[0].title, "Rust 1.85 <b>released</b>");
}

#[test]
fn test_unescaped_html_in_description_is_preserved() {
    let xml = r#"<rss version="2.0"><channel><item>
        <title>T</title>
        <link>L</link>
        <description>see <a href="x">here</a> now</description>
    </item></channel></rss>"#;
    let feeds = rss2::parse(xml).unwrap();
    assert_eq!(
        feeds[0].description.clone().unwrap(),
        r#"see <a href="x">here</a> now"#
    );
}

#[test]
fn test_adjacent_whitespace_survives_an_entity() {
    // The space before `<b>` sits at the boundary between a text fragment and an
    // entity reference; trimming per fragment would swallow it.
    let xml = r#"<rss version="2.0"><channel><item>
        <title>a <b>c</b> d</title>
        <link>L</link>
    </item></channel></rss>"#;
    let feeds = rss2::parse(xml).unwrap();
    assert_eq!(feeds[0].title, "a <b>c</b> d");
}

#[test]
fn test_surrounding_indentation_is_still_trimmed() {
    let xml = "<rss version=\"2.0\"><channel><item>\n    <title>\n        Item 1\n    </title>\n    <link>L</link>\n</item></channel></rss>";
    let feeds = rss2::parse(xml).unwrap();
    assert_eq!(feeds[0].title, "Item 1");
}

#[test]
fn test_cdata_content_is_verbatim() {
    let xml = "<rss version=\"2.0\"><channel><item>\n<title>T</title>\n<link>L</link>\n<description>\n<![CDATA[<p>hi</p> ]]>\n</description>\n</item></channel></rss>";
    let feeds = rss2::parse(xml).unwrap();
    // The trailing space belongs to the CDATA section and must not be trimmed.
    assert_eq!(feeds[0].description.clone().unwrap(), "<p>hi</p> ");
}

#[test]
fn test_nested_tags_in_description() {
    let xml = r#"<rss version="2.0"><channel><item>
        <title>T</title>
        <link>L</link>
        <description><p>one</p><p>two</p></description>
    </item></channel></rss>"#;
    let feeds = rss2::parse(xml).unwrap();
    assert_eq!(
        feeds[0].description.clone().unwrap(),
        "<p>one</p><p>two</p>"
    );
}

#[test]
fn test_rss1_preserves_html() {
    let xml = r#"<rdf:RDF><item>
        <title>a <b>c</b> d</title>
        <link>L</link>
        <dc:creator>Alice</dc:creator>
    </item></rdf:RDF>"#;
    let feeds = rss1::parse(xml).unwrap();
    assert_eq!(feeds[0].title, "a <b>c</b> d");
    assert_eq!(feeds[0].creator.clone().unwrap(), "Alice");
}

#[test]
fn test_atom_preserves_html() {
    let xml = r#"<feed><entry>
        <title>Rust <b>1.85</b></title>
        <link rel="alternate" type="text/html" href="http://x/p"/>
    </entry></feed>"#;
    let feeds = atom::parse(xml).unwrap();
    assert_eq!(feeds[0].title, "Rust <b>1.85</b>");
    assert_eq!(feeds[0].link, "http://x/p");
}