Restructure project
This commit is contained in:
1 parent
0c158652c9
commit
14726ca7d4
28 files changed
+381
-357
No files matched your search
Generated
+6
-13
@@ -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",
|
||||
|
||||
+34
-5
@@ -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"] }
|
||||
@@ -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"] }
|
||||
@@ -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<Arc<Config>>,
|
||||
Extension(posts): Extension<ConcurrentPostsStore>,
|
||||
) -> Atom<Bytes> {
|
||||
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::<Vec<atom::Entry>>();
|
||||
|
||||
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())
|
||||
}
|
||||
File renamed without changes.
@@ -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)]
|
||||
@@ -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<Config>,
|
||||
posts: ConcurrentPostsStore,
|
||||
@@ -206,7 +207,7 @@ impl Renderer {
|
||||
|
||||
drop(fd);
|
||||
|
||||
Post::parse(
|
||||
parse_post(
|
||||
&self.code_renderer,
|
||||
*self.config.namespace_uuid,
|
||||
target.id.clone(),
|
||||
@@ -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<Arc<Config>>,
|
||||
Extension(posts): Extension<ConcurrentPostsStore>,
|
||||
) -> Atom<Bytes> {
|
||||
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::<Vec<atom::Entry>>();
|
||||
|
||||
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(),
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
use maud::html;
|
||||
|
||||
use crate::template;
|
||||
|
||||
use super::response::Html;
|
||||
|
||||
pub async fn handle() -> Html {
|
||||
@@ -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<ConcurrentPostsStore>) -> Html {
|
||||
File renamed without changes.
@@ -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(
|
||||
@@ -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<ConcurrentPostsStore>) -> Html {
|
||||
File renamed without changes.
@@ -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<Arc<Config>>,
|
||||
Extension(posts): Extension<ConcurrentPostsStore>,
|
||||
@@ -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,
|
||||
File renamed without changes.
File renamed without changes.
@@ -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();
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,5 @@
|
||||
pub mod codeblock;
|
||||
pub mod post;
|
||||
pub mod db;
|
||||
pub mod time;
|
||||
pub mod uuid;
|
||||
@@ -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<Self> {
|
||||
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<str> for PostId {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<str> for PostId {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl borrow::Borrow<str> for PostId {
|
||||
#[inline]
|
||||
fn borrow(&self) -> &str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl borrow::BorrowMut<str> 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)
|
||||
}
|
||||
}
|
||||
@@ -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<String>,
|
||||
author: ShString22,
|
||||
html: Markup,
|
||||
tags: Vec<ShString22>,
|
||||
created: DateTime<Utc>,
|
||||
updated: DateTime<Utc>,
|
||||
}
|
||||
|
||||
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<Utc> {
|
||||
self.created
|
||||
}
|
||||
|
||||
pub fn updated(&self) -> DateTime<Utc> {
|
||||
self.updated
|
||||
}
|
||||
}
|
||||
@@ -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<Self> {
|
||||
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<str> for PostId {
|
||||
fn as_ref(&self) -> &str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<str> for PostId {
|
||||
fn as_mut(&mut self) -> &mut str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl borrow::Borrow<str> for PostId {
|
||||
fn borrow(&self) -> &str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl borrow::BorrowMut<str> 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 struct Post {
|
||||
uuid: Uuid,
|
||||
id: PostId,
|
||||
title: String,
|
||||
subtitle: Option<String>,
|
||||
author: String,
|
||||
html: Markup,
|
||||
tags: Vec<ShString22>,
|
||||
pub fn parse(
|
||||
code_renderer: &CodeBlockRenderer,
|
||||
namespace: Uuid,
|
||||
post_id: PostId,
|
||||
file_name: &str,
|
||||
created: DateTime<Utc>,
|
||||
updated: DateTime<Utc>,
|
||||
}
|
||||
|
||||
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<Utc> {
|
||||
self.created
|
||||
}
|
||||
|
||||
pub fn updated(&self) -> DateTime<Utc> {
|
||||
self.updated
|
||||
}
|
||||
|
||||
pub fn parse(
|
||||
code_renderer: &CodeBlockRenderer,
|
||||
namespace: Uuid,
|
||||
post_id: PostId,
|
||||
file_name: &str,
|
||||
created: DateTime<Utc>,
|
||||
updated: DateTime<Utc>,
|
||||
source: &str,
|
||||
) -> Result<Self, ParseError>
|
||||
{
|
||||
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<Utc>,
|
||||
updated: DateTime<Utc>,
|
||||
mdpost: MdPost,
|
||||
) -> Result<Self, ParseError>
|
||||
{
|
||||
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,
|
||||
source: &str,
|
||||
) -> Result<Post, ParseError>
|
||||
{
|
||||
MdPost::parse(file_name, source)
|
||||
.and_then(|post| render_mdpost(
|
||||
code_renderer,
|
||||
namespace,
|
||||
post_id,
|
||||
created,
|
||||
updated,
|
||||
})
|
||||
}
|
||||
post
|
||||
))
|
||||
}
|
||||
|
||||
fn render_mdpost(
|
||||
code_renderer: &CodeBlockRenderer,
|
||||
namespace: Uuid,
|
||||
id: PostId,
|
||||
created: DateTime<Utc>,
|
||||
updated: DateTime<Utc>,
|
||||
mdpost: MdPost,
|
||||
) -> Result<Post, ParseError>
|
||||
{
|
||||
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(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<String>,
|
||||
author: String,
|
||||
author: ShString22,
|
||||
tags: Vec<ShString22>,
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,7 +0,0 @@
|
||||
[package]
|
||||
name = "css_gen"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
syntect = "4"
|
||||
Reference in new issue
Block a user