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