aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar cel 🌸 <cel@blos.sm>2023-06-22 21:37:10 +0100
committerLibravatar cel 🌸 <cel@blos.sm>2023-06-22 21:37:10 +0100
commitab529917847641190f21129804bbda1784b4f998 (patch)
treedfdd39286d330dd62339ade0499471b70187ae04 /src
parentbc5ac494c6162743810b9aeb1cda4f22d94a486a (diff)
downloadblossom-ab529917847641190f21129804bbda1784b4f998.tar.gz
blossom-ab529917847641190f21129804bbda1784b4f998.tar.bz2
blossom-ab529917847641190f21129804bbda1784b4f998.zip
implement atom syndication
Diffstat (limited to 'src')
-rw-r--r--src/error.rs16
-rw-r--r--src/main.rs16
-rw-r--r--src/posts/mod.rs1
-rw-r--r--src/posts/syndication.rs96
4 files changed, 127 insertions, 2 deletions
diff --git a/src/error.rs b/src/error.rs
index eb6f6e6..42c01b1 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,3 +1,5 @@
+use std::string::FromUtf8Error;
+
use rocket::{http::Status, Responder};
#[derive(Responder, Debug)]
@@ -8,6 +10,8 @@ pub enum BlossomError {
Chrono(Status, #[response(ignore)] chrono::ParseError),
Io(Status, #[response(ignore)] std::io::Error),
Deserialization(Status, #[response(ignore)] toml::de::Error),
+ Syndicator(Status, #[response(ignore)] atom_syndication::Error),
+ Utf8Conversion(Status, #[response(ignore)] FromUtf8Error),
NotFound(Status),
NoMetadata(Status),
Unimplemented(Status),
@@ -48,3 +52,15 @@ impl From<toml::de::Error> for BlossomError {
BlossomError::Deserialization(Status::new(500), e)
}
}
+
+impl From<atom_syndication::Error> for BlossomError {
+ fn from(e: atom_syndication::Error) -> Self {
+ BlossomError::Syndicator(Status::new(500), e)
+ }
+}
+
+impl From<FromUtf8Error> for BlossomError {
+ fn from(e: FromUtf8Error) -> Self {
+ BlossomError::Utf8Conversion(Status::new(500), e)
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index 40f546d..844f3aa 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,8 +7,9 @@ mod skweets;
use std::borrow::Cow;
use std::collections::HashSet;
+use atom_syndication::Feed;
use rocket::fs::{relative, FileServer};
-use rocket::http::Status;
+use rocket::http::{ContentType, Status};
use rocket::{Request, State};
use rocket_dyn_templates::{context, Template};
@@ -87,6 +88,17 @@ async fn blog(filter: Vec<String>) -> Result<Template> {
))
}
+#[get("/feed")]
+async fn feed() -> Result<(Status, (ContentType, String))> {
+ let posts = posts::get_blogposts().await?;
+ let feed = posts::syndication::atom(posts).await;
+ let feed: String = String::from_utf8(feed.write_to(Vec::new())?)?;
+ Ok((
+ Status::new(200),
+ (ContentType::new("application", "atom+xml"), feed),
+ ))
+}
+
#[get("/contact")]
async fn contact() -> Template {
Template::render("contact", context! {})
@@ -130,7 +142,7 @@ async fn main() -> std::result::Result<(), rocket::Error> {
.attach(Template::custom(|engines| {
engines.tera.autoescape_on(vec![]);
}))
- .mount("/", routes![home, contact, blog, blogpost, plants])
+ .mount("/", routes![home, contact, blog, blogpost, feed, plants])
.register("/", catchers![catcher])
.mount("/", FileServer::from(relative!("static")))
.launch()
diff --git a/src/posts/mod.rs b/src/posts/mod.rs
index 2dc5c4b..7f8e6c4 100644
--- a/src/posts/mod.rs
+++ b/src/posts/mod.rs
@@ -1,5 +1,6 @@
mod article;
mod note;
+pub mod syndication;
use std::collections::HashSet;
diff --git a/src/posts/syndication.rs b/src/posts/syndication.rs
new file mode 100644
index 0000000..f6f0b17
--- /dev/null
+++ b/src/posts/syndication.rs
@@ -0,0 +1,96 @@
+use atom_syndication::{Category, Content, Entry, Feed, Generator, Link, Person, Text, TextType};
+
+use super::{Article, Post};
+
+pub async fn atom(posts: Vec<Post<Article>>) -> Feed {
+ let me = Person {
+ name: "cel".into(),
+ email: Some("cel@blos.sm".into()),
+ uri: Some("https://blos.sm".into()),
+ };
+ let mut authors = Vec::new();
+ authors.push(me);
+ let link = Link {
+ href: "https://blos.sm/feed".into(),
+ rel: "self".into(),
+ hreflang: Some("en".into()),
+ mime_type: Some("application/atom+xml".into()),
+ title: Some("atom feed".into()),
+ length: None,
+ };
+ let mut links = Vec::new();
+ links.push(link);
+ let mut feed = Feed {
+ title: Text {
+ value: "cel's site".into(),
+ base: None,
+ lang: Some("en".into()),
+ r#type: TextType::Text,
+ },
+ id: "https://blos.sm".into(),
+ updated: posts[0].created_at.into(),
+ authors: authors.clone(),
+ categories: Vec::new(),
+ contributors: authors.clone(),
+ generator: Some(Generator {
+ value: "blos.sm".into(),
+ uri: Some("https://bunny.garden/cel/blos.sm".into()),
+ version: None,
+ }),
+ icon: Some("/icon.png".into()),
+ links: links.clone(),
+ logo: Some("/logo.png".into()),
+ rights: None,
+ subtitle: None,
+ entries: Vec::new(),
+ base: Some("https://blos.sm".into()),
+ lang: Some("en".into()),
+ ..Default::default()
+ };
+ for mut post in posts {
+ post.render().await.unwrap_or_default();
+ let mut id = String::from("https://blos.sm/blog/");
+ id.push_str(&post.data.name);
+ let categories = post
+ .tags
+ .into_iter()
+ .map(|tag| Category {
+ term: tag.clone(),
+ scheme: None,
+ label: Some(tag.clone()),
+ })
+ .collect();
+ let entry = Entry {
+ title: Text {
+ value: post.subject.unwrap_or_default(),
+ base: None,
+ lang: Some("en".into()),
+ r#type: TextType::Text,
+ },
+ id: id.clone(),
+ updated: if let Some(updated_at) = post.updated_at {
+ updated_at.into()
+ } else {
+ post.created_at.into()
+ },
+ authors: authors.clone(),
+ categories,
+ contributors: authors.clone(),
+ links: links.clone(),
+ published: Some(post.created_at.into()),
+ rights: None,
+ source: None,
+ summary: None,
+ content: Some(Content {
+ base: None,
+ lang: Some("en".into()),
+ value: post.render,
+ src: Some(id),
+ content_type: Some("html".to_string()),
+ }),
+ ..Default::default()
+ };
+ feed.entries.push(entry);
+ }
+ feed
+}