Skip to main content

systemprompt_generator/rss/
xml.rs

1use chrono::{DateTime, Utc};
2
3#[derive(Debug, Clone)]
4pub struct RssItem {
5    pub title: String,
6    pub link: String,
7    pub description: String,
8    pub pub_date: DateTime<Utc>,
9    pub guid: String,
10    pub author: Option<String>,
11}
12
13#[derive(Debug, Clone)]
14pub struct RssChannel {
15    pub title: String,
16    pub link: String,
17    pub description: String,
18    pub items: Vec<RssItem>,
19}
20
21pub fn escape_xml(s: &str) -> String {
22    s.replace('&', "&amp;")
23        .replace('<', "&lt;")
24        .replace('>', "&gt;")
25        .replace('"', "&quot;")
26        .replace('\'', "&apos;")
27}
28
29pub fn format_rfc2822(dt: &DateTime<Utc>) -> String {
30    dt.format("%a, %d %b %Y %H:%M:%S +0000").to_string()
31}
32
33pub fn build_rss_xml(channel: &RssChannel) -> String {
34    let mut xml = String::with_capacity(8192);
35
36    xml.push_str(r#"<?xml version="1.0" encoding="UTF-8"?>"#);
37    xml.push('\n');
38    xml.push_str(r#"<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">"#);
39    xml.push('\n');
40    xml.push_str("<channel>\n");
41
42    xml.push_str(&format!(
43        "  <title>{}</title>\n",
44        escape_xml(&channel.title)
45    ));
46    xml.push_str(&format!("  <link>{}</link>\n", escape_xml(&channel.link)));
47    xml.push_str(&format!(
48        "  <description>{}</description>\n",
49        escape_xml(&channel.description)
50    ));
51    xml.push_str(&format!(
52        r#"  <atom:link href="{}/feed.xml" rel="self" type="application/rss+xml"/>"#,
53        escape_xml(&channel.link)
54    ));
55    xml.push('\n');
56
57    for item in &channel.items {
58        xml.push_str("  <item>\n");
59        xml.push_str(&format!("    <title>{}</title>\n", escape_xml(&item.title)));
60        xml.push_str(&format!("    <link>{}</link>\n", escape_xml(&item.link)));
61        xml.push_str(&format!(
62            "    <description>{}</description>\n",
63            escape_xml(&item.description)
64        ));
65        xml.push_str(&format!(
66            "    <pubDate>{}</pubDate>\n",
67            format_rfc2822(&item.pub_date)
68        ));
69        xml.push_str(&format!(
70            "    <guid isPermaLink=\"true\">{}</guid>\n",
71            escape_xml(&item.guid)
72        ));
73        if let Some(ref author) = item.author {
74            xml.push_str(&format!("    <author>{}</author>\n", escape_xml(author)));
75        }
76        xml.push_str("  </item>\n");
77    }
78
79    xml.push_str("</channel>\n");
80    xml.push_str("</rss>\n");
81
82    xml
83}