From d7a521e581e43278a53a467701dee1ba50592b02 Mon Sep 17 00:00:00 2001 From: Pantonshire Date: Sat, 4 Jun 2022 20:53:54 +0100 Subject: [PATCH] Switch to TOML for configuration --- .gitignore | 2 +- Cargo.lock | 301 ++---------------- Cargo.toml | 14 +- example_config.kdl | 26 -- example_config.toml | 23 ++ src/bin/blog_server/config.rs | 100 +----- src/bin/blog_server/fs_watcher.rs | 13 +- src/bin/blog_server/main.rs | 108 +++++-- src/bin/blog_server/render.rs | 16 +- src/bin/blog_server/service/atom.rs | 4 +- src/bin/blog_server/service/contact.rs | 2 +- src/bin/blog_server/service/index.rs | 2 +- src/bin/blog_server/service/mod.rs | 2 +- src/bin/blog_server/service/post.rs | 2 +- src/bin/blog_server/service/posts_list.rs | 2 +- src/bin/blog_server/service/response.rs | 44 +-- src/bin/blog_server/service/rss.rs | 2 +- src/bin/blog_server/service/site.rs | 4 +- src/bin/blog_server/service/static_content.rs | 8 +- src/bin/blog_server/template.rs | 2 +- src/bin/prepublish/main.rs | 4 + src/lib/post/error.rs | 27 +- src/lib/post/header.rs | 114 +++---- src/lib/post/markdown_post.rs | 31 -- src/lib/post/mod.rs | 4 +- src/lib/post/rendered_post.rs | 18 +- src/lib/post/source.rs | 12 +- 27 files changed, 275 insertions(+), 612 deletions(-) delete mode 100644 example_config.kdl create mode 100644 example_config.toml create mode 100644 src/bin/prepublish/main.rs delete mode 100644 src/lib/post/markdown_post.rs diff --git a/.gitignore b/.gitignore index 4d49d01..13806da 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .DS_Store /target/ -/config.kdl +/config.toml /content/static/fonts/ /article_media/ /test_posts/ diff --git a/Cargo.lock b/Cargo.lock index 575bb2d..e2b260b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,30 +2,12 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" -dependencies = [ - "gimli", -] - [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aho-corasick" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] - [[package]] name = "ansi_term" version = "0.12.1" @@ -37,9 +19,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.53" +version = "0.1.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" +checksum = "e24af9c57a5a2463ffe401d5340433d21487b76238bae64ee5992ed688b4e753" dependencies = [ "proc-macro2", "quote", @@ -59,17 +41,6 @@ dependencies = [ "quick-xml", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi 0.3.9", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -121,21 +92,6 @@ dependencies = [ "mime", ] -[[package]] -name = "backtrace" -version = "0.3.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" -dependencies = [ - "addr2line", - "cc", - "cfg-if 1.0.0", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - [[package]] name = "base64" version = "0.13.0" @@ -164,17 +120,17 @@ dependencies = [ "atom_syndication", "axum", "chrono", - "kdl", - "knuffel", + "hyper", "libshire", "maud", - "miette", "mime", "notify", "pulldown-cmark", "rss", + "serde", "syntect", "tokio", + "toml", "tower", "tower-http", "tracing", @@ -214,16 +170,11 @@ dependencies = [ "libc", "num-integer", "num-traits", + "serde", "time 0.1.44", "winapi 0.3.9", ] -[[package]] -name = "chumsky" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d02796e4586c6c41aeb68eae9bfb4558a522c35f1430c14b40136c3706e09e4" - [[package]] name = "crc32fast" version = "1.3.2" @@ -438,24 +389,12 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "gimli" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" - [[package]] name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -[[package]] -name = "heck" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" - [[package]] name = "hermit-abi" version = "0.1.19" @@ -573,29 +512,12 @@ dependencies = [ "libc", ] -[[package]] -name = "is_ci" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" - [[package]] name = "itoa" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" -[[package]] -name = "kdl" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ba123fe3f30838b649efd5606531e8326623c5f44491c7e631f3b970e20cdb" -dependencies = [ - "miette", - "nom", - "thiserror", -] - [[package]] name = "kernel32-sys" version = "0.2.2" @@ -606,33 +528,6 @@ dependencies = [ "winapi-build", ] -[[package]] -name = "knuffel" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f9f7a07459e9dc5d07f5dabfc2c2a965bf39195451eb785b61460c65f386eef" -dependencies = [ - "base64", - "chumsky", - "knuffel-derive", - "miette", - "thiserror", - "unicode-width", -] - -[[package]] -name = "knuffel-derive" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbcbdb0b6f26a4e5ecb0dd9074a430398a41b2c1624c205bcc202541ddc15488" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -654,7 +549,10 @@ checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "libshire" version = "0.1.0" -source = "git+https://github.com/pantonshire/libshire#c0f0a3a01716ec48d91fa3daf035e506f3a36165" +source = "git+https://github.com/pantonshire/libshire#378800ce890c1e936d4923b2fc92255876bacdc3" +dependencies = [ + "serde", +] [[package]] name = "line-wrap" @@ -729,37 +627,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" -[[package]] -name = "miette" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c90329e44f9208b55f45711f9558cec15d7ef8295cc65ecd6d4188ae8edc58c" -dependencies = [ - "atty", - "backtrace", - "miette-derive", - "once_cell", - "owo-colors", - "supports-color", - "supports-hyperlinks", - "supports-unicode", - "terminal_size", - "textwrap", - "thiserror", - "unicode-width", -] - -[[package]] -name = "miette-derive" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b5bc45b761bcf1b5e6e6c4128cd93b84c218721a8d9b894aa0aff4ed180174c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "mime" version = "0.3.16" @@ -776,12 +643,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" version = "0.5.3" @@ -863,16 +724,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91" -[[package]] -name = "nom" -version = "7.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "notify" version = "4.0.17" @@ -929,15 +780,6 @@ dependencies = [ "libc", ] -[[package]] -name = "object" -version = "0.28.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.12.0" @@ -966,12 +808,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "owo-colors" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b" - [[package]] name = "parking_lot" version = "0.12.1" @@ -1126,17 +962,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "regex" -version = "1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - [[package]] name = "regex-syntax" version = "0.6.26" @@ -1155,12 +980,6 @@ dependencies = [ "quick-xml", ] -[[package]] -name = "rustc-demangle" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" - [[package]] name = "ryu" version = "1.0.10" @@ -1193,6 +1012,9 @@ name = "serde" version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +dependencies = [ + "serde_derive", +] [[package]] name = "serde_derive" @@ -1258,12 +1080,6 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" -[[package]] -name = "smawk" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" - [[package]] name = "socket2" version = "0.4.4" @@ -1280,39 +1096,11 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "supports-color" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4872ced36b91d47bae8a214a683fe54e7078875b399dfa251df346c9b547d1f9" -dependencies = [ - "atty", - "is_ci", -] - -[[package]] -name = "supports-hyperlinks" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "590b34f7c5f01ecc9d78dba4b3f445f31df750a67621cf31626f3b7441ce6406" -dependencies = [ - "atty", -] - -[[package]] -name = "supports-unicode" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8b945e45b417b125a8ec51f1b7df2f8df7920367700d1f98aedd21e5735f8b2" -dependencies = [ - "atty", -] - [[package]] name = "syn" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" +checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf" dependencies = [ "proc-macro2", "quote", @@ -1347,47 +1135,6 @@ dependencies = [ "yaml-rust", ] -[[package]] -name = "terminal_size" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" -dependencies = [ - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "textwrap" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" -dependencies = [ - "smawk", - "unicode-linebreak", - "unicode-width", -] - -[[package]] -name = "thiserror" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "thread_local" version = "1.1.4" @@ -1463,6 +1210,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + [[package]] name = "tower" version = "0.4.12" @@ -1598,15 +1354,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" -[[package]] -name = "unicode-linebreak" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a52dcaab0c48d931f7cc8ef826fa51690a08e1ea55117ef26f89864f532383f" -dependencies = [ - "regex", -] - [[package]] name = "unicode-width" version = "0.1.9" diff --git a/Cargo.toml b/Cargo.toml index e258d68..91d06cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,14 @@ path = "src/bin/blog_server/main.rs" name = "css_gen" path = "src/bin/css_gen/main.rs" +[[bin]] +name = "prepublish" +path = "src/bin/prepublish/main.rs" + [dependencies] -libshire = { git = "https://github.com/pantonshire/libshire" } +libshire = { git = "https://github.com/pantonshire/libshire", features = ["serde"] } tokio = { version = "1", features = ["full"] } +hyper = "0.14" axum = "0.5" tower = { version = "0.4", features = ["limit"] } tower-http = { version = "0.3", features = ["fs", "trace"] } @@ -25,12 +30,11 @@ mime = "0.3" maud = "0.23" atom_syndication = "0.11" rss = "2" -kdl = "4" -knuffel = "2" # TODO: replace with kdl crate +serde = { version = "1", features = ["derive"] } +toml = "0.5" pulldown-cmark = "0.9" syntect = "4" notify = "4" -chrono = "0.4" +chrono = { version = "0.4", features = ["serde"] } tracing = "0.1" tracing-subscriber = "0.3" -miette = { version = "4", features = ["fancy"] } diff --git a/example_config.kdl b/example_config.kdl deleted file mode 100644 index a2004e8..0000000 --- a/example_config.kdl +++ /dev/null @@ -1,26 +0,0 @@ -bind "127.0.0.1:8080" -concurrency-limit 128 - -static-dir "./content/static/" -favicon-dir "./content/favicon/" -robots-path "./content/robots.txt" -posts-dir "./posts/" -post-media-dir "./article_media/" - -namespace-uuid "00000000-0000-0000-0000-000000000000" - -self-ref { - protocol "http" - domain "127.0.0.1:8080" -} - -rss { - num-posts 20 - title "Pantonshire" - ttl 360 -} - -atom { - num-posts 20 - title "Pantonshire" -} diff --git a/example_config.toml b/example_config.toml new file mode 100644 index 0000000..444afb0 --- /dev/null +++ b/example_config.toml @@ -0,0 +1,23 @@ +bind = "127.0.0.1:8080" +concurrency_limit = 128 + +static_dir = "./content/static/" +favicon_dir = "./content/favicon/" +robots_path = "./content/robots.txt" +posts_dir = "./articles/src/" +post_media_dir = "./articles/media/" + +namespace_uuid = "00000000-0000-0000-0000-000000000000" + +[self_ref] +protocol = "http" +domain = "localhost:8080" + +[rss] +num_posts = 20 +title = "Pantonshire" +ttl = 360 + +[atom] +num_posts = 20 +title = "Pantonshire" diff --git a/src/bin/blog_server/config.rs b/src/bin/blog_server/config.rs index 838dfe5..320237c 100644 --- a/src/bin/blog_server/config.rs +++ b/src/bin/blog_server/config.rs @@ -1,114 +1,46 @@ -use std::{path::PathBuf, ops}; +use std::{net::SocketAddr, path::PathBuf, str}; -use knuffel::{ - ast::{Literal, TypeName}, - decode::{Context, Kind}, - errors::{DecodeError, ExpectedType}, - span::Spanned, - traits::ErrorSpan, - DecodeScalar, -}; +use libshire::uuid::Uuid; +use serde::Deserialize; -//TODO: replace knuffel crate with kdl crate - -#[derive(knuffel::Decode, Clone, Debug)] -pub struct Config { - #[knuffel(child, unwrap(argument))] - pub bind: String, - #[knuffel(child, unwrap(argument))] +#[derive(Deserialize, Clone, Debug)] +pub(crate) struct Config { + pub bind: SocketAddr, pub concurrency_limit: usize, - #[knuffel(child, unwrap(argument))] pub static_dir: PathBuf, - #[knuffel(child, unwrap(argument))] pub favicon_dir: PathBuf, - #[knuffel(child, unwrap(argument))] pub robots_path: PathBuf, - #[knuffel(child, unwrap(argument))] pub posts_dir: PathBuf, - #[knuffel(child, unwrap(argument))] pub post_media_dir: PathBuf, - #[knuffel(child, unwrap(argument))] pub namespace_uuid: Uuid, - #[knuffel(child)] pub self_ref: SelfRefConfig, - #[knuffel(child)] pub rss: RssConfig, - #[knuffel(child)] pub atom: AtomConfig, } -#[derive(knuffel::Decode, Clone, Debug)] -pub struct SelfRefConfig { - #[knuffel(child, unwrap(argument))] +#[derive(Deserialize, Clone, Debug)] +pub(crate) struct SelfRefConfig { pub protocol: String, - #[knuffel(child, unwrap(argument))] pub domain: String, } -#[derive(knuffel::Decode, Clone, Debug)] -pub struct RssConfig { - #[knuffel(child, unwrap(argument))] +#[derive(Deserialize, Clone, Debug)] +pub(crate) struct RssConfig { pub num_posts: usize, - #[knuffel(child, unwrap(argument))] pub title: String, - #[knuffel(child, unwrap(argument))] pub ttl: u32, } -#[derive(knuffel::Decode, Clone, Debug)] -pub struct AtomConfig { - #[knuffel(child, unwrap(argument))] +#[derive(Deserialize, Clone, Debug)] +pub(crate) struct AtomConfig { pub num_posts: usize, - #[knuffel(child, unwrap(argument))] pub title: String, } -#[derive(Clone, Copy, Default, Debug)] -#[repr(transparent)] -pub struct Uuid(pub libshire::uuid::Uuid); - -impl Uuid { - pub fn as_inner(&self) -> &libshire::uuid::Uuid { - &self.0 - } -} - -impl ops::Deref for Uuid { - type Target = libshire::uuid::Uuid; - - fn deref(&self) -> &Self::Target { - self.as_inner() - } -} - -impl DecodeScalar for Uuid { - fn type_check(type_name: &Option>, ctx: &mut Context) { - if let Some(type_name) = type_name { - ctx.emit_error(DecodeError::TypeName { - span: type_name.span().clone(), - found: Some((&**type_name).clone()), - expected: ExpectedType::no_type(), - rust_type: "Uuid", - }); - } - } +impl str::FromStr for Config { + type Err = toml::de::Error; - fn raw_decode( - value: &Spanned, - ctx: &mut Context, - ) -> Result> { - match &**value { - Literal::String(s) => match s.parse() { - Ok(uuid) => Ok(Self(uuid)), - Err(err) => { - ctx.emit_error(DecodeError::conversion(value, err)); - Ok(Default::default()) - } - }, - _ => { - ctx.emit_error(DecodeError::scalar_kind(Kind::String, value)); - Ok(Default::default()) - } - } + fn from_str(s: &str) -> Result { + toml::from_str(s) } } diff --git a/src/bin/blog_server/fs_watcher.rs b/src/bin/blog_server/fs_watcher.rs index 8872d44..0329299 100644 --- a/src/bin/blog_server/fs_watcher.rs +++ b/src/bin/blog_server/fs_watcher.rs @@ -4,7 +4,6 @@ use std::{ time::Duration, }; -use miette::{IntoDiagnostic, WrapErr}; use notify::{ DebouncedEvent, RecommendedWatcher, @@ -14,20 +13,20 @@ use notify::{ }; use tracing::info; -pub fn start_watching( +use crate::Error; + +pub(crate) fn start_watching( tx: mpsc::Sender, watch_path: &Path -) -> miette::Result +) -> Result { let mut watcher = watcher(tx, Duration::from_secs(2)) - .into_diagnostic() - .wrap_err("Failed to create filesystem watcher")?; + .map_err(Error::CreateWatcher)?; // Watch the path in non-recursive mode, so events are not generated for nodes in // sub-directories. watcher.watch(watch_path, RecursiveMode::NonRecursive) - .into_diagnostic() - .wrap_err_with(|| format!("Failed to watch directory {}", watch_path.to_string_lossy()))?; + .map_err(|err| Error::WatchDir(watch_path.to_owned(), err))?; info!(path = %watch_path.to_string_lossy(), "Watching directory"); diff --git a/src/bin/blog_server/main.rs b/src/bin/blog_server/main.rs index 30566ad..6c90c93 100644 --- a/src/bin/blog_server/main.rs +++ b/src/bin/blog_server/main.rs @@ -4,10 +4,20 @@ mod render; mod service; mod template; -use std::{env, fs, sync::Arc, thread}; +use std::{ + env, + error, + fmt, + fs, + io, + net::SocketAddr, + path::PathBuf, + process, + sync::Arc, + thread, +}; -use axum::Server; -use miette::{IntoDiagnostic, Context}; +use hyper::Server; use tracing::info; use blog::{ @@ -18,23 +28,30 @@ use blog::{ use config::Config; use render::Renderer; -fn main() -> miette::Result<()> { +fn main() { + if let Err(err) = run() { + eprintln!("***** Fatal error *****"); + eprintln!("{}", err); + process::exit(1); + } +} + +fn run() -> Result<(), Error> { tracing_subscriber::fmt::init(); - // Load the configuration from the KDL config file specified by the first command-line + // Load the configuration from the TOML config file specified by the first command-line // argument. let config = Arc::new({ let config_path = env::args().nth(1) - .ok_or_else(|| miette::Error::msg("No config file specified"))?; + .ok_or(Error::NoConfig)?; info!(path = %config_path, "Loading config"); let contents = fs::read_to_string(&config_path) - .into_diagnostic() - .wrap_err_with(|| format!("Failed to read config file {}", config_path))?; + .map_err(Error::ReadConfig)?; - knuffel::parse::(&config_path, &contents) - .wrap_err_with(|| format!("Failed to parse config file {}", config_path))? + contents.parse::() + .map_err(Error::BadConfig)? }); // Create the data structure used to store the rendered posts. This uses an `Arc` internally, @@ -65,30 +82,67 @@ fn main() -> miette::Result<()> { tokio::runtime::Builder::new_multi_thread() .enable_all() .build() - .into_diagnostic() - .wrap_err("Failed to create async runtime")? - .block_on(run(config, posts_store)) + .map_err(Error::TokioRuntime)? + .block_on(run_server(config, posts_store)) } -async fn run( +async fn run_server( config: Arc, posts_store: ConcurrentPostsStore, -) -> miette::Result<()> +) -> Result<(), Error> { - let bind_address = &config.bind - .parse() - .into_diagnostic() - .wrap_err_with(|| format!("Failed to parse socket address \"{}\"", config.bind))?; + let service = service::site_service(config.clone(), posts_store); - let service = service::site_service(config, posts_store); + info!(address = %config.bind, "Starting server"); - info!(address = %bind_address, "Starting server"); - - Server::try_bind(bind_address) - .into_diagnostic() - .wrap_err_with(|| format!("Failed to bind {}", bind_address))? + Server::try_bind(&config.bind) + .map_err(|err| Error::Bind(config.bind, err))? .serve(service.into_make_service()) .await - .into_diagnostic() - .wrap_err("Fatal error while running the server") + .map_err(Error::Server) } + +#[derive(Debug)] +enum Error { + NoConfig, + ReadConfig(io::Error), + BadConfig(toml::de::Error), + CreateWatcher(notify::Error), + WatchDir(PathBuf, notify::Error), + TokioRuntime(io::Error), + Bind(SocketAddr, hyper::Error), + Server(hyper::Error), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::NoConfig => { + write!(f, "no config file specified") + }, + Self::ReadConfig(err) => { + write!(f, "failed to read config file: {}", err) + }, + Self::BadConfig(err) => { + write!(f, "error in config: {}", err) + }, + Self::CreateWatcher(err) => { + write!(f, "failed to create filesystem watcher: {}", err) + }, + Self::WatchDir(path, err) => { + write!(f, "failed to watch directory {}: {}", path.to_string_lossy(), err) + }, + Self::TokioRuntime(err) => { + write!(f, "failed to create async runtime: {}", err) + }, + Self::Bind(addr, err) => { + write!(f, "failed to bind {}: {}", addr, err) + }, + Self::Server(err) => { + write!(f, "error while running server: {}", err) + }, + } + } +} + +impl error::Error for Error {} diff --git a/src/bin/blog_server/render.rs b/src/bin/blog_server/render.rs index f0f85f8..5f9407a 100644 --- a/src/bin/blog_server/render.rs +++ b/src/bin/blog_server/render.rs @@ -18,7 +18,7 @@ use blog::{ use crate::Config; -pub struct Renderer { +pub(crate) struct Renderer { config: Arc, posts: ConcurrentPostsStore, code_renderer: CodeBlockRenderer, @@ -27,7 +27,7 @@ pub struct Renderer { } impl Renderer { - pub fn new( + pub(crate) fn new( config: Arc, posts: ConcurrentPostsStore, code_renderer: CodeBlockRenderer, @@ -51,7 +51,7 @@ impl Renderer { } #[tracing::instrument(skip(self))] - pub fn handle_events(self) { + pub(crate) fn handle_events(self) { while let Ok(notify_event) = self.rx.recv() { let fs_event = match notify_event { // Convert create & write events for valid post file names to update events. @@ -204,7 +204,7 @@ impl Renderer { Post::new_from_str( &self.code_renderer, - *self.config.namespace_uuid, + self.config.namespace_uuid, target.id.clone(), updated, &contents @@ -221,8 +221,8 @@ enum Event { } struct EventTarget { - pub path: PathBuf, - pub id: Id, + path: PathBuf, + id: Id, } impl fmt::Debug for EventTarget { @@ -232,7 +232,7 @@ impl fmt::Debug for EventTarget { } impl EventTarget { - pub fn from_path(path: PathBuf) -> Option { + fn from_path(path: PathBuf) -> Option { path.file_name() .and_then(|file_name| file_name.to_str()) .and_then(Id::from_file_name) @@ -243,7 +243,7 @@ impl EventTarget { } } -pub enum Error { +pub(crate) enum Error { Io(Box), NotAFile, Parsing(Box), diff --git a/src/bin/blog_server/service/atom.rs b/src/bin/blog_server/service/atom.rs index 286a817..febc830 100644 --- a/src/bin/blog_server/service/atom.rs +++ b/src/bin/blog_server/service/atom.rs @@ -9,7 +9,7 @@ use crate::Config; use super::response::Atom; -pub async fn handle( +pub(super) async fn handle( Extension(config): Extension>, Extension(posts): Extension, ) -> Atom { @@ -50,7 +50,7 @@ pub async fn handle( Atom( atom::FeedBuilder::default() - .id(format!("urn:uuid:{}", *config.namespace_uuid)) + .id(format!("urn:uuid:{}", config.namespace_uuid)) .title(config.atom.title.clone()) .updated(updated) .links(vec![ diff --git a/src/bin/blog_server/service/contact.rs b/src/bin/blog_server/service/contact.rs index ea2f150..d562c80 100644 --- a/src/bin/blog_server/service/contact.rs +++ b/src/bin/blog_server/service/contact.rs @@ -4,7 +4,7 @@ use crate::template; use super::response::Html; -pub async fn handle() -> Html { +pub(super) async fn handle() -> Html { Html::new() .with_title_static("Contact") .with_crawler_permissive() diff --git a/src/bin/blog_server/service/index.rs b/src/bin/blog_server/service/index.rs index 74bd14a..8b47850 100644 --- a/src/bin/blog_server/service/index.rs +++ b/src/bin/blog_server/service/index.rs @@ -7,7 +7,7 @@ use crate::template; use super::response::Html; -pub async fn handle(Extension(posts): Extension) -> Html { +pub(super) async fn handle(Extension(posts): Extension) -> Html { Html::new() .with_title_static("Pantonshire") .with_crawler_permissive() diff --git a/src/bin/blog_server/service/mod.rs b/src/bin/blog_server/service/mod.rs index 02941eb..51d172e 100644 --- a/src/bin/blog_server/service/mod.rs +++ b/src/bin/blog_server/service/mod.rs @@ -8,4 +8,4 @@ mod rss; mod site; mod static_content; -pub use site::service as site_service; +pub(crate) use site::service as site_service; diff --git a/src/bin/blog_server/service/post.rs b/src/bin/blog_server/service/post.rs index 26725df..a463693 100644 --- a/src/bin/blog_server/service/post.rs +++ b/src/bin/blog_server/service/post.rs @@ -7,7 +7,7 @@ use crate::template; use super::response::{Error, Html}; -pub async fn handle( +pub(super) async fn handle( Path(post_id): Path, Extension(posts): Extension ) -> Result diff --git a/src/bin/blog_server/service/posts_list.rs b/src/bin/blog_server/service/posts_list.rs index 5ed4f00..05ca825 100644 --- a/src/bin/blog_server/service/posts_list.rs +++ b/src/bin/blog_server/service/posts_list.rs @@ -7,7 +7,7 @@ use crate::template; use super::response::Html; -pub async fn handle(Extension(posts): Extension) -> Html { +pub(super) async fn handle(Extension(posts): Extension) -> Html { Html::new() .with_title_static("Articles") .with_crawler_permissive() diff --git a/src/bin/blog_server/service/response.rs b/src/bin/blog_server/service/response.rs index 379f4a3..1775f4f 100644 --- a/src/bin/blog_server/service/response.rs +++ b/src/bin/blog_server/service/response.rs @@ -11,7 +11,7 @@ use axum::{ use maud::{html, Markup, Render, Escaper, DOCTYPE}; #[derive(Debug)] -pub enum Error { +pub(super) enum Error { Internal, PostNotFound, StaticResourceNotFound, @@ -62,7 +62,7 @@ impl IntoResponse for Error { } } -pub struct Html { +pub(super) struct Html { status: StatusCode, title: Cow<'static, str>, head: Option, @@ -71,7 +71,7 @@ pub struct Html { } impl Html { - pub fn new() -> Self { + pub(super) fn new() -> Self { Self { status: StatusCode::OK, title: Cow::Borrowed("untitled"), @@ -81,39 +81,39 @@ impl Html { } } - pub fn with_status(self, status: StatusCode) -> Self { + pub(super) fn with_status(self, status: StatusCode) -> Self { Self { status, ..self } } - pub fn with_title(self, title: Cow<'static, str>) -> Self { + pub(super) fn with_title(self, title: Cow<'static, str>) -> Self { Self { title, ..self } } - pub fn with_title_static(self, title: &'static str) -> Self { + pub(super) fn with_title_static(self, title: &'static str) -> Self { self.with_title(Cow::Borrowed(title)) } - pub fn with_title_owned(self, title: String) -> Self { + pub(super) fn with_title_owned(self, title: String) -> Self { self.with_title(Cow::Owned(title)) } - pub fn with_head(self, head: Markup) -> Self { + pub(super) fn with_head(self, head: Markup) -> Self { Self { head: Some(head), ..self } } - pub fn with_body(self, body: Markup) -> Self { + pub(super) fn with_body(self, body: Markup) -> Self { Self { body: Some(body), ..self } } - pub fn with_crawler_hints(self, crawler_hints: CrawlerHints) -> Self { + pub(super) fn with_crawler_hints(self, crawler_hints: CrawlerHints) -> Self { Self { crawler_hints, ..self } } - pub fn with_crawler_restrictive(self) -> Self { + pub(super) fn with_crawler_restrictive(self) -> Self { self.with_crawler_hints(CrawlerHints::restrictive()) } - pub fn with_crawler_permissive(self) -> Self { + pub(super) fn with_crawler_permissive(self) -> Self { self.with_crawler_hints(CrawlerHints::permissive()) } } @@ -160,7 +160,7 @@ impl IntoResponse for Html { } #[derive(Clone, Copy, Debug)] -pub struct CrawlerHints { +pub(super) struct CrawlerHints { index: bool, follow: bool, archive: bool, @@ -169,7 +169,7 @@ pub struct CrawlerHints { } impl CrawlerHints { - pub const fn restrictive() -> Self { + pub(super) const fn restrictive() -> Self { Self { index: false, follow: false, @@ -179,7 +179,7 @@ impl CrawlerHints { } } - pub const fn permissive() -> Self { + pub(super) const fn permissive() -> Self { Self { index: true, follow: true, @@ -189,23 +189,23 @@ impl CrawlerHints { } } - pub const fn with_index(self, index: bool) -> Self { + pub(super) const fn with_index(self, index: bool) -> Self { Self { index, ..self } } - pub const fn with_follow(self, follow: bool) -> Self { + pub(super) const fn with_follow(self, follow: bool) -> Self { Self { follow, ..self } } - pub const fn with_archive(self, archive: bool) -> Self { + pub(super) const fn with_archive(self, archive: bool) -> Self { Self { archive, ..self } } - pub const fn with_snippet(self, snippet: bool) -> Self { + pub(super) const fn with_snippet(self, snippet: bool) -> Self { Self { snippet, ..self } } - pub const fn with_image_index(self, image_index: bool) -> Self { + pub(super) const fn with_image_index(self, image_index: bool) -> Self { Self { image_index, ..self } } @@ -271,7 +271,7 @@ impl Render for CrawlerHints { } } -pub struct Rss(pub T); +pub(super) struct Rss(pub T); impl>> IntoResponse for Rss { fn into_response(self) -> Response { @@ -284,7 +284,7 @@ impl>> IntoResponse for Rss { } } -pub struct Atom(pub T); +pub(super) struct Atom(pub T); impl>> IntoResponse for Atom { fn into_response(self) -> Response { diff --git a/src/bin/blog_server/service/rss.rs b/src/bin/blog_server/service/rss.rs index 5d91f0d..c2dab07 100644 --- a/src/bin/blog_server/service/rss.rs +++ b/src/bin/blog_server/service/rss.rs @@ -14,7 +14,7 @@ use crate::Config; use super::response::Rss; -pub async fn handle( +pub(super) async fn handle( Extension(config): Extension>, Extension(posts): Extension, ) -> Rss { diff --git a/src/bin/blog_server/service/site.rs b/src/bin/blog_server/service/site.rs index d763ed6..840419d 100644 --- a/src/bin/blog_server/service/site.rs +++ b/src/bin/blog_server/service/site.rs @@ -26,7 +26,7 @@ use super::{ static_content, }; -pub fn service( +pub(crate) fn service( config: Arc, posts_store: ConcurrentPostsStore, ) -> Router @@ -55,7 +55,7 @@ pub fn service( .layer(Extension(posts_store)) } -pub async fn handle_fallback(uri: Uri) -> Error { +async fn handle_fallback(uri: Uri) -> Error { info!(path = %uri.path(), "Requested resource not found"); Error::RouteNotFound } diff --git a/src/bin/blog_server/service/static_content.rs b/src/bin/blog_server/service/static_content.rs index b1b4fa7..3275d74 100644 --- a/src/bin/blog_server/service/static_content.rs +++ b/src/bin/blog_server/service/static_content.rs @@ -18,7 +18,7 @@ use tracing::{info, error}; use super::response::Error; -pub fn file_service(file_path: &Path, mime: Option<&Mime>) -> MethodRouter { +pub(super) fn file_service(file_path: &Path, mime: Option<&Mime>) -> MethodRouter { let serve_file = match mime { Some(mime) => ServeFile::new_with_mime(file_path, mime), None => ServeFile::new(file_path), @@ -28,7 +28,7 @@ pub fn file_service(file_path: &Path, mime: Option<&Mime>) -> MethodRouter MethodRouter { +pub(super) fn dir_service(dir_path: &Path) -> MethodRouter { let fallback_service = handle_fallback .into_service() .map_err(Empty::elim::); @@ -40,12 +40,12 @@ pub fn dir_service(dir_path: &Path) -> MethodRouter { .handle_error(handle_error) } -pub async fn handle_fallback(uri: Uri) -> Error { +pub(super) async fn handle_fallback(uri: Uri) -> Error { info!(path = %uri.path(), "Requested static file not found"); Error::StaticResourceNotFound } -pub async fn handle_error(uri: Uri, err: io::Error) -> Error { +pub(super) async fn handle_error(uri: Uri, err: io::Error) -> Error { error!(path = %uri.path(), err = %err, "IO error"); Error::Internal } diff --git a/src/bin/blog_server/template.rs b/src/bin/blog_server/template.rs index aa98745..5543c86 100644 --- a/src/bin/blog_server/template.rs +++ b/src/bin/blog_server/template.rs @@ -1,6 +1,6 @@ use maud::{html, Markup}; -pub fn main_page(content: Markup) -> Markup { +pub(crate) fn main_page(content: Markup) -> Markup { html! { header #page_header { nav #page_nav { diff --git a/src/bin/prepublish/main.rs b/src/bin/prepublish/main.rs new file mode 100644 index 0000000..3033f62 --- /dev/null +++ b/src/bin/prepublish/main.rs @@ -0,0 +1,4 @@ + +fn main() { + +} diff --git a/src/lib/post/error.rs b/src/lib/post/error.rs index 31aa194..da9b476 100644 --- a/src/lib/post/error.rs +++ b/src/lib/post/error.rs @@ -1,18 +1,9 @@ use std::{error, fmt}; -use kdl::KdlError; - #[derive(Debug)] pub enum Error { NoDelim, - Syntax(Box), - FieldMissing { - field: &'static str, - }, - BadType { - field: &'static str, - expected: &'static str, - }, + Header(Box), IdTooLong(usize), } @@ -22,14 +13,8 @@ impl fmt::Display for Error { Self::NoDelim => { write!(f, "post has no header; no delimiter `\\n---\\n` found") }, - Self::Syntax(err) => { - write!(f, "syntax error in post header: {}", err) - }, - Self::FieldMissing { field } => { - write!(f, "missing required post header field `{}`", field) - }, - Self::BadType { field, expected } => { - write!(f, "expected post header field `{}` to be {}", field, expected) + Self::Header(err) => { + write!(f, "error decoding post header: {}", err) }, Self::IdTooLong(len) => { write!(f, "post id too long: {} bytes", len) @@ -40,8 +25,8 @@ impl fmt::Display for Error { impl error::Error for Error {} -impl From for Error { - fn from(err: KdlError) -> Self { - Self::Syntax(Box::new(err)) +impl From for Error { + fn from(err: toml::de::Error) -> Self { + Self::Header(Box::new(err)) } } diff --git a/src/lib/post/header.rs b/src/lib/post/header.rs index 43a7f2c..0863f7b 100644 --- a/src/lib/post/header.rs +++ b/src/lib/post/header.rs @@ -1,16 +1,19 @@ +use std::{fmt, str}; + use chrono::{DateTime, Utc}; -use kdl::KdlDocument; use libshire::strings::ShString22; - -use crate::time::{datetime_unix_seconds, unix_epoch}; +use serde::{Serialize, Deserialize}; use super::error::Error; +#[derive(Clone, Deserialize, Serialize, Debug)] pub struct Header { pub(super) title: String, pub(super) subtitle: Option, pub(super) author: ShString22, + #[serde(default)] pub(super) tags: Vec, + #[serde(default = "crate::time::unix_epoch")] pub(super) published: DateTime, } @@ -21,97 +24,74 @@ impl Header { &self.title } + #[inline] + #[must_use] + pub fn title_mut(&mut self) -> &mut String { + &mut self.title + } + #[inline] #[must_use] pub fn subtitle(&self) -> Option<&str> { self.subtitle.as_deref() } + #[inline] + #[must_use] + pub fn subtitle_mut(&mut self) -> &mut Option { + &mut self.subtitle + } + #[inline] #[must_use] pub fn author(&self) -> &str { &self.author } + #[inline] + #[must_use] + pub fn author_mut(&mut self) -> &mut ShString22 { + &mut self.author + } + #[inline] #[must_use] pub fn tags(&self) -> &[ShString22] { &self.tags } + #[inline] + #[must_use] + pub fn tags_mut(&mut self) -> &mut Vec { + &mut self.tags + } + #[inline] #[must_use] pub fn published(&self) -> DateTime { self.published } -} -impl<'a> TryFrom<&'a KdlDocument> for Header { - type Error = Error; - - fn try_from(doc: &'a KdlDocument) -> Result { - let title = doc - .get_arg("title") - .ok_or(Error::FieldMissing { field: "title" }) - .and_then(|value| value - .as_string() - .ok_or(Error::BadType { field: "title", expected: "string" }) - .map(|title| title.to_owned()))?; - - let subtitle = doc - .get_arg("subtitle") - .map(|value| value - .as_string() - .ok_or(Error::BadType { field: "subtitle", expected: "string" }) - .map(|subtitle| subtitle.to_owned())) - .transpose()?; - - let author = doc - .get_arg("title") - .ok_or(Error::FieldMissing { field: "author" }) - .and_then(|value| value - .as_string() - .ok_or(Error::BadType { field: "author", expected: "string" }) - .map(ShString22::from))?; - - let tags = doc - .get("tags") - .map(|node| node.entries()) - .unwrap_or_default() - .iter() - .filter_map(|entry| match entry.name() { - Some(_) => None, - None => Some(entry.value()), - }) - .map(|value| value - .as_string() - .ok_or(Error::BadType { field: "tag", expected: "string" }) - .map(ShString22::from)) - .collect::>()?; - - let published = doc - .get_arg("published") - .map(|value| value - .as_i64() - .ok_or(Error::BadType { field: "published", expected: "integer unix timestamp (seconds)" }) - .map(datetime_unix_seconds)) - .transpose()? - .unwrap_or_else(unix_epoch); - - Ok(Header { - title, - subtitle, - author, - tags, - published, - }) + #[inline] + #[must_use] + pub fn published_mut(&mut self) -> &mut DateTime { + &mut self.published } } -impl TryFrom for Header { - type Error = Error; +impl str::FromStr for Header { + type Err = Error; + + fn from_str(s: &str) -> Result { + toml::from_str(s) + .map_err(Error::from) + } +} - fn try_from(value: KdlDocument) -> Result { - Self::try_from(&value) +impl fmt::Display for Header { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + toml::to_string_pretty(self) + .map_err(|_| fmt::Error) + .and_then(|s| f.write_str(&s)) } } diff --git a/src/lib/post/markdown_post.rs b/src/lib/post/markdown_post.rs deleted file mode 100644 index c84f62d..0000000 --- a/src/lib/post/markdown_post.rs +++ /dev/null @@ -1,31 +0,0 @@ -use super::{error::Error, header::Header, source::PostSource}; - -pub struct MarkdownPost { - pub(super) header: Header, - pub(super) markdown: String, -} - -impl MarkdownPost { - #[inline] - #[must_use] - pub fn header(&self) -> &Header { - &self.header - } - - #[inline] - #[must_use] - pub fn markdown(&self) -> &str { - &self.markdown - } -} - -impl TryFrom for MarkdownPost { - type Error = Error; - - fn try_from(source: PostSource) -> Result { - Ok(Self { - header: source.header.try_into()?, - markdown: source.markdown, - }) - } -} diff --git a/src/lib/post/mod.rs b/src/lib/post/mod.rs index 2579a6a..da00852 100644 --- a/src/lib/post/mod.rs +++ b/src/lib/post/mod.rs @@ -1,7 +1,6 @@ mod error; mod id; mod header; -mod markdown_post; mod render; mod rendered_post; mod source; @@ -9,10 +8,9 @@ mod source; pub use error::Error; pub use header::Header; pub use id::Id; -pub use markdown_post::MarkdownPost; pub use rendered_post::RenderedPost; pub use source::PostSource; -const POST_FILE_EXTENSION: &str = ".kdl.md"; +const POST_FILE_EXTENSION: &str = ".toml.md"; pub type Post = RenderedPost; diff --git a/src/lib/post/rendered_post.rs b/src/lib/post/rendered_post.rs index 87bfa59..a89f50d 100644 --- a/src/lib/post/rendered_post.rs +++ b/src/lib/post/rendered_post.rs @@ -8,9 +8,8 @@ use super::{ error::Error, header::Header, id::Id, - markdown_post::MarkdownPost, - render::render_markdown, source::PostSource, + render::render_markdown, }; pub struct RenderedPost { @@ -30,19 +29,16 @@ impl RenderedPost { source: &str ) -> Result { - let markdown_post = source - .parse::() - .and_then(MarkdownPost::try_from)?; - - Self::new_from_markdown_post(code_renderer, namespace, id, updated, markdown_post) + let source = source.parse::()?; + Self::new_from_source(code_renderer, namespace, id, updated, source) } - pub fn new_from_markdown_post( + pub fn new_from_source( code_renderer: &CodeBlockRenderer, namespace: Uuid, id: Id, updated: Option>, - markdown_post: MarkdownPost + source: PostSource ) -> Result { let uuid = Uuid::new_v5(namespace, &*id) @@ -53,9 +49,9 @@ impl RenderedPost { Ok(Self { uuid, id, - header: markdown_post.header, + header: source.header, updated: updated.unwrap_or_else(unix_epoch), - html: render_markdown(code_renderer, &markdown_post.markdown), + html: render_markdown(code_renderer, &source.markdown), }) } diff --git a/src/lib/post/source.rs b/src/lib/post/source.rs index b71b09b..5d2106d 100644 --- a/src/lib/post/source.rs +++ b/src/lib/post/source.rs @@ -1,27 +1,25 @@ use std::{fmt, str}; -use kdl::KdlDocument; - -use super::error::Error; +use super::{error::Error, header::Header}; const DELIM: &str = "\n---\n"; #[derive(Clone, Debug)] pub struct PostSource { - pub(super) header: KdlDocument, + pub(super) header: Header, pub(super) markdown: String, } impl PostSource { #[inline] #[must_use] - pub fn header(&self) -> &KdlDocument { + pub fn header(&self) -> &Header { &self.header } #[inline] #[must_use] - pub fn header_mut(&mut self) -> &mut KdlDocument { + pub fn header_mut(&mut self) -> &mut Header { &mut self.header } @@ -53,7 +51,7 @@ impl str::FromStr for PostSource { } impl fmt::Display for PostSource { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}{}{}", self.header, DELIM, self.markdown) } }