Switch to TOML for configuration
This commit is contained in:
1 parent
ad3a0ff956
commit
d7a521e581
27 files changed
+275
-612
No files matched your search
+1
-1
@@ -1,6 +1,6 @@
|
||||
.DS_Store
|
||||
/target/
|
||||
/config.kdl
|
||||
/config.toml
|
||||
/content/static/fonts/
|
||||
/article_media/
|
||||
/test_posts/
|
||||
Generated
+24
-277
@@ -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"
|
||||
|
||||
+9
-5
@@ -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"] }
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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 str::FromStr for Config {
|
||||
type Err = toml::de::Error;
|
||||
|
||||
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<S: ErrorSpan> DecodeScalar<S> for Uuid {
|
||||
fn type_check(type_name: &Option<Spanned<TypeName, S>>, ctx: &mut Context<S>) {
|
||||
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",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_decode(
|
||||
value: &Spanned<Literal, S>,
|
||||
ctx: &mut Context<S>,
|
||||
) -> Result<Self, DecodeError<S>> {
|
||||
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<Self, Self::Err> {
|
||||
toml::from_str(s)
|
||||
}
|
||||
}
|
||||
@@ -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<DebouncedEvent>,
|
||||
watch_path: &Path
|
||||
) -> miette::Result<RecommendedWatcher>
|
||||
) -> Result<RecommendedWatcher, Error>
|
||||
{
|
||||
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");
|
||||
|
||||
|
||||
+81
-27
@@ -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>(&config_path, &contents)
|
||||
.wrap_err_with(|| format!("Failed to parse config file {}", config_path))?
|
||||
contents.parse::<Config>()
|
||||
.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<Config>,
|
||||
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 {}
|
||||
@@ -18,7 +18,7 @@ use blog::{
|
||||
|
||||
use crate::Config;
|
||||
|
||||
pub struct Renderer {
|
||||
pub(crate) struct Renderer {
|
||||
config: Arc<Config>,
|
||||
posts: ConcurrentPostsStore,
|
||||
code_renderer: CodeBlockRenderer,
|
||||
@@ -27,7 +27,7 @@ pub struct Renderer {
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub fn new(
|
||||
pub(crate) fn new(
|
||||
config: Arc<Config>,
|
||||
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<Self> {
|
||||
fn from_path(path: PathBuf) -> Option<Self> {
|
||||
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<io::Error>),
|
||||
NotAFile,
|
||||
Parsing(Box<ParseError>),
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::Config;
|
||||
|
||||
use super::response::Atom;
|
||||
|
||||
pub async fn handle(
|
||||
pub(super) async fn handle(
|
||||
Extension(config): Extension<Arc<Config>>,
|
||||
Extension(posts): Extension<ConcurrentPostsStore>,
|
||||
) -> Atom<Bytes> {
|
||||
@@ -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![
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::template;
|
||||
|
||||
use super::response::Html;
|
||||
|
||||
pub async fn handle(Extension(posts): Extension<ConcurrentPostsStore>) -> Html {
|
||||
pub(super) async fn handle(Extension(posts): Extension<ConcurrentPostsStore>) -> Html {
|
||||
Html::new()
|
||||
.with_title_static("Pantonshire")
|
||||
.with_crawler_permissive()
|
||||
|
||||
@@ -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;
|
||||
@@ -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<String>,
|
||||
Extension(posts): Extension<ConcurrentPostsStore>
|
||||
) -> Result<Html, Error>
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::template;
|
||||
|
||||
use super::response::Html;
|
||||
|
||||
pub async fn handle(Extension(posts): Extension<ConcurrentPostsStore>) -> Html {
|
||||
pub(super) async fn handle(Extension(posts): Extension<ConcurrentPostsStore>) -> Html {
|
||||
Html::new()
|
||||
.with_title_static("Articles")
|
||||
.with_crawler_permissive()
|
||||
|
||||
@@ -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<Markup>,
|
||||
@@ -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<T>(pub T);
|
||||
pub(super) struct Rss<T>(pub T);
|
||||
|
||||
impl<T: Into<Full<Bytes>>> IntoResponse for Rss<T> {
|
||||
fn into_response(self) -> Response {
|
||||
@@ -284,7 +284,7 @@ impl<T: Into<Full<Bytes>>> IntoResponse for Rss<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Atom<T>(pub T);
|
||||
pub(super) struct Atom<T>(pub T);
|
||||
|
||||
impl<T: Into<Full<Bytes>>> IntoResponse for Atom<T> {
|
||||
fn into_response(self) -> Response {
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::Config;
|
||||
|
||||
use super::response::Rss;
|
||||
|
||||
pub async fn handle(
|
||||
pub(super) async fn handle(
|
||||
Extension(config): Extension<Arc<Config>>,
|
||||
Extension(posts): Extension<ConcurrentPostsStore>,
|
||||
) -> Rss<Bytes> {
|
||||
|
||||
@@ -26,7 +26,7 @@ use super::{
|
||||
static_content,
|
||||
};
|
||||
|
||||
pub fn service(
|
||||
pub(crate) fn service(
|
||||
config: Arc<Config>,
|
||||
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
|
||||
}
|
||||
@@ -18,7 +18,7 @@ use tracing::{info, error};
|
||||
|
||||
use super::response::Error;
|
||||
|
||||
pub fn file_service(file_path: &Path, mime: Option<&Mime>) -> MethodRouter<Body, Infallible> {
|
||||
pub(super) fn file_service(file_path: &Path, mime: Option<&Mime>) -> MethodRouter<Body, Infallible> {
|
||||
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<Body,
|
||||
.handle_error(handle_error)
|
||||
}
|
||||
|
||||
pub fn dir_service(dir_path: &Path) -> MethodRouter<Body, Infallible> {
|
||||
pub(super) fn dir_service(dir_path: &Path) -> MethodRouter<Body, Infallible> {
|
||||
let fallback_service = handle_fallback
|
||||
.into_service()
|
||||
.map_err(Empty::elim::<io::Error>);
|
||||
@@ -40,12 +40,12 @@ pub fn dir_service(dir_path: &Path) -> MethodRouter<Body, Infallible> {
|
||||
.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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
fn main() {
|
||||
|
||||
}
|
||||
+6
-21
@@ -1,18 +1,9 @@
|
||||
use std::{error, fmt};
|
||||
|
||||
use kdl::KdlError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
NoDelim,
|
||||
Syntax(Box<KdlError>),
|
||||
FieldMissing {
|
||||
field: &'static str,
|
||||
},
|
||||
BadType {
|
||||
field: &'static str,
|
||||
expected: &'static str,
|
||||
},
|
||||
Header(Box<toml::de::Error>),
|
||||
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<KdlError> for Error {
|
||||
fn from(err: KdlError) -> Self {
|
||||
Self::Syntax(Box::new(err))
|
||||
impl From<toml::de::Error> for Error {
|
||||
fn from(err: toml::de::Error) -> Self {
|
||||
Self::Header(Box::new(err))
|
||||
}
|
||||
}
|
||||
+48
-68
@@ -1,16 +1,19 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use kdl::KdlDocument;
|
||||
use libshire::strings::ShString22;
|
||||
use std::{fmt, str};
|
||||
|
||||
use crate::time::{datetime_unix_seconds, unix_epoch};
|
||||
use chrono::{DateTime, Utc};
|
||||
use libshire::strings::ShString22;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use super::error::Error;
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize, Debug)]
|
||||
pub struct Header {
|
||||
pub(super) title: String,
|
||||
pub(super) subtitle: Option<String>,
|
||||
pub(super) author: ShString22,
|
||||
#[serde(default)]
|
||||
pub(super) tags: Vec<ShString22>,
|
||||
#[serde(default = "crate::time::unix_epoch")]
|
||||
pub(super) published: DateTime<Utc>,
|
||||
}
|
||||
|
||||
@@ -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<String> {
|
||||
&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<ShString22> {
|
||||
&mut self.tags
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn published(&self) -> DateTime<Utc> {
|
||||
self.published
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a KdlDocument> for Header {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(doc: &'a KdlDocument) -> Result<Self, Self::Error> {
|
||||
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::<Result<_, _>>()?;
|
||||
|
||||
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<Utc> {
|
||||
&mut self.published
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<KdlDocument> for Header {
|
||||
type Error = Error;
|
||||
impl str::FromStr for Header {
|
||||
type Err = Error;
|
||||
|
||||
fn try_from(value: KdlDocument) -> Result<Self, Self::Error> {
|
||||
Self::try_from(&value)
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
toml::from_str(s)
|
||||
.map_err(Error::from)
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -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<PostSource> for MarkdownPost {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(source: PostSource) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
header: source.header.try_into()?,
|
||||
markdown: source.markdown,
|
||||
})
|
||||
}
|
||||
}
|
||||
+1
-3
@@ -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;
|
||||
@@ -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<Self, Error>
|
||||
{
|
||||
let markdown_post = source
|
||||
.parse::<PostSource>()
|
||||
.and_then(MarkdownPost::try_from)?;
|
||||
|
||||
Self::new_from_markdown_post(code_renderer, namespace, id, updated, markdown_post)
|
||||
let source = source.parse::<PostSource>()?;
|
||||
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<DateTime<Utc>>,
|
||||
markdown_post: MarkdownPost
|
||||
source: PostSource
|
||||
) -> Result<Self, Error>
|
||||
{
|
||||
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),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user