Initial blog setup
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/target
|
||||
/.nvim.lua
|
||||
/.nvim.workspace.lua
|
||||
999
Cargo.lock
generated
Normal file
999
Cargo.lock
generated
Normal file
@@ -0,0 +1,999 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"itoa",
|
||||
"matchit",
|
||||
"memchr",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustversion",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_path_to_error",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tower 0.5.2",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"rustversion",
|
||||
"sync_wrapper",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "blog-server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"chrono",
|
||||
"once_cell",
|
||||
"pulldown-cmark",
|
||||
"serde",
|
||||
"tokio",
|
||||
"tower 0.4.13",
|
||||
"tower-http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body-util"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-range-header"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||
|
||||
[[package]]
|
||||
name = "httpdate"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"getopts",
|
||||
"memchr",
|
||||
"pulldown-cmark-escape",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark-escape"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_path_to_error"
|
||||
version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
||||
dependencies = [
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project-lite",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"http-range-header",
|
||||
"httpdate",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "blog-server"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
axum = "0.7"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tower = "0.4"
|
||||
tower-http = { version = "0.5", features = ["fs"] }
|
||||
pulldown-cmark = "0.12"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
chrono = "0.4"
|
||||
once_cell = "1.19"
|
||||
173
project_templates/.nvim.lua
Normal file
173
project_templates/.nvim.lua
Normal file
@@ -0,0 +1,173 @@
|
||||
-- Load persistent configuration from .workspace.lua
|
||||
local ws_file = "./.nvim.workspace.lua"
|
||||
local loaded, workspace = pcall(dofile, ws_file)
|
||||
if not loaded then
|
||||
workspace = { args = {}, build_type = "debug", binary = "blog-server" }
|
||||
end
|
||||
|
||||
local build_folder
|
||||
local bin_target
|
||||
local bin_name
|
||||
|
||||
local function updateBuildEnv()
|
||||
build_folder = './target/' .. workspace.build_type
|
||||
bin_target = workspace.binary
|
||||
bin_name = build_folder .. '/' .. bin_target
|
||||
|
||||
-- The run (F6) arguments
|
||||
vim.opt.makeprg = "cargo build"
|
||||
vim.g.cargo_makeprg_params = 'build'
|
||||
if workspace.build_type == "release" then
|
||||
vim.opt.makeprg = vim.opt.makeprg .. " --release"
|
||||
vim.g.cargo_makeprg_params = vim.g.cargo_makeprg_params .. " --release"
|
||||
end
|
||||
|
||||
-- Rust compiler error format
|
||||
vim.opt.errorformat = {
|
||||
-- Main error/warning line with file:line:col format
|
||||
'%E%>error%m,' .. -- Start of error block
|
||||
'%W%>warning: %m,' .. -- Start of warning block
|
||||
'%-G%>help: %m,' .. -- Ignore help lines
|
||||
'%-G%>note: %m,' .. -- Ignore note lines
|
||||
'%C%> --> %f:%l:%c,' .. -- Continuation: file location
|
||||
'%Z%>%p^%m,' .. -- End: column pointer (^^^)
|
||||
'%C%>%s%#|%.%#,' .. -- Continuation: context lines with |
|
||||
'%C%>%s%#%m,' .. -- Continuation: other context
|
||||
'%-G%.%#' -- Ignore everything else
|
||||
}
|
||||
end
|
||||
|
||||
updateBuildEnv()
|
||||
|
||||
-- Prevent Vim's built-in rust ftplugin from loading the cargo compiler
|
||||
vim.api.nvim_create_autocmd("FileType", {
|
||||
pattern = "rust",
|
||||
callback = function()
|
||||
vim.b.current_compiler = 'custom'
|
||||
end,
|
||||
})
|
||||
|
||||
local function writeWorkspace()
|
||||
-- A very minimal serializer for workspace configuration
|
||||
local s = { l = "", ls = {}, i = "" }
|
||||
local function w(v) s.l = s.l .. v end
|
||||
local function nl()
|
||||
s.ls[#s.ls + 1] = s.l; s.l = s.i;
|
||||
end
|
||||
local function wv(v)
|
||||
local t = type(v)
|
||||
if t == 'table' then
|
||||
w('{'); local pi = s.i; s.i = s.i .. " "
|
||||
for k1, v1 in pairs(v) do
|
||||
nl(); w('['); wv(k1); w('] = '); wv(v1); w(',')
|
||||
end
|
||||
s.i = pi; nl(); w('}');
|
||||
elseif t == 'number' then
|
||||
w(tostring(v))
|
||||
elseif t == 'string' then
|
||||
w('"' .. v .. '"')
|
||||
else
|
||||
w(tostring(v))
|
||||
end
|
||||
end
|
||||
|
||||
-- Write the workspace file
|
||||
w("return "); wv(workspace); nl()
|
||||
vim.fn.writefile(s.ls, ws_file)
|
||||
end
|
||||
|
||||
-- nvim-dap configuration
|
||||
local dap_ok, dap = pcall(require, "dap")
|
||||
local dap_def_cfg
|
||||
local dap_configs
|
||||
|
||||
-- Update args for both run and debug configs
|
||||
local function updateArgs(args)
|
||||
workspace.args = args
|
||||
if dap_configs ~= nil then dap_configs[1].args = args end
|
||||
writeWorkspace()
|
||||
end
|
||||
|
||||
-- Find terminal tab or create new one, then run command
|
||||
local function TermRun(cmd)
|
||||
local found = false
|
||||
for i = 1, vim.fn.tabpagenr('$') do
|
||||
vim.cmd('tabnext ' .. i)
|
||||
if vim.bo.buftype == 'terminal' then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not found then vim.cmd('tabnew | terminal') end
|
||||
vim.fn.chansend(vim.b.terminal_job_id, '' .. cmd .. '\n')
|
||||
vim.fn.feedkeys("G", "n")
|
||||
end
|
||||
|
||||
-- The Configure command
|
||||
vim.api.nvim_create_user_command("Configure", function(a)
|
||||
local args = {}
|
||||
local bt = "debug"
|
||||
if #a.args > 0 then bt = a.args end
|
||||
workspace.build_type = bt
|
||||
updateBuildEnv()
|
||||
writeWorkspace()
|
||||
end, { nargs = '?', desc = "Update run/debug arguments" })
|
||||
|
||||
vim.api.nvim_create_user_command("Args", function(a) updateArgs(a.fargs) end,
|
||||
{ nargs = '*', desc = "Update run/debug arguments" })
|
||||
|
||||
if dap_ok then
|
||||
local rust_path = vim.fn.system("rustc --print sysroot") -- We need to query this to get the sysroot
|
||||
rust_path = string.sub(rust_path, 1, #rust_path - 1) -- trim trailing newline
|
||||
local lldb_init = {
|
||||
"command script import " .. rust_path .. "/lib/rustlib/etc/lldb_lookup.py",
|
||||
"command source " .. rust_path .. "/lib/rustlib/etc/lldb_commands",
|
||||
}
|
||||
dap_configs = {
|
||||
{
|
||||
name = 'test all',
|
||||
type = 'codelldb',
|
||||
request = 'launch',
|
||||
program = bin_name,
|
||||
args = workspace.args,
|
||||
cwd = '${workspaceFolder}',
|
||||
stopOnEntry = false,
|
||||
initCommands = lldb_init
|
||||
}
|
||||
}
|
||||
dap_def_cfg = dap_configs[1]
|
||||
dap.providers.configs["project"] = function()
|
||||
return dap_configs
|
||||
end
|
||||
|
||||
-- DebugArgs to set debugger arguments and run immediately
|
||||
vim.api.nvim_create_user_command("DebugArgs", function(a)
|
||||
updateArgs(a.fargs)
|
||||
dap.run(dap_configs[1])
|
||||
end, { nargs = '*', desc = "Starts debugging with specified arguments" })
|
||||
end
|
||||
|
||||
if MakeAnd then
|
||||
local r = function()
|
||||
MakeAnd(function()
|
||||
TermRun(bin_name .. " " .. table.concat(workspace.args, " "))
|
||||
end)
|
||||
end
|
||||
-- RunArgs sets the run arguments that F6 uses and reruns immediately
|
||||
vim.api.nvim_create_user_command("RunArgs", function(a)
|
||||
updateArgs(a.fargs)
|
||||
r()
|
||||
end, { nargs = '*', desc = "Starts debugging with specified arguments" })
|
||||
|
||||
-- F6 to run the application
|
||||
vim.keymap.set('n', '<F6>', r)
|
||||
|
||||
if dap_ok then
|
||||
-- Shift-F5 to launch default config
|
||||
vim.keymap.set('n', '<F17>', function()
|
||||
MakeAnd(function()
|
||||
dap.run(dap_def_cfg)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
end
|
||||
61
src/git_tracker.rs
Normal file
61
src/git_tracker.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct GitVersion {
|
||||
pub branch: String,
|
||||
pub commit: String,
|
||||
}
|
||||
|
||||
pub fn get_git_version(git_path: &PathBuf) -> Result<GitVersion, String> {
|
||||
let head_path = git_path.join(".git/HEAD");
|
||||
|
||||
// Read HEAD file
|
||||
let head_content = fs::read_to_string(&head_path)
|
||||
.map_err(|e| format!("Failed to read .git/HEAD: {}", e))?;
|
||||
|
||||
let head_content = head_content.trim();
|
||||
|
||||
// Check if HEAD points to a ref
|
||||
if head_content.starts_with("ref:") {
|
||||
let ref_path = head_content.strip_prefix("ref:").unwrap().trim();
|
||||
let full_ref_path = git_path.join(".git").join(ref_path);
|
||||
|
||||
// Read the ref file to get the commit hash
|
||||
let commit_hash = fs::read_to_string(&full_ref_path)
|
||||
.map_err(|e| format!("Failed to read ref file: {}", e))?
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
// Extract branch name from ref path
|
||||
let branch = ref_path
|
||||
.strip_prefix("refs/heads/")
|
||||
.unwrap_or(ref_path)
|
||||
.to_string();
|
||||
|
||||
Ok(GitVersion {
|
||||
branch,
|
||||
commit: commit_hash,
|
||||
})
|
||||
} else {
|
||||
// Detached HEAD state - just use the commit hash
|
||||
Ok(GitVersion {
|
||||
branch: "detached".to_string(),
|
||||
commit: head_content.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_git_version() {
|
||||
// This test will only work if run in a git repository
|
||||
if let Ok(version) = get_git_version(".") {
|
||||
assert!(!version.commit.is_empty());
|
||||
assert!(!version.branch.is_empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
160
src/main.rs
Normal file
160
src/main.rs
Normal file
@@ -0,0 +1,160 @@
|
||||
mod git_tracker;
|
||||
mod post_manager;
|
||||
mod template_engine;
|
||||
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
response::{Html, IntoResponse},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::RwLock;
|
||||
use tower_http::services::ServeDir;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AppState {
|
||||
post_manager: Arc<RwLock<post_manager::PostManager>>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let post_manager = Arc::new(RwLock::new(
|
||||
post_manager::PostManager::new(".").expect("Failed to initialize post manager"),
|
||||
));
|
||||
|
||||
let app_state = AppState {
|
||||
post_manager: post_manager.clone(),
|
||||
};
|
||||
|
||||
let p_router = Router::new()
|
||||
.route("/:post_name", get(post_handler))
|
||||
.fallback_service(ServeDir::new("posts"));
|
||||
|
||||
let app = Router::new()
|
||||
.route("/", get(index_handler))
|
||||
.route("/update", get(update_handler))
|
||||
.route("/:page", get(all_handler))
|
||||
.nest("/p", p_router)
|
||||
.with_state(app_state);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
|
||||
.await
|
||||
.expect("Failed to bind to address");
|
||||
|
||||
println!("Server running on http://127.0.0.1:3000");
|
||||
|
||||
axum::serve(listener, app)
|
||||
.await
|
||||
.expect("Failed to start server");
|
||||
}
|
||||
|
||||
async fn index_handler(State(state): State<AppState>) -> impl IntoResponse {
|
||||
match render_template("index.html", &state).await {
|
||||
Ok(html) => Html(html).into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn all_handler(State(state): State<AppState>, Path(page): Path<String>) -> impl IntoResponse {
|
||||
if page.contains("..") {
|
||||
return (StatusCode::NOT_FOUND, "Invalid path").into_response();
|
||||
}
|
||||
match render_template(&format!("page_{}.html", page), &state).await {
|
||||
Ok(html) => Html(html).into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e).into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn post_handler(
|
||||
State(state): State<AppState>,
|
||||
Path(post_name): Path<String>,
|
||||
) -> impl IntoResponse {
|
||||
if post_name.contains("..") {
|
||||
return (StatusCode::NOT_FOUND, "Invalid path").into_response();
|
||||
}
|
||||
|
||||
let manager = state.post_manager.read().await;
|
||||
match manager.get_post(&post_name) {
|
||||
Some(_post) => {
|
||||
drop(manager);
|
||||
match render_post_template(&state, post_name).await {
|
||||
Ok(html) => Html(html).into_response(),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e).into_response(),
|
||||
}
|
||||
}
|
||||
None => (StatusCode::NOT_FOUND, "Post not found").into_response(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_handler(State(state): State<AppState>) -> impl IntoResponse {
|
||||
// Run git pull --autostash
|
||||
let output = Command::new("git")
|
||||
.arg("pull")
|
||||
.arg("--autostash")
|
||||
.output()
|
||||
.await;
|
||||
|
||||
match output {
|
||||
Ok(output) => {
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
eprintln!("Git pull failed: {}", stderr);
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Git pull failed: {}", stderr),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
println!("Git pull output: {}", stdout);
|
||||
|
||||
// Refresh posts
|
||||
let mut manager = state.post_manager.write().await;
|
||||
match manager.refresh_posts() {
|
||||
Ok(_) => {
|
||||
println!("Successfully refreshed log pages");
|
||||
(StatusCode::OK, "Update successful: pulled changes and refreshed log pages")
|
||||
.into_response()
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Failed to refresh posts: {}", e);
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Failed to refresh posts: {}", e),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Failed to execute git pull: {}", e);
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Failed to execute git pull: {}", e),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async fn render_template(template_name: &str, state: &AppState) -> Result<String, String> {
|
||||
let template_path = format!("templates/{}", template_name);
|
||||
let template_content = std::fs::read_to_string(&template_path)
|
||||
.map_err(|e| format!("Failed to read template: {}", e))?;
|
||||
|
||||
let manager = state.post_manager.read().await;
|
||||
template_engine::render_template(&template_content, &*manager, None)
|
||||
}
|
||||
|
||||
async fn render_post_template(state: &AppState, post_name: String) -> Result<String, String> {
|
||||
let template_content = std::fs::read_to_string("templates/post.html")
|
||||
.map_err(|e| format!("Failed to read post template: {}", e))?;
|
||||
|
||||
let manager = state.post_manager.read().await;
|
||||
template_engine::render_template(&template_content, &*manager, Some(&post_name))
|
||||
}
|
||||
204
src/post_manager.rs
Normal file
204
src/post_manager.rs
Normal file
@@ -0,0 +1,204 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use pulldown_cmark::{html, Options, Parser};
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Post {
|
||||
pub name: String,
|
||||
#[allow(dead_code)]
|
||||
pub filename: String,
|
||||
#[allow(dead_code)]
|
||||
pub markdown_content: String,
|
||||
pub html_content: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub modified_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
struct Entry {
|
||||
#[allow(dead_code)]
|
||||
short_name: String,
|
||||
file_path: String,
|
||||
created_at: DateTime<Utc>,
|
||||
modified_at: DateTime<Utc>,
|
||||
status: i32,
|
||||
}
|
||||
|
||||
pub struct PostManager {
|
||||
root_dir: PathBuf,
|
||||
posts_dir_rel: String,
|
||||
posts: HashMap<String, Post>,
|
||||
}
|
||||
|
||||
impl PostManager {
|
||||
pub fn new(root_dir: &str) -> Result<Self, String> {
|
||||
let root_dir = PathBuf::from(root_dir);
|
||||
let posts_dir_rel = "posts";
|
||||
|
||||
let mut manager = PostManager {
|
||||
root_dir,
|
||||
posts_dir_rel: posts_dir_rel.to_string(),
|
||||
posts: HashMap::new(),
|
||||
};
|
||||
|
||||
manager.refresh_posts()?;
|
||||
Ok(manager)
|
||||
}
|
||||
|
||||
pub fn get_root_dir(&self) -> &PathBuf {
|
||||
&self.root_dir
|
||||
}
|
||||
|
||||
fn query_posts(&self) -> Result<HashMap<String, Entry>, String> {
|
||||
let output = Command::new("git")
|
||||
.arg("log")
|
||||
.arg("--raw")
|
||||
.arg("--no-merges")
|
||||
.arg("--pretty=%h - %ad - %s")
|
||||
.arg("--date=unix")
|
||||
.arg("--")
|
||||
.arg(self.posts_dir_rel.clone())
|
||||
.current_dir(&self.root_dir)
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to execute git whatchanged: {}", e))?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(format!(
|
||||
"Git whatchanged command failed: {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
));
|
||||
}
|
||||
|
||||
let log_output = String::from_utf8_lossy(&output.stdout);
|
||||
let mut result: HashMap<String, Entry> = HashMap::new();
|
||||
let mut current_timestamp: Option<DateTime<Utc>> = None;
|
||||
|
||||
for line in log_output.lines() {
|
||||
let line = line.trim();
|
||||
|
||||
// Skip empty lines
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse commit header line: "f4fcf0e - 1761305168 - New post"
|
||||
if !line.starts_with(':') {
|
||||
if let Some(dash_pos) = line.find(" - ") {
|
||||
let after_first_dash = &line[dash_pos + 3..];
|
||||
if let Some(second_dash_pos) = after_first_dash.find(" - ") {
|
||||
let timestamp_str = after_first_dash[..second_dash_pos].trim();
|
||||
if let Ok(timestamp) = timestamp_str.parse::<i64>() {
|
||||
current_timestamp = DateTime::from_timestamp(timestamp, 0);
|
||||
eprintln!("Timestamp: {:?} ({})", current_timestamp, timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse file change line: ":000000 100644 0000000 6bdad65 A posts/hello-world-2.md"
|
||||
if line.starts_with(':') {
|
||||
eprintln!("Parsing line: {}", line);
|
||||
if let Some(timestamp) = current_timestamp {
|
||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||
if parts.len() >= 6 {
|
||||
let status = parts[4]; // A (add), D (delete), M (modify)
|
||||
let file_path = parts[5];
|
||||
|
||||
let fn_ = file_path
|
||||
.strip_prefix("posts/")
|
||||
.and_then(|s| s.strip_suffix(".md"));
|
||||
if let Some(name) = fn_ {
|
||||
let entry = result.entry(name.to_string()).or_insert(Entry {
|
||||
short_name: name.to_string(),
|
||||
file_path: file_path.to_string(),
|
||||
created_at: timestamp,
|
||||
modified_at: timestamp,
|
||||
status: if status == "D" { -1 } else { 1 },
|
||||
});
|
||||
|
||||
// Always use the oldest timestamp for posts creation dates
|
||||
entry.created_at = timestamp;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintln!("Invalid git log output, expected prior timestamp: {}", line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn refresh_posts(&mut self) -> Result<(), String> {
|
||||
self.posts.clear();
|
||||
|
||||
// Get timestamps from git history
|
||||
let query_result = self.query_posts()?;
|
||||
for (name, entry) in query_result {
|
||||
if entry.status != 1 {
|
||||
continue;
|
||||
}
|
||||
let markdown_content = fs::read_to_string(&entry.file_path)
|
||||
.map_err(|e| format!("Failed to read post file: {}", e))?;
|
||||
let html_content = markdown_to_html(&markdown_content);
|
||||
let post = Post {
|
||||
name: name.clone(),
|
||||
filename: entry.file_path,
|
||||
markdown_content,
|
||||
html_content,
|
||||
created_at: entry.created_at,
|
||||
modified_at: entry.modified_at,
|
||||
};
|
||||
eprintln!("Loaded post: {} ({})", name, entry.created_at);
|
||||
self.posts.insert(name, post);
|
||||
}
|
||||
|
||||
println!("Loaded {} posts", self.posts.len());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_post(&self, name: &str) -> Option<&Post> {
|
||||
self.posts.get(name)
|
||||
}
|
||||
|
||||
// Get all posts, sorted by creation date
|
||||
pub fn get_all_posts(&self) -> Vec<&Post> {
|
||||
let mut posts: Vec<&Post> = self.posts.values().collect();
|
||||
posts.sort_by(|a, b| b.created_at.cmp(&a.created_at));
|
||||
posts
|
||||
}
|
||||
|
||||
// Get the timstamp of when the blog was most recently updated
|
||||
// derived from latest post update
|
||||
pub fn get_update_timestamp(&self) -> Result<DateTime<Utc>, String> {
|
||||
let mut posts: Vec<&Post> = self.posts.values().collect();
|
||||
posts.sort_by(|a, b| b.modified_at.cmp(&a.modified_at));
|
||||
Ok(posts
|
||||
.first()
|
||||
.ok_or("No posts found".to_string())?
|
||||
.created_at)
|
||||
}
|
||||
|
||||
pub fn get_posts_limited(&self, limit: usize) -> Vec<&Post> {
|
||||
let mut posts = self.get_all_posts();
|
||||
posts.truncate(limit);
|
||||
posts
|
||||
}
|
||||
}
|
||||
|
||||
fn markdown_to_html(markdown: &str) -> String {
|
||||
let mut options = Options::empty();
|
||||
options.insert(Options::ENABLE_STRIKETHROUGH);
|
||||
options.insert(Options::ENABLE_TABLES);
|
||||
options.insert(Options::ENABLE_FOOTNOTES);
|
||||
options.insert(Options::ENABLE_TASKLISTS);
|
||||
options.insert(Options::ENABLE_SMART_PUNCTUATION);
|
||||
|
||||
let parser = Parser::new_ext(markdown, options);
|
||||
let mut html_output = String::new();
|
||||
html::push_html(&mut html_output, parser);
|
||||
html_output
|
||||
}
|
||||
270
src/template_engine.rs
Normal file
270
src/template_engine.rs
Normal file
@@ -0,0 +1,270 @@
|
||||
use crate::post_manager::PostManager;
|
||||
use crate::git_tracker::get_git_version;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
|
||||
pub fn render_template(
|
||||
template: &str,
|
||||
post_manager: &PostManager,
|
||||
current_post: Option<&str>,
|
||||
) -> Result<String, String> {
|
||||
parse_template(template, post_manager, current_post, 0)
|
||||
}
|
||||
|
||||
fn parse_template(
|
||||
template: &str,
|
||||
post_manager: &PostManager,
|
||||
current_post: Option<&str>,
|
||||
depth: usize,
|
||||
) -> Result<String, String> {
|
||||
const MAX_DEPTH: usize = 10;
|
||||
if depth > MAX_DEPTH {
|
||||
return Err("Too many nested includes (max 10)".to_string());
|
||||
}
|
||||
|
||||
let mut result = String::new();
|
||||
let chars: Vec<char> = template.chars().collect();
|
||||
let mut i = 0;
|
||||
|
||||
while i < chars.len() {
|
||||
// Check for $< tag start
|
||||
if i + 1 < chars.len() && chars[i] == '$' && chars[i + 1] == '<' {
|
||||
// Try to parse tag
|
||||
if let Some((tag_name, attrs, end_pos)) = parse_tag(&chars, i) {
|
||||
// Process tag immediately based on type
|
||||
let content = process_tag(&tag_name, &attrs, post_manager, current_post, depth)?;
|
||||
result.push_str(&content);
|
||||
i = end_pos;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Regular character
|
||||
result.push(chars[i]);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn parse_tag(chars: &[char], start: usize) -> Option<(String, HashMap<String, String>, usize)> {
|
||||
let mut i = start + 2; // Skip $<
|
||||
|
||||
// Skip whitespace
|
||||
while i < chars.len() && chars[i].is_whitespace() {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// Parse tag name
|
||||
let mut tag_name = String::new();
|
||||
while i < chars.len() && !chars[i].is_whitespace() && chars[i] != '/' && chars[i] != '>' {
|
||||
tag_name.push(chars[i]);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if tag_name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Parse attributes
|
||||
let mut attrs = HashMap::new();
|
||||
|
||||
loop {
|
||||
// Skip whitespace
|
||||
while i < chars.len() && chars[i].is_whitespace() {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// Check for end of tag
|
||||
if i >= chars.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if chars[i] == '/' {
|
||||
i += 1;
|
||||
if i < chars.len() && chars[i] == '>' {
|
||||
return Some((tag_name, attrs, i + 1));
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
if chars[i] == '>' {
|
||||
return Some((tag_name, attrs, i + 1));
|
||||
}
|
||||
|
||||
// Parse attribute name
|
||||
let mut attr_name = String::new();
|
||||
while i < chars.len() && chars[i] != '=' && !chars[i].is_whitespace() {
|
||||
attr_name.push(chars[i]);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if attr_name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Skip whitespace
|
||||
while i < chars.len() && chars[i].is_whitespace() {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// Expect =
|
||||
if i >= chars.len() || chars[i] != '=' {
|
||||
return None;
|
||||
}
|
||||
i += 1;
|
||||
|
||||
// Skip whitespace
|
||||
while i < chars.len() && chars[i].is_whitespace() {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// Parse attribute value (quoted)
|
||||
if i >= chars.len() || chars[i] != '"' {
|
||||
return None;
|
||||
}
|
||||
i += 1; // Skip opening quote
|
||||
|
||||
let mut attr_value = String::new();
|
||||
while i < chars.len() && chars[i] != '"' {
|
||||
attr_value.push(chars[i]);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if i >= chars.len() {
|
||||
return None;
|
||||
}
|
||||
i += 1; // Skip closing quote
|
||||
|
||||
attrs.insert(attr_name, attr_value);
|
||||
}
|
||||
}
|
||||
|
||||
fn process_tag(
|
||||
tag_name: &str,
|
||||
attrs: &HashMap<String, String>,
|
||||
post_manager: &PostManager,
|
||||
current_post: Option<&str>,
|
||||
depth: usize,
|
||||
) -> Result<String, String> {
|
||||
match tag_name {
|
||||
"include" => {
|
||||
let src = attrs.get("src")
|
||||
.ok_or_else(|| "include tag missing 'src' attribute".to_string())?;
|
||||
|
||||
let include_path = format!("templates/{}", src);
|
||||
let content = fs::read_to_string(&include_path)
|
||||
.map_err(|e| format!("Failed to read include file '{}': {}", src, e))?;
|
||||
|
||||
// Recursively parse the included content
|
||||
parse_template(&content, post_manager, current_post, depth + 1)
|
||||
}
|
||||
|
||||
"post-html" => {
|
||||
let post_name = current_post
|
||||
.ok_or_else(|| "post-html tag used outside of post context".to_string())?;
|
||||
|
||||
let post = post_manager
|
||||
.get_post(post_name)
|
||||
.ok_or_else(|| format!("Post '{}' not found", post_name))?;
|
||||
|
||||
Ok(post.html_content.clone())
|
||||
}
|
||||
|
||||
"updated" => {
|
||||
// Insert the last updated time of the current post (or most recent post)
|
||||
let post_ts = if let Some(p) = current_post {
|
||||
post_manager.get_post(p).ok_or_else(|| format!("Post '{}' not found", p))?.modified_at
|
||||
} else {
|
||||
post_manager.get_update_timestamp()?
|
||||
};
|
||||
|
||||
Ok(post_ts.format("%Y-%m-%d").to_string())
|
||||
}
|
||||
|
||||
"version" => {
|
||||
let git_dir = post_manager.get_root_dir().clone();
|
||||
let git_version = get_git_version(&git_dir)?;
|
||||
Ok(format!("{}/{}", git_version.branch, git_version.commit))
|
||||
}
|
||||
|
||||
"post-list" => {
|
||||
let limit = attrs.get("limit")
|
||||
.and_then(|s| s.parse::<usize>().ok());
|
||||
|
||||
let posts = if let Some(l) = limit {
|
||||
post_manager.get_posts_limited(l)
|
||||
} else {
|
||||
post_manager.get_all_posts()
|
||||
};
|
||||
|
||||
let mut list_html = String::from("<ul class=\"post-list\">\n");
|
||||
for post in posts {
|
||||
list_html.push_str(&format!(
|
||||
" <li><a href=\"/p/{}\">{}</a> <span class=\"date\">{}</span></li>\n",
|
||||
post.name,
|
||||
post.name,
|
||||
post.created_at.format("%Y-%m-%d")
|
||||
));
|
||||
}
|
||||
list_html.push_str("</ul>");
|
||||
|
||||
Ok(list_html)
|
||||
}
|
||||
|
||||
_ => Err(format!("Unknown tag: {}", tag_name))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_tag_post_list() {
|
||||
let chars: Vec<char> = "$<post-list/>".chars().collect();
|
||||
let result = parse_tag(&chars, 0);
|
||||
assert!(result.is_some());
|
||||
let (tag_name, attrs, _) = result.unwrap();
|
||||
assert_eq!(tag_name, "post-list");
|
||||
assert!(attrs.is_empty());
|
||||
|
||||
let chars: Vec<char> = "$<post-list />".chars().collect();
|
||||
let result = parse_tag(&chars, 0);
|
||||
assert!(result.is_some());
|
||||
|
||||
let chars: Vec<char> = r#"$<post-list limit="50"/>"#.chars().collect();
|
||||
let result = parse_tag(&chars, 0);
|
||||
assert!(result.is_some());
|
||||
let (tag_name, attrs, _) = result.unwrap();
|
||||
assert_eq!(tag_name, "post-list");
|
||||
assert_eq!(attrs.get("limit"), Some(&"50".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tag_include() {
|
||||
let chars: Vec<char> = r#"$<include src="header.html"/>"#.chars().collect();
|
||||
let result = parse_tag(&chars, 0);
|
||||
assert!(result.is_some());
|
||||
let (tag_name, attrs, _) = result.unwrap();
|
||||
assert_eq!(tag_name, "include");
|
||||
assert_eq!(attrs.get("src"), Some(&"header.html".to_string()));
|
||||
|
||||
let chars: Vec<char> = r#"$<include src="header.html" />"#.chars().collect();
|
||||
let result = parse_tag(&chars, 0);
|
||||
assert!(result.is_some());
|
||||
let (tag_name, attrs, _) = result.unwrap();
|
||||
assert_eq!(tag_name, "include");
|
||||
assert_eq!(attrs.get("src"), Some(&"header.html".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tag_post_html() {
|
||||
let chars: Vec<char> = "$<post-html/>".chars().collect();
|
||||
let result = parse_tag(&chars, 0);
|
||||
assert!(result.is_some());
|
||||
let (tag_name, attrs, _) = result.unwrap();
|
||||
assert_eq!(tag_name, "post-html");
|
||||
assert!(attrs.is_empty());
|
||||
}
|
||||
}
|
||||
8
templates/footer.html
Normal file
8
templates/footer.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<footer style="margin-top: 50px; padding-top: 20px; border-top: 1px solid #ddd; color: #666; text-align: center;">
|
||||
<p>
|
||||
Guus Waals - updated on $<updated/> </br>
|
||||
$<version/>
|
||||
</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
103
templates/header.html
Normal file
103
templates/header.html
Normal file
@@ -0,0 +1,103 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>My Blog</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
}
|
||||
nav {
|
||||
border-bottom: 2px solid #333;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
nav span {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
margin-right: 20px;
|
||||
}
|
||||
nav a {
|
||||
margin-right: 20px;
|
||||
text-decoration: none;
|
||||
color: #0066cc;
|
||||
}
|
||||
nav a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.post-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.post-list li {
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
border-left: 3px solid #0066cc;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.post-list a {
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
.post-list a:hover {
|
||||
color: #0066cc;
|
||||
}
|
||||
.date {
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
float: right;
|
||||
}
|
||||
article {
|
||||
margin-top: 30px;
|
||||
}
|
||||
h1, h2, h3 {
|
||||
color: #222;
|
||||
}
|
||||
code {
|
||||
background: #f4f4f4;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
pre {
|
||||
background: #f4f4f4;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
pre code {
|
||||
padding: 0;
|
||||
}
|
||||
.github-link {
|
||||
float: right;
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.github-link svg {
|
||||
fill: #333;
|
||||
transition: fill 0.2s;
|
||||
}
|
||||
.github-link:hover svg {
|
||||
fill: #0066cc;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<span>(dis) gus' things</span>
|
||||
<a href="/">Home</a>
|
||||
<a href="/all">All Posts</a>
|
||||
<a href="https://github.com/guusw" class="github-link" target="_blank" aria-label="View source on Git">
|
||||
<svg viewBox="0 0 16 16" width="24" height="24" aria-hidden="true">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
|
||||
</svg>
|
||||
</a>
|
||||
</nav>
|
||||
4
templates/index.html
Normal file
4
templates/index.html
Normal file
@@ -0,0 +1,4 @@
|
||||
$<include src="header.html"/>
|
||||
<h1>Recent Posts</h1>
|
||||
$<post-list limit="10"/>
|
||||
$<include src="footer.html"/>
|
||||
4
templates/page_all.html
Normal file
4
templates/page_all.html
Normal file
@@ -0,0 +1,4 @@
|
||||
$<include src="header.html"/>
|
||||
<h1>All Posts</h1>
|
||||
$<post-list/>
|
||||
$<include src="footer.html"/>
|
||||
6
templates/post.html
Normal file
6
templates/post.html
Normal file
@@ -0,0 +1,6 @@
|
||||
$<include src="header.html"/>
|
||||
<article>
|
||||
$<post-html/>
|
||||
</article>
|
||||
<p><a href="/">← Back to home</a></p>
|
||||
$<include src="footer.html"/>
|
||||
Reference in New Issue
Block a user