Skip to content
Snippets Groups Projects
Commit 4ea3f439 authored by Stephen D's avatar Stephen D
Browse files

rss feed

parent dca27bd5
No related branches found
No related tags found
No related merge requests found
use std::path::Path;
use std::path::PathBuf;
use anyhow::{bail, Error};
use fnv::FnvHashMap;
use warp::hyper::StatusCode;
use crate::{
config::Logo, favicon::Favicon, image::MyImage, load::load_from_path, misc::Misc,
config::Config, favicon::Favicon, image::MyImage, load::load_from_path, misc::Misc,
path::UrlPath, post::Post, resp::Response, style::Style,
};
pub struct Blog {
name: String,
description: String,
config: Config,
pages: Vec<Post>,
posts: Vec<Post>,
......@@ -19,39 +18,33 @@ pub struct Blog {
misc: Vec<Misc>,
custom_style: Option<Style>,
favicon: Option<Favicon>,
logo: Option<Logo>,
}
impl Blog {
pub fn new(
name: String,
description: String,
base_path: &Path,
logo: Option<Logo>,
) -> Result<Self, Error> {
let pages = load_from_path(base_path, &base_path.join("pages"), "pages")?;
let posts = load_from_path(base_path, &base_path.join("posts"), "posts")?;
let imgs = load_from_path(base_path, &base_path.join("assets/img"), "")?;
let misc = load_from_path(base_path, &base_path.join("assets/misc"), "")?;
pub fn new(config: Config) -> Result<Self, Error> {
let base_path = PathBuf::from(&config.path);
let pages = load_from_path(&base_path, &base_path.join("pages"), "pages")?;
let posts = load_from_path(&base_path, &base_path.join("posts"), "posts")?;
let imgs = load_from_path(&base_path, &base_path.join("assets/img"), "")?;
let misc = load_from_path(&base_path, &base_path.join("assets/misc"), "")?;
let custom_style = Style::load_if_exists(base_path.join("assets/style.css"))?;
let favicon = Favicon::load_if_exists(base_path.join("assets/favicon.png"))?;
Ok(Self {
name,
description,
config,
pages,
posts,
imgs,
misc,
custom_style,
favicon,
logo,
})
}
fn home(&mut self) -> Result<String, Error> {
let name = &self.name;
let description = &self.description;
fn home(&self) -> anyhow::Result<String> {
let name = &self.config.name;
let description = &self.config.description;
let mut content = format!(
r#"<div class="centered"><h1>{name}</h1>{description}</div><h2>Recent Posts</h2><table>"#
......@@ -66,10 +59,32 @@ impl Blog {
Ok(self.dress_page(None, &content))
}
fn rss(&self) -> anyhow::Result<String> {
let mut out = format!(
concat!(
r#"<?xml version="1.0" encoding="UTF-8" ?>"#,
r#"<rss version="2.0">"#,
"<channel>",
"<title>{}</title>",
"<link>{}</link>",
"<description>{}</description>"
),
self.config.name, self.config.root, self.config.description
);
for p in &self.posts {
out.push_str(&p.rss(&self.config.root)?);
}
out.push_str("</channel></rss>");
Ok(out)
}
fn dress_page(&self, title: Option<&str>, content: &str) -> String {
let title = match title {
Some(title) => format!("{} | {}", title.trim(), self.name.trim()),
None => self.name.trim().to_string(),
Some(title) => format!("{} | {}", title.trim(), self.config.name.trim()),
None => self.config.name.trim().to_string(),
};
let favicons = match &self.favicon {
......@@ -77,7 +92,7 @@ impl Blog {
None => "",
};
let logo = match &self.logo {
let logo = match &self.config.logo {
Some(logo) => format!(
r#"<img src="{}" width="{}" height="{}" alt="{}" class="align-right" />"#,
logo.path, logo.width, logo.height, logo.alt
......@@ -152,6 +167,8 @@ impl TryFrom<Blog> for RenderedBlog {
insert_path(&mut pages, &m.url.clone(), m.clone().response())?;
}
insert_path(&mut pages, "/posts.rss", Response::rss(b.rss()?))?;
let not_found =
Response::html(b.dress_page(Some("Page not found"), include_str!("assets/404.html")));
......
......@@ -16,6 +16,7 @@ pub struct Config {
pub name: String,
pub description: String,
pub path: String,
pub root: String,
pub logo: Option<Logo>,
}
......
use config::Config;
use std::{path::PathBuf, sync::Arc};
use std::sync::Arc;
use tokio::sync::RwLock;
use blog::{Blog, RenderedBlog};
......@@ -53,15 +53,7 @@ async fn handle_request(
async fn main() {
let config = Config::load().unwrap();
let blog: RenderedBlog = Blog::new(
config.name,
config.description,
&PathBuf::from(config.path),
config.logo,
)
.unwrap()
.try_into()
.unwrap();
let blog: RenderedBlog = Blog::new(config).unwrap().try_into().unwrap();
let blog = Arc::new(RwLock::new(blog));
let blog_filter = warp::any().map(move || blog.clone());
......
......@@ -15,6 +15,16 @@ impl UrlPath {
Self { parts }
}
pub fn new_from_raw(s: String) -> Self {
Self { parts: vec![s] }
}
pub fn join(mut self, append: &str) -> Self {
self.parts.extend(UrlPath::new(append).parts);
self
}
}
impl Display for UrlPath {
......
use std::{cmp::Ordering, fs, path::Path};
use anyhow::{bail, Context};
use chrono::NaiveDate;
use chrono::{DateTime, NaiveDate, Utc};
use orgize::{elements::Keyword, export::HtmlHandler, Element};
use crate::{
date::parse_org_date,
load::Loadable,
parser::{parse, CustomHtmlHandler, Parser},
path::UrlPath,
util::slugify,
};
......@@ -40,6 +41,29 @@ impl Post {
Ok(out)
}
pub fn rss(&self, root_url: &str) -> anyhow::Result<String> {
let title = &self.title;
let link = UrlPath::new_from_raw(root_url.to_string()).join(&self.url);
let date = match self.date {
Some(d) => {
format!(
"<pubDate>{}</pubDate>",
DateTime::<Utc>::from_utc(
d.and_hms_opt(0, 0, 0)
.context("Could not convert date to datetime")?,
Utc
)
.to_rfc2822()
)
}
None => "".to_string(),
};
Ok(format!(
"<item><title>{title}</title><link>{link}</link><guid>{link}</guid>{date}</item>"
))
}
pub fn link(&self) -> anyhow::Result<String> {
let link = match self.date_f()? {
Some(date_f) => format!(
......
......@@ -33,6 +33,14 @@ impl Response {
}
}
pub fn rss(rss: String) -> Self {
Self {
content_type: "text/xml; charset=utf-8",
expiry: Expiry::Instant,
data: rss.into_bytes(),
}
}
pub fn css(css: String) -> Self {
Self {
content_type: "text/css; charset=utf-8",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment