This commit is contained in:
parent
152ccf8ba5
commit
692e765561
157
src/main.rs
157
src/main.rs
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue