corobel/src/syndication.rs

132 lines
4.3 KiB
Rust

use crate::cohost_account::CohostAccount;
use crate::cohost_posts::*;
use crate::ARGS;
use atom_syndication::LinkBuilder;
use rss::extension::atom::{AtomExtensionBuilder, Link};
use rss::Channel;
fn rel_link_for(rel: &str, project_name: &str, page_number: u64) -> Link {
LinkBuilder::default()
.rel(rel)
.href(format!(
"https://{}{}{}/feed.rss?page={}",
&ARGS.domain,
&ARGS.base_url,
project_name.clone(),
page_number
))
.build()
}
pub fn channel_for_posts_page(
project_name: impl AsRef<str>,
page_number: u64,
project: CohostAccount,
mut page: CohostPostsPage,
) -> Channel {
let project_name = project_name.as_ref().clone();
let mut builder = rss::ChannelBuilder::default();
builder
.title(format!("{} Cohost Posts", project.display_name))
.description(project.description)
.generator(Some(format!(
"{} {}",
env!("CARGO_CRATE_NAME"),
env!("CARGO_PKG_VERSION")
)))
.link(format!(
"https://cohost.org/{}?page={}",
project_name, page_number
));
let mut atom = AtomExtensionBuilder::default();
let mut links = vec![
rel_link_for("self", project_name, page_number),
rel_link_for("first", project_name, 0),
];
if page_number > 0 {
links.push(rel_link_for("previous", project_name, page_number - 1));
}
if page.number_items > 0 {
// Cohost API is wrong about the pagination so we can only guess. If there are posts, there might be more posts.
links.push(rel_link_for("next", project_name, page_number + 1));
}
atom.links(links);
builder.atom_ext(Some(atom.build()));
page.items.sort_by_key(|item| item.published_at);
let mut items = Vec::with_capacity(page.number_items);
for item in page.items {
let mut item_builder = rss::ItemBuilder::default();
let mut categories: Vec<rss::Category> = Vec::with_capacity(item.tags.len());
for tag in item.tags.iter().cloned() {
categories.push(rss::Category {
name: tag,
domain: Some("https://cohost.org".into()),
});
}
item_builder
.link(item.url.clone())
.title(item.headline)
.author(item.poster.handle)
.guid(Some(rss::Guid {
value: item.url.clone(),
permalink: true,
}))
.categories(categories)
.pub_date(item.published_at.to_rfc2822())
.source(Some(rss::Source {
title: Some(format!("{} Cohost Posts", project.display_name)),
url: format!("https://{}/feed/{}.rss", ARGS.domain, project_name),
}));
let mut body_text = String::new();
if item.share_tree.len() == 1 {
body_text.push_str("(in reply to another post)\n---\n")
} else if item.share_tree.len() > 1 {
body_text.push_str(&format!(
"(in reply to {} other posts)\n---\n",
item.share_tree.len()
));
}
if item.cws.is_empty() {
body_text.push_str(&item.plain_body);
} else {
body_text.push_str("Sensitive post, body text omitted. Content warnings:{}");
for cw in item.cws {
body_text.push_str(&format!(" {},", cw));
}
body_text.pop(); // Remove trailing comma
body_text.push_str("\n---\n")
};
if !item.tags.is_empty() {
body_text.push_str("\n\n Post tagged:");
for tag in item.tags {
body_text.push_str(&format!(" #{},", tag));
}
body_text.pop(); // Remove trailing comma
}
use pulldown_cmark::Options;
let options = Options::ENABLE_FOOTNOTES
& Options::ENABLE_STRIKETHROUGH
& Options::ENABLE_TABLES
& Options::ENABLE_TASKLISTS;
let parser = pulldown_cmark::Parser::new_ext(&body_text, options);
let mut html_output = String::new();
pulldown_cmark::html::push_html(&mut html_output, parser);
item_builder.content(html_output);
items.push(item_builder.build());
}
builder.items(items);
builder.build()
}