From 14726ca7d412a4615bfbebe43d4b9f7afa7718f5 Mon Sep 17 00:00:00 2001 From: Pantonshire Date: Tue, 31 May 2022 18:41:21 +0100 Subject: [PATCH] Restructure project --- Cargo.lock | 19 +- Cargo.toml | 39 ++- blog_server/Cargo.toml | 37 --- blog_server/src/service/atom.rs | 82 ------- .../src => src/bin/blog_server}/fs_watcher.rs | 0 .../src => src/bin/blog_server}/main.rs | 13 +- .../src => src/bin/blog_server}/render.rs | 11 +- src/bin/blog_server/service/atom.rs | 78 ++++++ .../bin/blog_server}/service/contact.rs | 1 + .../bin/blog_server}/service/index.rs | 8 +- .../bin/blog_server}/service/mod.rs | 0 .../bin/blog_server}/service/post.rs | 8 +- .../bin/blog_server}/service/posts_list.rs | 8 +- .../bin/blog_server}/service/response.rs | 0 .../bin/blog_server}/service/rss.rs | 10 +- .../bin/blog_server}/service/site.rs | 8 +- .../blog_server}/service/static_content.rs | 0 .../src => src/bin/blog_server}/template.rs | 0 .../css_gen/src => src/bin/css_gen}/main.rs | 11 +- {blog_server/src => src/lib}/codeblock.rs | 8 +- .../src/posts_store.rs => src/lib/db.rs | 0 src/lib/lib.rs | 5 + src/lib/post/id.rs | 88 +++++++ src/lib/post/mod.rs | 61 +++++ .../src/post.rs => src/lib/post/parse.rs | 232 +++++------------- {blog_server/src => src/lib}/time.rs | 0 {blog_server/src => src/lib}/uuid.rs | 0 utils/css_gen/Cargo.toml | 7 - 28 files changed, 379 insertions(+), 355 deletions(-) delete mode 100644 blog_server/Cargo.toml delete mode 100644 blog_server/src/service/atom.rs rename {blog_server/src => src/bin/blog_server}/fs_watcher.rs (100%) rename {blog_server/src => src/bin/blog_server}/main.rs (96%) rename {blog_server/src => src/bin/blog_server}/render.rs (98%) create mode 100644 src/bin/blog_server/service/atom.rs rename {blog_server/src => src/bin/blog_server}/service/contact.rs (99%) rename {blog_server/src => src/bin/blog_server}/service/index.rs (98%) rename {blog_server/src => src/bin/blog_server}/service/mod.rs (100%) rename {blog_server/src => src/bin/blog_server}/service/post.rs (94%) rename {blog_server/src => src/bin/blog_server}/service/posts_list.rs (95%) rename {blog_server/src => src/bin/blog_server}/service/response.rs (100%) rename {blog_server/src => src/bin/blog_server}/service/rss.rs (96%) rename {blog_server/src => src/bin/blog_server}/service/site.rs (97%) rename {blog_server/src => src/bin/blog_server}/service/static_content.rs (100%) rename {blog_server/src => src/bin/blog_server}/template.rs (100%) rename {utils/css_gen/src => src/bin/css_gen}/main.rs (76%) rename {blog_server/src => src/lib}/codeblock.rs (93%) rename blog_server/src/posts_store.rs => src/lib/db.rs (100%) create mode 100644 src/lib/lib.rs create mode 100644 src/lib/post/id.rs create mode 100644 src/lib/post/mod.rs rename blog_server/src/post.rs => src/lib/post/parse.rs (55%) rename {blog_server/src => src/lib}/time.rs (100%) rename {blog_server/src => src/lib}/uuid.rs (100%) delete mode 100644 utils/css_gen/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index c965b6c..1b790a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -158,8 +158,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "blog_server" -version = "0.1.0" +name = "blog" +version = "0.2.0" dependencies = [ "atom_syndication", "axum", @@ -232,13 +232,6 @@ dependencies = [ "cfg-if 1.0.0", ] -[[package]] -name = "css_gen" -version = "0.1.0" -dependencies = [ - "syntect", -] - [[package]] name = "darling" version = "0.12.4" @@ -773,9 +766,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" dependencies = [ "adler", ] @@ -953,9 +946,9 @@ checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b" [[package]] name = "parking_lot" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", "parking_lot_core", diff --git a/Cargo.toml b/Cargo.toml index 697cf69..8c3166b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,35 @@ -[workspace] +[package] +name = "blog" +version = "0.2.0" +edition = "2021" -members = [ - "blog_server", - "utils/css_gen" -] +[lib] +name = "blog" +path = "src/lib/lib.rs" + +[[bin]] +name = "blog_server" +path = "src/bin/blog_server/main.rs" + +[[bin]] +name = "css_gen" +path = "src/bin/css_gen/main.rs" + +[dependencies] +libshire = { git = "https://github.com/pantonshire/libshire" } +tokio = { version = "1", features = ["full"] } +axum = "0.5" +tower = { version = "0.4", features = ["limit"] } +tower-http = { version = "0.3", features = ["fs", "trace"] } +mime = "0.3" +maud = "0.23" +atom_syndication = "0.11" +rss = "2" +knuffel = "2" +pulldown-cmark = "0.9" +syntect = "4" +notify = "4" +chrono = "0.4" +tracing = "0.1" +tracing-subscriber = "0.3" +miette = { version = "4", features = ["fancy"] } diff --git a/blog_server/Cargo.toml b/blog_server/Cargo.toml deleted file mode 100644 index 95d7444..0000000 --- a/blog_server/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "blog_server" -version = "0.1.0" -edition = "2021" - -[dependencies] -# My own utilities library -libshire = { git = "https://github.com/pantonshire/libshire" } -# Async runtime for Axum -tokio = { version = "1", features = ["full"] } -# Web server framework -axum = "0.5" -# Middleware for the web server -tower = { version = "0.4", features = ["limit"] } -tower-http = { version = "0.3", features = ["fs", "trace"] } -# MIME type implementation -mime = "0.3" -# Compile-time HTTP templating -maud = "0.23" -# Serialisation for RSS and Atom -atom_syndication = "0.11" -rss = "2" -# KDL parsing -knuffel = "2" -# CommonMark parsing -pulldown-cmark = "0.9" -# Syntax highlighting -syntect = "4" -# Filesystem event watcher -notify = "4" -# Time library -chrono = "0.4" -# Logging for observability -tracing = "0.1" -tracing-subscriber = "0.3" -# Pretty errors -miette = { version = "4", features = ["fancy"] } diff --git a/blog_server/src/service/atom.rs b/blog_server/src/service/atom.rs deleted file mode 100644 index 66b18c4..0000000 --- a/blog_server/src/service/atom.rs +++ /dev/null @@ -1,82 +0,0 @@ -use std::sync::Arc; - -use atom_syndication as atom; -use axum::{ - body::Bytes, - extract::Extension, -}; - -use super::response::Atom; -use crate::{ - Config, - posts_store::ConcurrentPostsStore, - time::unix_epoch, -}; - -pub async fn handle( - Extension(config): Extension>, - Extension(posts): Extension, -) -> Atom { - let (atom_entries, updated) = { - let guard = posts.read().await; - - let atom_entries = guard.iter_by_created() - .take(config.atom.num_posts) - .map(|post| { - atom::EntryBuilder::default() - .id(format!("urn:uuid:{}", post.uuid())) - .title(post.title().to_owned()) - .updated(post.updated()) - .links(vec![ - atom::LinkBuilder::default() - .href(format!( - "{}://{}/articles/{}", - config.self_ref.protocol, - config.self_ref.domain, - post.id() - )) - .rel("alternate".to_owned()) - .mime_type(Some("text/html".to_owned())) - .build() - ]) - .author(atom::PersonBuilder::default() - .name(post.author().to_owned()) - .build()) - .build() - }) - .collect::>(); - - let updated = guard.last_updated() - .unwrap_or_else(unix_epoch); - - (atom_entries, updated) - }; - - Atom(atom::FeedBuilder::default() - .id(format!("urn:uuid:{}", *config.namespace_uuid)) - .title(config.atom.title.clone()) - .updated(updated) - .links(vec![ - atom::LinkBuilder::default() - .href(format!( - "{}://{}/atom.xml", - config.self_ref.protocol, - config.self_ref.domain - )) - .rel("self".to_owned()) - .build(), - atom::LinkBuilder::default() - .href(format!( - "{}://{}/articles/", - config.self_ref.protocol, - config.self_ref.domain - )) - .rel("alternate".to_owned()) - .mime_type(Some("text/html".to_owned())) - .build() - ]) - .entries(atom_entries) - .build() - .to_string() - .into()) -} diff --git a/blog_server/src/fs_watcher.rs b/src/bin/blog_server/fs_watcher.rs similarity index 100% rename from blog_server/src/fs_watcher.rs rename to src/bin/blog_server/fs_watcher.rs diff --git a/blog_server/src/main.rs b/src/bin/blog_server/main.rs similarity index 96% rename from blog_server/src/main.rs rename to src/bin/blog_server/main.rs index c618221..5b7f43f 100644 --- a/blog_server/src/main.rs +++ b/src/bin/blog_server/main.rs @@ -1,12 +1,7 @@ -mod codeblock; mod fs_watcher; -mod post; -mod posts_store; mod render; mod service; mod template; -mod time; -mod uuid; use std::{env, fs, path::PathBuf, sync::Arc, thread}; @@ -14,8 +9,12 @@ use axum::Server; use miette::{IntoDiagnostic, Context}; use tracing::info; -use codeblock::CodeBlockRenderer; -use posts_store::ConcurrentPostsStore; +use blog::{ + codeblock::CodeBlockRenderer, + db::ConcurrentPostsStore, + uuid, +}; + use render::Renderer; #[derive(knuffel::Decode, Clone, Debug)] diff --git a/blog_server/src/render.rs b/src/bin/blog_server/render.rs similarity index 98% rename from blog_server/src/render.rs rename to src/bin/blog_server/render.rs index 36b08e0..575ecaa 100644 --- a/blog_server/src/render.rs +++ b/src/bin/blog_server/render.rs @@ -10,14 +10,15 @@ use chrono::DateTime; use notify::DebouncedEvent; use tracing::{info, warn, error}; -use crate::{ +use blog::{ codeblock::CodeBlockRenderer, - Config, - post::{ParseError, Post, PostId}, - posts_store::ConcurrentPostsStore, + post::{parse as parse_post, ParseError, Post, PostId}, + db::ConcurrentPostsStore, time::unix_epoch, }; +use crate::Config; + pub struct Renderer { config: Arc, posts: ConcurrentPostsStore, @@ -206,7 +207,7 @@ impl Renderer { drop(fd); - Post::parse( + parse_post( &self.code_renderer, *self.config.namespace_uuid, target.id.clone(), diff --git a/src/bin/blog_server/service/atom.rs b/src/bin/blog_server/service/atom.rs new file mode 100644 index 0000000..1fc9a23 --- /dev/null +++ b/src/bin/blog_server/service/atom.rs @@ -0,0 +1,78 @@ +use std::sync::Arc; + +use atom_syndication as atom; +use axum::{body::Bytes, extract::Extension}; + +use blog::{db::ConcurrentPostsStore, time::unix_epoch}; + +use crate::Config; + +use super::response::Atom; + +pub async fn handle( + Extension(config): Extension>, + Extension(posts): Extension, +) -> Atom { + let (atom_entries, updated) = { + let guard = posts.read().await; + + let atom_entries = guard + .iter_by_created() + .take(config.atom.num_posts) + .map(|post| { + atom::EntryBuilder::default() + .id(format!("urn:uuid:{}", post.uuid())) + .title(post.title().to_owned()) + .updated(post.updated()) + .links(vec![atom::LinkBuilder::default() + .href(format!( + "{}://{}/articles/{}", + config.self_ref.protocol, + config.self_ref.domain, + post.id() + )) + .rel("alternate".to_owned()) + .mime_type(Some("text/html".to_owned())) + .build()]) + .author( + atom::PersonBuilder::default() + .name(post.author().to_owned()) + .build(), + ) + .build() + }) + .collect::>(); + + let updated = guard.last_updated().unwrap_or_else(unix_epoch); + + (atom_entries, updated) + }; + + Atom( + atom::FeedBuilder::default() + .id(format!("urn:uuid:{}", *config.namespace_uuid)) + .title(config.atom.title.clone()) + .updated(updated) + .links(vec![ + atom::LinkBuilder::default() + .href(format!( + "{}://{}/atom.xml", + config.self_ref.protocol, config.self_ref.domain + )) + .rel("self".to_owned()) + .build(), + atom::LinkBuilder::default() + .href(format!( + "{}://{}/articles/", + config.self_ref.protocol, config.self_ref.domain + )) + .rel("alternate".to_owned()) + .mime_type(Some("text/html".to_owned())) + .build(), + ]) + .entries(atom_entries) + .build() + .to_string() + .into(), + ) +} diff --git a/blog_server/src/service/contact.rs b/src/bin/blog_server/service/contact.rs similarity index 99% rename from blog_server/src/service/contact.rs rename to src/bin/blog_server/service/contact.rs index ee200e3..ea2f150 100644 --- a/blog_server/src/service/contact.rs +++ b/src/bin/blog_server/service/contact.rs @@ -1,6 +1,7 @@ use maud::html; use crate::template; + use super::response::Html; pub async fn handle() -> Html { diff --git a/blog_server/src/service/index.rs b/src/bin/blog_server/service/index.rs similarity index 98% rename from blog_server/src/service/index.rs rename to src/bin/blog_server/service/index.rs index f5ae44a..77fd765 100644 --- a/blog_server/src/service/index.rs +++ b/src/bin/blog_server/service/index.rs @@ -1,10 +1,10 @@ use axum::extract::Extension; use maud::html; -use crate::{ - posts_store::ConcurrentPostsStore, - template, -}; +use blog::db::ConcurrentPostsStore; + +use crate::template; + use super::response::Html; pub async fn handle(Extension(posts): Extension) -> Html { diff --git a/blog_server/src/service/mod.rs b/src/bin/blog_server/service/mod.rs similarity index 100% rename from blog_server/src/service/mod.rs rename to src/bin/blog_server/service/mod.rs diff --git a/blog_server/src/service/post.rs b/src/bin/blog_server/service/post.rs similarity index 94% rename from blog_server/src/service/post.rs rename to src/bin/blog_server/service/post.rs index 05fd8a8..9cf859b 100644 --- a/blog_server/src/service/post.rs +++ b/src/bin/blog_server/service/post.rs @@ -1,10 +1,10 @@ use axum::extract::{Extension, Path}; use maud::html; -use crate::{ - posts_store::ConcurrentPostsStore, - template, -}; +use blog::db::ConcurrentPostsStore; + +use crate::template; + use super::response::{Error, Html}; pub async fn handle( diff --git a/blog_server/src/service/posts_list.rs b/src/bin/blog_server/service/posts_list.rs similarity index 95% rename from blog_server/src/service/posts_list.rs rename to src/bin/blog_server/service/posts_list.rs index 0d18b2b..c6a58a0 100644 --- a/blog_server/src/service/posts_list.rs +++ b/src/bin/blog_server/service/posts_list.rs @@ -1,10 +1,10 @@ use axum::extract::Extension; use maud::html; -use crate::{ - posts_store::ConcurrentPostsStore, - template, -}; +use blog::db::ConcurrentPostsStore; + +use crate::template; + use super::response::Html; pub async fn handle(Extension(posts): Extension) -> Html { diff --git a/blog_server/src/service/response.rs b/src/bin/blog_server/service/response.rs similarity index 100% rename from blog_server/src/service/response.rs rename to src/bin/blog_server/service/response.rs diff --git a/blog_server/src/service/rss.rs b/src/bin/blog_server/service/rss.rs similarity index 96% rename from blog_server/src/service/rss.rs rename to src/bin/blog_server/service/rss.rs index 3e6dee8..b5ed17c 100644 --- a/blog_server/src/service/rss.rs +++ b/src/bin/blog_server/service/rss.rs @@ -5,13 +5,15 @@ use axum::{ extract::Extension, }; -use super::response::Rss; -use crate::{ - Config, - posts_store::ConcurrentPostsStore, +use blog::{ + db::ConcurrentPostsStore, time::unix_epoch, }; +use crate::Config; + +use super::response::Rss; + pub async fn handle( Extension(config): Extension>, Extension(posts): Extension, diff --git a/blog_server/src/service/site.rs b/src/bin/blog_server/service/site.rs similarity index 97% rename from blog_server/src/service/site.rs rename to src/bin/blog_server/service/site.rs index 9f19669..d763ed6 100644 --- a/blog_server/src/service/site.rs +++ b/src/bin/blog_server/service/site.rs @@ -11,10 +11,10 @@ use tower::limit::ConcurrencyLimitLayer; use tower_http::trace::TraceLayer; use tracing::info; -use crate::{ - Config, - posts_store::ConcurrentPostsStore -}; +use blog::db::ConcurrentPostsStore; + +use crate::Config; + use super::{ atom, contact, diff --git a/blog_server/src/service/static_content.rs b/src/bin/blog_server/service/static_content.rs similarity index 100% rename from blog_server/src/service/static_content.rs rename to src/bin/blog_server/service/static_content.rs diff --git a/blog_server/src/template.rs b/src/bin/blog_server/template.rs similarity index 100% rename from blog_server/src/template.rs rename to src/bin/blog_server/template.rs diff --git a/utils/css_gen/src/main.rs b/src/bin/css_gen/main.rs similarity index 76% rename from utils/css_gen/src/main.rs rename to src/bin/css_gen/main.rs index c9b8d87..51110f5 100644 --- a/utils/css_gen/src/main.rs +++ b/src/bin/css_gen/main.rs @@ -1,10 +1,11 @@ -use std::env; -use std::process; +use std::{env, process}; -use syntect::highlighting::ThemeSet; -use syntect::html::{css_for_theme_with_class_style, ClassStyle}; +use syntect::{ + highlighting::ThemeSet, + html::css_for_theme_with_class_style, +}; -const CLASS_STYLE: ClassStyle = ClassStyle::SpacedPrefixed { prefix: "cb_" }; +use blog::codeblock::CLASS_STYLE; fn main() { let theme_set = ThemeSet::load_defaults(); diff --git a/blog_server/src/codeblock.rs b/src/lib/codeblock.rs similarity index 93% rename from blog_server/src/codeblock.rs rename to src/lib/codeblock.rs index 2b810d3..e8f72db 100644 --- a/blog_server/src/codeblock.rs +++ b/src/lib/codeblock.rs @@ -3,7 +3,7 @@ use syntect::html::{ClassedHTMLGenerator, ClassStyle}; use syntect::parsing::SyntaxSet; use syntect::util::LinesWithEndings; -const CLASS_STYLE: ClassStyle = ClassStyle::SpacedPrefixed { prefix: "cb_" }; +pub const CLASS_STYLE: ClassStyle = ClassStyle::SpacedPrefixed { prefix: "cb_" }; pub struct CodeBlockRenderer { syntax_set: SyntaxSet, @@ -71,3 +71,9 @@ impl CodeBlockRenderer { } } } + +impl Default for CodeBlockRenderer { + fn default() -> Self { + Self::new() + } +} diff --git a/blog_server/src/posts_store.rs b/src/lib/db.rs similarity index 100% rename from blog_server/src/posts_store.rs rename to src/lib/db.rs diff --git a/src/lib/lib.rs b/src/lib/lib.rs new file mode 100644 index 0000000..b3bf79b --- /dev/null +++ b/src/lib/lib.rs @@ -0,0 +1,5 @@ +pub mod codeblock; +pub mod post; +pub mod db; +pub mod time; +pub mod uuid; diff --git a/src/lib/post/id.rs b/src/lib/post/id.rs new file mode 100644 index 0000000..e0a7c24 --- /dev/null +++ b/src/lib/post/id.rs @@ -0,0 +1,88 @@ +use std::{borrow, fmt, ops}; + +use libshire::strings::ShString22; + +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct PostId(ShString22); + +impl PostId { + #[inline] + #[must_use] + pub fn from_file_name(file_name: &str) -> Option { + fn is_invalid_char(c: char) -> bool { + c == '/' || c == '\\' || c == '.' + } + + let prefix = file_name + .strip_suffix(super::POST_FILE_EXTENSION)?; + + if prefix.contains(is_invalid_char) { + return None; + } + + Some(Self(ShString22::new_from_str(prefix))) + } + + #[inline] + #[must_use] + pub fn into_inner(self) -> ShString22 { + self.0 + } + + #[inline] + #[must_use] + pub fn as_inner(&self) -> &ShString22 { + &self.0 + } +} + +impl ops::Deref for PostId { + type Target = str; + + #[inline] + fn deref(&self) -> &Self::Target { + &*self.0 + } +} + +impl ops::DerefMut for PostId { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut *self.0 + } +} + +impl AsRef for PostId { + #[inline] + fn as_ref(&self) -> &str { + self + } +} + +impl AsMut for PostId { + #[inline] + fn as_mut(&mut self) -> &mut str { + self + } +} + +impl borrow::Borrow for PostId { + #[inline] + fn borrow(&self) -> &str { + self + } +} + +impl borrow::BorrowMut for PostId { + #[inline] + fn borrow_mut(&mut self) -> &mut str { + self + } +} + +impl fmt::Display for PostId { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} diff --git a/src/lib/post/mod.rs b/src/lib/post/mod.rs new file mode 100644 index 0000000..3c1df47 --- /dev/null +++ b/src/lib/post/mod.rs @@ -0,0 +1,61 @@ +mod id; +mod parse; + +use chrono::{DateTime, Utc}; +use libshire::{strings::ShString22, uuid::Uuid}; +use maud::{Markup, PreEscaped}; + +pub use id::PostId; +pub use parse::{parse, ParseError}; + +const POST_FILE_EXTENSION: &str = ".kdl.md"; + +pub struct Post { + uuid: Uuid, + id: PostId, + title: String, + subtitle: Option, + author: ShString22, + html: Markup, + tags: Vec, + created: DateTime, + updated: DateTime, +} + +impl Post { + pub fn uuid(&self) -> Uuid { + self.uuid + } + + pub fn id(&self) -> &PostId { + &self.id + } + + pub fn title(&self) -> &str { + &self.title + } + + pub fn subtitle(&self) -> Option<&str> { + self.subtitle.as_deref() + } + + pub fn author(&self) -> &str { + &self.author + } + + pub fn html(&self) -> PreEscaped<&str> { + PreEscaped(&self.html.0) + } + + pub fn tags(&self) -> &[ShString22] { + &self.tags + } + + pub fn created(&self) -> DateTime { + self.created + } + + pub fn updated(&self) -> DateTime { + self.updated + } +} diff --git a/blog_server/src/post.rs b/src/lib/post/parse.rs similarity index 55% rename from blog_server/src/post.rs rename to src/lib/post/parse.rs index 5ee3a39..8f60c07 100644 --- a/blog_server/src/post.rs +++ b/src/lib/post/parse.rs @@ -1,187 +1,73 @@ -use std::{borrow, error, fmt, ops}; +use std::{error, fmt}; use chrono::{DateTime, Utc}; use libshire::{strings::ShString22, uuid::{Uuid, UuidV5Error}}; -use maud::{Markup, PreEscaped, html}; +use maud::{html, PreEscaped}; use crate::codeblock::CodeBlockRenderer; -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct PostId(ShString22); +use super::{id::PostId, Post}; -impl PostId { - pub fn from_file_name(file_name: &str) -> Option { - const POST_FILE_EXTENSION: &str = ".kdl.md"; - - fn is_invalid_char(c: char) -> bool { - c == '/' || c == '\\' || c == '.' - } - - let prefix = file_name - .strip_suffix(POST_FILE_EXTENSION)?; - - if prefix.contains(is_invalid_char) { - return None; - } - - Some(Self(ShString22::new_from_str(prefix))) - } -} - -impl ops::Deref for PostId { - type Target = str; - - fn deref(&self) -> &Self::Target { - &*self.0 - } -} - -impl ops::DerefMut for PostId { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut *self.0 - } -} - -impl AsRef for PostId { - fn as_ref(&self) -> &str { - self - } -} - -impl AsMut for PostId { - fn as_mut(&mut self) -> &mut str { - self - } -} - -impl borrow::Borrow for PostId { - fn borrow(&self) -> &str { - self - } -} - -impl borrow::BorrowMut for PostId { - fn borrow_mut(&mut self) -> &mut str { - self - } -} - -impl fmt::Display for PostId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&**self, f) - } +pub fn parse( + code_renderer: &CodeBlockRenderer, + namespace: Uuid, + post_id: PostId, + file_name: &str, + created: DateTime, + updated: DateTime, + source: &str, +) -> Result +{ + MdPost::parse(file_name, source) + .and_then(|post| render_mdpost( + code_renderer, + namespace, + post_id, + created, + updated, + post + )) } -pub struct Post { - uuid: Uuid, +fn render_mdpost( + code_renderer: &CodeBlockRenderer, + namespace: Uuid, id: PostId, - title: String, - subtitle: Option, - author: String, - html: Markup, - tags: Vec, created: DateTime, updated: DateTime, -} - -impl Post { - pub fn uuid(&self) -> Uuid { - self.uuid - } - - pub fn id(&self) -> &PostId { - &self.id - } - - pub fn title(&self) -> &str { - &self.title - } - - pub fn subtitle(&self) -> Option<&str> { - self.subtitle.as_deref() - } - - pub fn author(&self) -> &str { - &self.author - } - - pub fn html(&self) -> PreEscaped<&str> { - PreEscaped(&self.html.0) - } - - pub fn tags(&self) -> &[ShString22] { - &self.tags - } - - pub fn created(&self) -> DateTime { - self.created - } - - pub fn updated(&self) -> DateTime { - self.updated - } + mdpost: MdPost, +) -> Result +{ + use pulldown_cmark::{Options, Parser, html::push_html}; - pub fn parse( - code_renderer: &CodeBlockRenderer, - namespace: Uuid, - post_id: PostId, - file_name: &str, - created: DateTime, - updated: DateTime, - source: &str, - ) -> Result - { - MdPost::parse(file_name, source) - .and_then(|post| Self::from_mdpost( - code_renderer, - namespace, - post_id, - created, - updated, - post - )) - } - - fn from_mdpost( - code_renderer: &CodeBlockRenderer, - namespace: Uuid, - id: PostId, - created: DateTime, - updated: DateTime, - mdpost: MdPost, - ) -> Result - { - use pulldown_cmark::{Options, Parser, html::push_html}; - - const PARSER_OPTIONS: Options = Options::ENABLE_TABLES - .union(Options::ENABLE_FOOTNOTES) - .union(Options::ENABLE_STRIKETHROUGH); - - let uuid = Uuid::new_v5(namespace, &*id) - .map_err(|err| match err { - UuidV5Error::NameTooLong(len) => ParseError::IdTooLong(len), - })?; - - let mut parser = PostMdParser::new( - Parser::new_ext(&mdpost.markdown, PARSER_OPTIONS), - code_renderer - ); - - let mut html_buf = String::new(); - push_html(&mut html_buf, parser.by_ref()); - - Ok(Self { - uuid, - id, - title: mdpost.title, - subtitle: mdpost.subtitle, - author: mdpost.author, - html: PreEscaped(html_buf), - tags: mdpost.tags, - created, - updated, - }) - } + const PARSER_OPTIONS: Options = Options::ENABLE_TABLES + .union(Options::ENABLE_FOOTNOTES) + .union(Options::ENABLE_STRIKETHROUGH); + + let uuid = Uuid::new_v5(namespace, &*id) + .map_err(|err| match err { + UuidV5Error::NameTooLong(len) => ParseError::IdTooLong(len), + })?; + + let mut parser = PostMdParser::new( + Parser::new_ext(&mdpost.markdown, PARSER_OPTIONS), + code_renderer + ); + + let mut html_buf = String::new(); + push_html(&mut html_buf, parser.by_ref()); + + Ok(Post { + uuid, + id, + title: mdpost.title, + subtitle: mdpost.subtitle, + author: mdpost.author, + html: PreEscaped(html_buf), + tags: mdpost.tags, + created, + updated, + }) } /// Iterator struct which wraps another event iterator in order to render code blocks, collect the links @@ -283,7 +169,7 @@ struct MdPost { markdown: String, title: String, subtitle: Option, - author: String, + author: ShString22, tags: Vec, } @@ -303,7 +189,7 @@ impl MdPost { markdown: md.to_owned(), title: header.title, subtitle: header.subtitle, - author: header.author, + author: header.author.into(), tags: header.tags.into_iter().map(|tag| tag.tag.into()).collect(), }) } diff --git a/blog_server/src/time.rs b/src/lib/time.rs similarity index 100% rename from blog_server/src/time.rs rename to src/lib/time.rs diff --git a/blog_server/src/uuid.rs b/src/lib/uuid.rs similarity index 100% rename from blog_server/src/uuid.rs rename to src/lib/uuid.rs diff --git a/utils/css_gen/Cargo.toml b/utils/css_gen/Cargo.toml deleted file mode 100644 index fcb1c4d..0000000 --- a/utils/css_gen/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "css_gen" -version = "0.1.0" -edition = "2021" - -[dependencies] -syntect = "4"