cargo fmt
All checks were successful
Fly Deploy / Deploy app (push) Successful in 2m16s

This commit is contained in:
Gabriel Simmer 2023-09-26 23:56:54 +01:00
parent 152ccf8ba5
commit 692e765561
Signed by: arch
SSH key fingerprint: SHA256:m3OEcdtrnBpMX+2BDGh/byv3hrCekCLzDYMdvGEKPPQ

View file

@ -15,8 +15,9 @@ use axum::{
use clap::Parser; use clap::Parser;
use file_format::{FileFormat, Kind}; use file_format::{FileFormat, Kind};
use hyper::body::Bytes; use hyper::body::Bytes;
use maud::{html, Markup, DOCTYPE, PreEscaped, Render}; use maud::{html, Markup, PreEscaped, Render, DOCTYPE};
use orgize::Org; use orgize::Org;
use rss::ChannelBuilder;
use serde::Deserialize; use serde::Deserialize;
use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions}; use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions};
use sqlx::{FromRow, Pool, Sqlite}; use sqlx::{FromRow, Pool, Sqlite};
@ -25,10 +26,9 @@ use std::fs::{self, File};
use std::io::prelude::*; use std::io::prelude::*;
use std::str::FromStr; use std::str::FromStr;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use time::{self, format_description, format_description::well_known::Rfc2822};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
use rss::ChannelBuilder;
use time::{self, format_description, format_description::well_known::Rfc2822};
lazy_static! { lazy_static! {
static ref CACHE: Mutex<HashMap<String, CachedPage>> = Mutex::new(HashMap::new()); static ref CACHE: Mutex<HashMap<String, CachedPage>> = Mutex::new(HashMap::new());
@ -76,22 +76,22 @@ struct ProjectConfig {
struct Post { struct Post {
name: String, name: String,
route: String, route: String,
date: String, date: String,
} }
impl Render for Project { impl Render for Project {
fn render(&self) -> Markup { fn render(&self) -> Markup {
html! { html! {
div .project { div .project {
h4 { ( self.name ) " " } h4 { ( self.name ) " " }
@for language in &self.languages { @for language in &self.languages {
span .language .(language.to_lowercase()) { ( language ) } span .language .(language.to_lowercase()) { ( language ) }
} }
p { ( self.description ) " " a href=(self.link.link) { (self.link.display_text) }} p { ( self.description ) " " a href=(self.link.link) { (self.link.display_text) }}
} }
} }
} }
} }
#[tokio::main] #[tokio::main]
@ -109,7 +109,7 @@ async fn main() -> Result<(), sqlx::Error> {
let app = Router::new() let app = Router::new()
.route("/", get(homepage)) .route("/", get(homepage))
.route("/rss", get(rss)) .route("/rss", get(rss))
.route("/blog", get(list_blog_posts)) .route("/blog", get(list_blog_posts))
.route("/blog/:post", get(blog_post)) .route("/blog/:post", get(blog_post))
.nest_service("/assets", ServeDir::new("assets")) .nest_service("/assets", ServeDir::new("assets"))
@ -127,7 +127,7 @@ async fn main() -> Result<(), sqlx::Error> {
} }
fn get_posts() -> Vec<Post> { fn get_posts() -> Vec<Post> {
let mut posts: Vec<Post> = Vec::new(); let mut posts: Vec<Post> = Vec::new();
for entry in fs::read_dir("./posts").unwrap() { for entry in fs::read_dir("./posts").unwrap() {
let entry = entry.unwrap(); let entry = entry.unwrap();
let path = entry.path(); let path = entry.path();
@ -143,8 +143,8 @@ fn get_posts() -> Vec<Post> {
content.read(&mut buffer).unwrap(); content.read(&mut buffer).unwrap();
// Match date data of `date: YYYY-MM-DD` in the first 100 bytes // Match date data of `date: YYYY-MM-DD` in the first 100 bytes
let metadata = String::from_utf8_lossy(&buffer); let metadata = String::from_utf8_lossy(&buffer);
let metadata_lines = metadata.split("\n").collect::<Vec<&str>>(); let metadata_lines = metadata.split("\n").collect::<Vec<&str>>();
// dbg!(&metadata); // dbg!(&metadata);
// Split by --- and get the second element // Split by --- and get the second element
let date = metadata_lines let date = metadata_lines
.iter() .iter()
@ -152,28 +152,29 @@ fn get_posts() -> Vec<Post> {
.unwrap_or(&"") .unwrap_or(&"")
.split(":") .split(":")
.collect::<Vec<&str>>()[1]; .collect::<Vec<&str>>()[1];
let title = metadata_lines let title = metadata_lines
.iter() .iter()
.find(|&x| x.contains("title:")) .find(|&x| x.contains("title:"))
.unwrap_or(&"") .unwrap_or(&"")
.split(":") .split(":")
.collect::<Vec<&str>>()[1].trim(); .collect::<Vec<&str>>()[1]
.trim();
let date = date.trim(); let date = date.trim();
posts.push(Post { posts.push(Post {
name: title.to_owned(), name: title.to_owned(),
route: fname, route: fname,
date: date.to_owned(), date: date.to_owned(),
}); });
} }
} }
posts.sort_by(|a, b| b.date.cmp(&a.date)); posts.sort_by(|a, b| b.date.cmp(&a.date));
posts posts
} }
async fn rss() -> Result<impl IntoResponse, StatusCode> { async fn rss() -> Result<impl IntoResponse, StatusCode> {
let posts = get_posts(); let posts = get_posts();
let rss_posts: Vec<rss::Item> = posts.into_iter().map(|p| { let rss_posts: Vec<rss::Item> = posts.into_iter().map(|p| {
let date = format!("{} 00:00:00 +00:00:00", p.date); let date = format!("{} 00:00:00 +00:00:00", p.date);
let format = format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour sign:mandatory]:[offset_minute]:[offset_second]").unwrap(); let format = format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour sign:mandatory]:[offset_minute]:[offset_second]").unwrap();
let pub_date = match time::OffsetDateTime::parse(&date, &format).unwrap().format(&Rfc2822) { let pub_date = match time::OffsetDateTime::parse(&date, &format).unwrap().format(&Rfc2822) {
@ -186,28 +187,28 @@ async fn rss() -> Result<impl IntoResponse, StatusCode> {
.pub_date(Some(pub_date)) .pub_date(Some(pub_date))
.build() .build()
}).collect(); }).collect();
let channel = ChannelBuilder::default() let channel = ChannelBuilder::default()
.title("Gabriel Simmer's Blog".to_owned()) .title("Gabriel Simmer's Blog".to_owned())
.link("https://gabrielsimmer.com/blog".to_owned()) .link("https://gabrielsimmer.com/blog".to_owned())
.description("Gabriel Simmer's Blog Posts.".to_owned()) .description("Gabriel Simmer's Blog Posts.".to_owned())
.items(rss_posts) .items(rss_posts)
.build(); .build();
return Ok(Response::builder() return Ok(Response::builder()
.header("content-type", "application/rss+xml") .header("content-type", "application/rss+xml")
.status(StatusCode::OK) .status(StatusCode::OK)
.body(Full::from(channel.to_string())) .body(Full::from(channel.to_string()))
.unwrap()); .unwrap());
} }
fn header(page_title: &str) -> Markup { fn header(page_title: &str) -> Markup {
html! { html! {
(DOCTYPE) (DOCTYPE)
meta charset="utf-8"; meta charset="utf-8";
meta name="viewport" content="width=device-width, initial-scale=1"; meta name="viewport" content="width=device-width, initial-scale=1";
title { (page_title) } title { (page_title) }
link rel="stylesheet" href="/assets/styles.css"; link rel="stylesheet" href="/assets/styles.css";
script defer data-domain="gabrielsimmer.com" src="https://plausible.io/js/script.js" { }; script defer data-domain="gabrielsimmer.com" src="https://plausible.io/js/script.js" { };
} }
} }
@ -225,11 +226,11 @@ async fn homepage() -> Markup {
p { a href = "/blog" { "Blog" } " " a href = "https://floofy.tech/@arch" rel = "me" { "Fediverse" } } p { a href = "/blog" { "Blog" } " " a href = "https://floofy.tech/@arch" rel = "me" { "Fediverse" } }
h3 { "Projects" } h3 { "Projects" }
@for project in projects.projects { @for project in projects.projects {
(project) (project)
} }
h3 { "Experiments" } h3 { "Experiments" }
@for project in projects.experiments { @for project in projects.experiments {
(project) (project)
} }
} }
} }
@ -270,52 +271,52 @@ async fn blog_post(Path(post): Path<String>) -> Result<impl IntoResponse, Status
let content = fs::read_to_string(&path).unwrap(); let content = fs::read_to_string(&path).unwrap();
let mut html = "".to_owned(); let mut html = "".to_owned();
let mut date = "".to_owned(); let mut date = "".to_owned();
let mut title = "".to_owned(); let mut title = "".to_owned();
if ext == "md" { if ext == "md" {
let (parsed, content) = frontmatter::parse_and_find_content(&content).unwrap(); let (parsed, content) = frontmatter::parse_and_find_content(&content).unwrap();
let metadata = parsed.unwrap(); let metadata = parsed.unwrap();
date = metadata["date"].as_str().unwrap().to_owned(); date = metadata["date"].as_str().unwrap().to_owned();
title = metadata["title"].as_str().unwrap().to_owned(); title = metadata["title"].as_str().unwrap().to_owned();
html = comrak::markdown_to_html(&content, &comrak::ComrakOptions::default()); html = comrak::markdown_to_html(&content, &comrak::ComrakOptions::default());
} else if ext == "org" { } else if ext == "org" {
let mut writer = Vec::new(); let mut writer = Vec::new();
let parsed = Org::parse(&content); let parsed = Org::parse(&content);
let keywords = parsed.keywords(); let keywords = parsed.keywords();
// Get date and title from keywords iterator // Get date and title from keywords iterator
for keyword in keywords { for keyword in keywords {
if keyword.key == "date" { if keyword.key == "date" {
date = keyword.value.to_string(); date = keyword.value.to_string();
} else if keyword.key == "title" { } else if keyword.key == "title" {
title = keyword.value.to_string(); title = keyword.value.to_string();
} }
} }
parsed.write_html(&mut writer).unwrap(); parsed.write_html(&mut writer).unwrap();
html = String::from_utf8(writer).unwrap(); html = String::from_utf8(writer).unwrap();
} }
let html_maud = PreEscaped(html); let html_maud = PreEscaped(html);
let html = html! { let html = html! {
(header(title.as_str())) (header(title.as_str()))
body { body {
main { main {
h1 { (title) } h1 { (title) }
p { (date) } p { (date) }
(html_maud) (html_maud)
} }
} }
}; };
return Ok(Response::builder() return Ok(Response::builder()
.header("content-type", "text/html") .header("content-type", "text/html")
.status(StatusCode::OK) .status(StatusCode::OK)
.body(Full::from(html.into_string())) .body(Full::from(html.into_string()))
.unwrap()); .unwrap());
} }
} }
return Err(StatusCode::NOT_FOUND); return Err(StatusCode::NOT_FOUND);
} }
async fn cached_page<T>( async fn cached_page<T>(
@ -420,7 +421,7 @@ async fn cached_page<T>(
.header("cache", "not") .header("cache", "not")
.status(StatusCode::OK) .status(StatusCode::OK)
.body(Full::from(bytes)) .body(Full::from(bytes))
.unwrap() .unwrap();
} }
let content = String::from_utf8(res).unwrap(); let content = String::from_utf8(res).unwrap();