feed_parser/parsers/rss2/
mod.rs1use crate::parsers::{
2 Feed,
3 errors::{ParseError, ParseResult},
4 internal::{TextRun, compile_tag_patterns, deserialize_feed, escape_inline_html},
5};
6use core::str;
7use quick_xml::Reader;
8use quick_xml::Writer;
9use quick_xml::events::{BytesEnd, BytesStart, Event};
10use regex::Regex;
11use std::io::Cursor;
12use std::sync::LazyLock;
13
14#[cfg(test)]
15mod tests;
16
17static HTML_BEARING_TAGS: LazyLock<Vec<(&'static str, Regex)>> = LazyLock::new(|| {
19 compile_tag_patterns(&["title", "description", "summary", "content", "other"])
20});
21
22pub fn parse(text: &str) -> ParseResult<Vec<Feed>> {
30 let text = escape_inline_html(text, &HTML_BEARING_TAGS);
31
32 let mut reader = Reader::from_str(&text);
33
34 let mut feeds = Vec::new();
35 let mut writer = Writer::new(Cursor::new(Vec::new()));
36 let mut run = TextRun::default();
39 let mut parsing = false;
40 loop {
41 match reader.read_event()? {
42 Event::Start(e) => {
43 run.flush(&mut writer)?;
44 if parsing {
45 if e.name().as_ref() == b"dc:creator" {
46 writer.write_event(Event::Start(BytesStart::new("creator")))?;
47 } else if e.name().as_ref() == b"dc:date" {
48 writer.write_event(Event::Start(BytesStart::new("date")))?;
49 } else if e.name().as_ref() == b"pubDate" {
50 writer.write_event(Event::Start(BytesStart::new("publish_date")))?;
51 } else {
52 writer.write_event(Event::Start(e.clone()))?;
53 }
54 }
55 if e.name().as_ref() == b"item" {
56 writer.write_event(Event::Start(BytesStart::new("item")))?;
57 parsing = true;
58 }
59 }
60 Event::End(e) => {
61 run.flush(&mut writer)?;
62 if parsing && e.name().as_ref() == b"item" {
63 writer.write_event(Event::End(BytesEnd::new("item")))?;
64 let feed_text = writer.into_inner().into_inner();
65 feeds.push(deserialize_feed(str::from_utf8(&feed_text)?)?);
66
67 writer = Writer::new(Cursor::new(Vec::new()));
68 parsing = false;
69 } else if parsing {
70 if e.name().as_ref() == b"dc:creator" {
71 writer.write_event(Event::End(BytesEnd::new("creator")))?;
72 } else if e.name().as_ref() == b"dc:date" {
73 writer.write_event(Event::End(BytesEnd::new("date")))?;
74 } else if e.name().as_ref() == b"pubDate" {
75 writer.write_event(Event::End(BytesEnd::new("publish_date")))?;
76 } else {
77 writer.write_event(Event::End(e))?;
78 }
79 }
80 }
81 Event::Text(e) => {
82 if parsing {
83 run.push(Event::Text(e));
84 }
85 }
86 Event::CData(e) => {
87 if parsing {
88 run.push(Event::CData(e));
89 }
90 }
91 Event::GeneralRef(e) => {
94 if parsing {
95 run.push(Event::GeneralRef(e));
96 }
97 }
98 Event::Eof => break,
99 _ => {}
100 }
101 }
102
103 if parsing {
104 return Err(ParseError::InvalidFeedFormat(
105 "reached end of document inside an unclosed <item> element".to_string(),
106 ));
107 }
108
109 Ok(feeds)
110}