TMP pile
This commit is contained in:
452
Cargo.lock
generated
452
Cargo.lock
generated
@@ -658,6 +658,26 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
@@ -958,6 +978,12 @@ dependencies = [
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "fax"
|
||||
version = "0.2.6"
|
||||
@@ -1010,6 +1036,27 @@ version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.2"
|
||||
@@ -1122,11 +1169,24 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"r-efi 5.3.0",
|
||||
"wasip2",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi 6.0.0",
|
||||
"wasip2",
|
||||
"wasip3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.14.1"
|
||||
@@ -1209,6 +1269,15 @@ dependencies = [
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
@@ -1335,6 +1404,22 @@ dependencies = [
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.20"
|
||||
@@ -1353,9 +1438,11 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"windows-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1463,6 +1550,12 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
@@ -1651,6 +1744,12 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.3"
|
||||
@@ -1710,6 +1809,12 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.8.1"
|
||||
@@ -1955,6 +2060,23 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.6"
|
||||
@@ -2096,6 +2218,50 @@ version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.112"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "4.2.3"
|
||||
@@ -2203,6 +2369,19 @@ dependencies = [
|
||||
"siphasher 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pile-client"
|
||||
version = "0.0.2"
|
||||
source = "git+https://git.betalupi.com/Mark/pile.git?rev=90c5584513acde6f30f76d70c426cf6987643c1a#90c5584513acde6f30f76d70c426cf6987643c1a"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.17"
|
||||
@@ -2291,6 +2470,16 @@ dependencies = [
|
||||
"prettytable-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettytable-rs"
|
||||
version = "0.10.0"
|
||||
@@ -2502,6 +2691,12 @@ version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
@@ -2717,10 +2912,12 @@ dependencies = [
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"hyper-tls",
|
||||
"hyper-util",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
@@ -2731,6 +2928,7 @@ dependencies = [
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
@@ -2813,6 +3011,19 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.37"
|
||||
@@ -2869,6 +3080,15 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
||||
@@ -2881,6 +3101,29 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "3.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation 0.10.1",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.27"
|
||||
@@ -3002,6 +3245,26 @@ dependencies = [
|
||||
"tower-http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "service-pile"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"grass",
|
||||
"libservice",
|
||||
"maud",
|
||||
"mime",
|
||||
"pile-client",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"servable",
|
||||
"service-assets",
|
||||
"tokio",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "service-webpage"
|
||||
version = "0.0.1"
|
||||
@@ -3323,6 +3586,40 @@ dependencies = [
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation 0.9.4",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.4.2",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.7.0"
|
||||
@@ -3537,6 +3834,16 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.4"
|
||||
@@ -3958,6 +4265,12 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
@@ -3998,6 +4311,15 @@ dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip3"
|
||||
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.106"
|
||||
@@ -4056,6 +4378,28 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-streams"
|
||||
version = "0.4.2"
|
||||
@@ -4069,6 +4413,18 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.83"
|
||||
@@ -4099,6 +4455,7 @@ dependencies = [
|
||||
"libservice",
|
||||
"serde",
|
||||
"service-assets",
|
||||
"service-pile",
|
||||
"service-webpage",
|
||||
"tokio",
|
||||
"toolbox",
|
||||
@@ -4192,6 +4549,17 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.1"
|
||||
@@ -4318,6 +4686,88 @@ name = "wit-bindgen"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||
dependencies = [
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn 2.0.117",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
|
||||
@@ -70,8 +70,11 @@ md-footnote = { path = "crates/lib/md-footnote" }
|
||||
md-dev = { path = "crates/lib/md-dev" }
|
||||
|
||||
service-webpage = { path = "crates/service/service-webpage" }
|
||||
service-pile = { path = "crates/service/service-pile" }
|
||||
service-assets = { path = "crates/service/service-assets" }
|
||||
|
||||
pile-client = { git = "https://git.betalupi.com/Mark/pile.git", rev = "90c5584513acde6f30f76d70c426cf6987643c1a" }
|
||||
|
||||
|
||||
#
|
||||
# MARK: Server
|
||||
|
||||
@@ -13,6 +13,7 @@ libservice = { workspace = true }
|
||||
|
||||
service-webpage = { workspace = true }
|
||||
service-assets = { workspace = true }
|
||||
service-pile = { workspace = true }
|
||||
|
||||
tracing = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use anyhow::{Context, Result};
|
||||
use libservice::{Service, ServiceConnectInfo, ToService};
|
||||
use service_assets::AssetService;
|
||||
use service_pile::PileService;
|
||||
use service_webpage::WebpageService;
|
||||
use std::sync::Arc;
|
||||
use tracing::{error, info};
|
||||
@@ -72,10 +73,15 @@ pub struct RouterState {}
|
||||
pub async fn make_service(_state: Option<Arc<RouterState>>) -> Result<impl ToService> {
|
||||
let service_webpage = WebpageService::new();
|
||||
let service_assets = AssetService::new();
|
||||
let service_pile = PileService::new()
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!(e))
|
||||
.context("while initializing pile datasets")?;
|
||||
|
||||
Ok(Service::new()
|
||||
.merge(service_webpage)
|
||||
.nest("/assets", service_assets)
|
||||
.nest("/pile", service_pile)
|
||||
.to_service()
|
||||
.trace())
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ pub struct LoggingConfig {
|
||||
|
||||
// Libs
|
||||
pub libservice: LogLevel,
|
||||
pub servable: LogLevel,
|
||||
pub toolbox: LogLevel,
|
||||
|
||||
// Bins
|
||||
@@ -67,15 +68,22 @@ impl From<LoggingConfig> for EnvFilter {
|
||||
format!("axum={}", conf.silence),
|
||||
format!("selectors={}", conf.silence),
|
||||
format!("html5ever={}", conf.silence),
|
||||
format!("tantivy={}", conf.silence),
|
||||
format!("aws_smithy_runtime={}", conf.silence),
|
||||
format!("aws_smithy_http_client={}", conf.silence),
|
||||
format!("aws_sdk_s3={}", conf.silence),
|
||||
format!("aws_sigv4={}", conf.silence),
|
||||
//
|
||||
// Libs
|
||||
//
|
||||
format!("toolbox={}", conf.toolbox),
|
||||
format!("libservice={}", conf.libservice),
|
||||
format!("servable={}", conf.servable),
|
||||
//
|
||||
// Bins
|
||||
//
|
||||
format!("service_webpage={}", conf.service),
|
||||
format!("service_pile={}", conf.service),
|
||||
format!("webpage={}", conf.webpage),
|
||||
conf.other.to_string(),
|
||||
]
|
||||
@@ -183,6 +191,7 @@ impl LogFilterPreset {
|
||||
|
||||
// Libs
|
||||
libservice: LogLevel::Error,
|
||||
servable: LogLevel::Error,
|
||||
toolbox: LogLevel::Error,
|
||||
|
||||
// Bins
|
||||
@@ -196,6 +205,7 @@ impl LogFilterPreset {
|
||||
|
||||
// Libs
|
||||
libservice: LogLevel::Warn,
|
||||
servable: LogLevel::Warn,
|
||||
toolbox: LogLevel::Warn,
|
||||
|
||||
// Bins
|
||||
@@ -209,6 +219,7 @@ impl LogFilterPreset {
|
||||
|
||||
// Libs
|
||||
libservice: LogLevel::Info,
|
||||
servable: LogLevel::Info,
|
||||
toolbox: LogLevel::Info,
|
||||
|
||||
// Bins
|
||||
@@ -222,6 +233,7 @@ impl LogFilterPreset {
|
||||
|
||||
// Libs
|
||||
libservice: LogLevel::Debug,
|
||||
servable: LogLevel::Debug,
|
||||
toolbox: LogLevel::Debug,
|
||||
|
||||
// Bins
|
||||
@@ -235,6 +247,7 @@ impl LogFilterPreset {
|
||||
|
||||
// Libs
|
||||
libservice: LogLevel::Trace,
|
||||
servable: LogLevel::Trace,
|
||||
toolbox: LogLevel::Trace,
|
||||
|
||||
// Bins
|
||||
@@ -248,6 +261,7 @@ impl LogFilterPreset {
|
||||
|
||||
// Libs
|
||||
libservice: LogLevel::Trace,
|
||||
servable: LogLevel::Trace,
|
||||
toolbox: LogLevel::Trace,
|
||||
|
||||
// Bins
|
||||
@@ -261,6 +275,7 @@ impl LogFilterPreset {
|
||||
|
||||
// Libs
|
||||
libservice: LogLevel::Trace,
|
||||
servable: LogLevel::Trace,
|
||||
toolbox: LogLevel::Trace,
|
||||
|
||||
// Bins
|
||||
|
||||
28
crates/service/service-pile/Cargo.toml
Normal file
28
crates/service/service-pile/Cargo.toml
Normal file
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "service-pile"
|
||||
version = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
libservice = { workspace = true }
|
||||
service-assets = { workspace = true }
|
||||
|
||||
pile-client = { workspace = true }
|
||||
|
||||
tracing = { workspace = true }
|
||||
grass = { workspace = true }
|
||||
axum = { workspace = true }
|
||||
maud = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
reqwest = { workspace = true }
|
||||
tower-http = { workspace = true }
|
||||
servable = { workspace = true }
|
||||
url = { workspace = true }
|
||||
mime = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true }
|
||||
183
crates/service/service-pile/css/main.scss
Normal file
183
crates/service/service-pile/css/main.scss
Normal file
@@ -0,0 +1,183 @@
|
||||
@import "text";
|
||||
|
||||
:root {
|
||||
// Misc colors
|
||||
--bgColor: #121212;
|
||||
--lightBgColor: #3a3f46;
|
||||
--fgColor: #ebebeb;
|
||||
--metaColor: #6199bb;
|
||||
--lightMetaColor: #638c86;
|
||||
--linkColor: #e4dab3;
|
||||
--codeBgColor: #292929;
|
||||
--codeFgColor: var(--fgColor);
|
||||
|
||||
// Main colors
|
||||
--grey: #696969;
|
||||
|
||||
// Accent colors, used only manally
|
||||
--green: #a2c579;
|
||||
--magenta: #ad79c5;
|
||||
--orange: #e86a33;
|
||||
--yellow: #e8bc00;
|
||||
--pink: #fa9f83;
|
||||
}
|
||||
|
||||
::selection,
|
||||
::-moz-selection {
|
||||
color: var(--bgColor);
|
||||
background: var(--metaColor);
|
||||
}
|
||||
|
||||
html {
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
font-size: 62.5%;
|
||||
scrollbar-color: var(--metaColor) var(--bgColor);
|
||||
scrollbar-width: auto;
|
||||
background: var(--bgColor);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Fira";
|
||||
font-size: 1.6rem;
|
||||
line-height: 1.35;
|
||||
max-width: 64rem;
|
||||
margin: auto;
|
||||
overflow-wrap: break-word;
|
||||
background: var(--bgColor);
|
||||
color: var(--fgColor);
|
||||
}
|
||||
|
||||
div.wrapper {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 650px) {
|
||||
.wrapper {
|
||||
margin: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Loading spinner (three dots)
|
||||
.htmx-indicator {
|
||||
display: none;
|
||||
}
|
||||
.htmx-request .htmx-indicator,
|
||||
.htmx-request.htmx-indicator {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
@keyframes dot-bounce {
|
||||
0%, 80%, 100% { opacity: 0.2; transform: scale(0.8); }
|
||||
40% { opacity: 1; transform: scale(1); }
|
||||
}
|
||||
|
||||
.search-meta {
|
||||
font-size: 1.2rem;
|
||||
color: var(--grey);
|
||||
margin: 0 0 1.5em 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#search-results {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.result-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
border: 1px solid var(--lightBgColor);
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.result-item-thumb {
|
||||
width: 64px;
|
||||
flex-shrink: 0;
|
||||
background: var(--lightBgColor);
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.result-item-info {
|
||||
padding: 0.5em 0.8em;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 0.15em;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.result-item-key {
|
||||
font-family: monospace;
|
||||
font-size: 1.3rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
direction: rtl;
|
||||
text-align: left;
|
||||
color: var(--fgColor);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--metaColor);
|
||||
}
|
||||
}
|
||||
|
||||
.result-item-link {
|
||||
font-family: monospace;
|
||||
font-size: 1.1rem;
|
||||
color: var(--linkColor);
|
||||
text-decoration: none;
|
||||
align-self: flex-start;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.result-sentinel {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
#preview-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
border: 1px solid var(--lightBgColor);
|
||||
background: var(--bgColor);
|
||||
padding: 4px;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.6);
|
||||
|
||||
img {
|
||||
display: block;
|
||||
max-width: 480px;
|
||||
max-height: 480px;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
29
crates/service/service-pile/css/text.scss
Normal file
29
crates/service/service-pile/css/text.scss
Normal file
@@ -0,0 +1,29 @@
|
||||
h1 {
|
||||
font-size: 3.5rem;
|
||||
margin-top: 1ex;
|
||||
margin-bottom: 1ex;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 1ex;
|
||||
margin-bottom: 0.5ex;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
border-radius: .3rem;
|
||||
padding: 0 .2ex 0 .2ex;
|
||||
color: var(--linkColor);
|
||||
transition: 150ms;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-color: var(--linkColor);
|
||||
color: var(--bgColor);
|
||||
transition: 150ms;
|
||||
}
|
||||
37
crates/service/service-pile/src/lib.rs
Normal file
37
crates/service/service-pile/src/lib.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use axum::Router;
|
||||
use libservice::ToService;
|
||||
use pile_client::PileClient;
|
||||
use std::sync::Arc;
|
||||
|
||||
mod pages;
|
||||
mod routes;
|
||||
|
||||
pub const PILE_PREFIX: &str = "/pile";
|
||||
pub const ASSET_PREFIX: &str = "/assets";
|
||||
|
||||
pub struct PileService {
|
||||
client: Arc<PileClient>,
|
||||
}
|
||||
|
||||
impl PileService {
|
||||
pub async fn new() -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let endpoint = std::env::var("PILE_ENDPOINT")?;
|
||||
let api_key = std::env::var("PILE_API_KEY").ok();
|
||||
let client = PileClient::new(&endpoint, api_key.as_deref())?;
|
||||
Ok(Self {
|
||||
client: Arc::new(client),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToService for PileService {
|
||||
#[inline]
|
||||
fn make_router(&self) -> Option<Router<()>> {
|
||||
Some(routes::router(self.client.clone()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn service_name(&self) -> Option<String> {
|
||||
Some("pile".to_owned())
|
||||
}
|
||||
}
|
||||
110
crates/service/service-pile/src/pages/index.rs
Normal file
110
crates/service/service-pile/src/pages/index.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
use maud::{Markup, html};
|
||||
use servable::{HtmlPage, PageMetadata, RenderContext};
|
||||
use service_assets::assets::{CSS_FIRA, CSS_FONTAWESOME, HTMX};
|
||||
use std::{pin::Pin, sync::LazyLock};
|
||||
|
||||
use crate::{ASSET_PREFIX, PILE_PREFIX, routes::CSS_PILE};
|
||||
|
||||
pub static INDEX: LazyLock<HtmlPage> = LazyLock::new(|| {
|
||||
HtmlPage::default()
|
||||
.with_style_linked(CSS_PILE.route_at(PILE_PREFIX))
|
||||
.with_style_linked(CSS_FIRA.route_at(ASSET_PREFIX))
|
||||
.with_style_linked(CSS_FONTAWESOME.route_at(ASSET_PREFIX))
|
||||
.with_script_linked(HTMX.route_at(ASSET_PREFIX))
|
||||
.with_meta(PageMetadata {
|
||||
title: "Pile".into(),
|
||||
author: None,
|
||||
description: None,
|
||||
image: None,
|
||||
})
|
||||
.with_render(render)
|
||||
});
|
||||
|
||||
fn render<'a>(
|
||||
_page: &'a HtmlPage,
|
||||
_ctx: &'a RenderContext,
|
||||
) -> Pin<Box<dyn Future<Output = Markup> + Send + Sync + 'a>> {
|
||||
Box::pin(async {
|
||||
html! {
|
||||
div class="wrapper" style="margin-top:3ex;" {
|
||||
div {
|
||||
div style="
|
||||
text-align:center;
|
||||
padding-top:30px;
|
||||
padding-bottom:60px;
|
||||
" {
|
||||
h1 class="brand" {
|
||||
span class="fa fa-solid fa-book" aria-hidden="true" {}
|
||||
" Library search"
|
||||
}
|
||||
|
||||
div style="max-width:500px;margin:0 auto;padding:.4em 1em;" {
|
||||
form {
|
||||
input
|
||||
class="search-input"
|
||||
id="search"
|
||||
name="q"
|
||||
type="text"
|
||||
placeholder="Type to search..."
|
||||
style="
|
||||
-moz-box-sizing: border-box !important;
|
||||
box-sizing: border-box !important;
|
||||
outline: none;
|
||||
border: none;
|
||||
border-radius: 1px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
padding: 10px 16px;
|
||||
font-size: 17px;
|
||||
width: 100%;
|
||||
box-shadow: 0 0 0 1px var(--color-border),0 0 0 1px var(--color-border);
|
||||
transition: box-shadow 150ms ease-in-out;
|
||||
"
|
||||
autofocus=""
|
||||
autocomplete="off"
|
||||
hx-get=(format!("{PILE_PREFIX}/search"))
|
||||
hx-trigger="load, keyup changed delay:100ms"
|
||||
hx-target="#search-results"
|
||||
hx-swap="outerHTML"
|
||||
hx-indicator="#search-spinner"
|
||||
{}
|
||||
}
|
||||
|
||||
div id="search-spinner" class="htmx-indicator dot-spinner" {
|
||||
span {}
|
||||
span {}
|
||||
span {}
|
||||
}
|
||||
|
||||
div id="search-results" {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div id="preview-overlay" {
|
||||
img id="preview-overlay-img" src="" alt="" {}
|
||||
}
|
||||
|
||||
script { (maud::PreEscaped("
|
||||
function showPreview(el) {
|
||||
var ov = document.getElementById('preview-overlay');
|
||||
var img = document.getElementById('preview-overlay-img');
|
||||
img.src = el.dataset.preview;
|
||||
var rect = el.getBoundingClientRect();
|
||||
var size = 480;
|
||||
var left = rect.right + 12;
|
||||
if (left + size > window.innerWidth) { left = rect.left - size - 12; }
|
||||
ov.style.left = left + 'px';
|
||||
ov.style.top = Math.max(8, rect.top - size / 2 + rect.height / 2) + 'px';
|
||||
ov.style.display = 'block';
|
||||
}
|
||||
function hidePreview() {
|
||||
var ov = document.getElementById('preview-overlay');
|
||||
ov.style.display = 'none';
|
||||
document.getElementById('preview-overlay-img').src = '';
|
||||
}
|
||||
")) }
|
||||
}
|
||||
})
|
||||
}
|
||||
37
crates/service/service-pile/src/pages/mod.rs
Normal file
37
crates/service/service-pile/src/pages/mod.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use maud::html;
|
||||
use reqwest::StatusCode;
|
||||
use servable::{HtmlPage, PageMetadata};
|
||||
use service_assets::assets::{CSS_FIRA, CSS_FONTAWESOME};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
mod index;
|
||||
pub use index::INDEX;
|
||||
|
||||
use crate::{ASSET_PREFIX, PILE_PREFIX, routes::CSS_PILE};
|
||||
|
||||
pub static NOT_FOUND: LazyLock<HtmlPage> = LazyLock::new(|| {
|
||||
HtmlPage::default()
|
||||
.with_style_linked(CSS_PILE.route_at(PILE_PREFIX))
|
||||
.with_style_linked(CSS_FIRA.route_at(ASSET_PREFIX))
|
||||
.with_style_linked(CSS_FONTAWESOME.route_at(ASSET_PREFIX))
|
||||
.with_meta(PageMetadata {
|
||||
title: "Page not found".into(),
|
||||
author: None,
|
||||
description: None,
|
||||
image: None,
|
||||
})
|
||||
.with_render(move |_page, _ctx| {
|
||||
Box::pin(async {
|
||||
html! {
|
||||
div class="wrapper" {
|
||||
div style="display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh" {
|
||||
p style="font-weight:bold;font-size:50pt;margin:0;" { "404" }
|
||||
p style="font-size:13pt;margin:0;color:var(--grey);" { "(page not found)" }
|
||||
a style="font-size:12pt;margin:10pt;padding:5px;" href="/" {"<- Back to site"}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.with_code(StatusCode::NOT_FOUND)
|
||||
});
|
||||
231
crates/service/service-pile/src/routes/mod.rs
Normal file
231
crates/service/service-pile/src/routes/mod.rs
Normal file
@@ -0,0 +1,231 @@
|
||||
use axum::Router;
|
||||
use axum::extract::{Query, State};
|
||||
use axum::routing::get;
|
||||
use maud::{Markup, html};
|
||||
use pile_client::PileClient;
|
||||
use servable::{CACHE_BUST_STR, ServableRouter, ServableWithRoute, StaticAsset};
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use tower_http::compression::{CompressionLayer, DefaultPredicate};
|
||||
use tracing::warn;
|
||||
|
||||
use crate::PILE_PREFIX;
|
||||
use crate::pages;
|
||||
|
||||
const PAGE_SIZE: usize = 50;
|
||||
|
||||
pub(super) fn router(client: Arc<PileClient>) -> Router<()> {
|
||||
let compression: CompressionLayer = CompressionLayer::new()
|
||||
.br(true)
|
||||
.deflate(true)
|
||||
.gzip(true)
|
||||
.zstd(true)
|
||||
.compress_when(DefaultPredicate::new());
|
||||
|
||||
let search_router: Router<()> = Router::new()
|
||||
.route("/search", get(search_handler))
|
||||
.with_state(client.clone());
|
||||
|
||||
let api_router: Router<()> = client.dataset("books").proxy_router();
|
||||
|
||||
build_server()
|
||||
.into_router()
|
||||
.merge(search_router)
|
||||
.nest("/api", api_router)
|
||||
.layer(compression)
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct SearchQuery {
|
||||
#[serde(default)]
|
||||
q: String,
|
||||
#[serde(default)]
|
||||
page: usize,
|
||||
}
|
||||
|
||||
async fn search_handler(
|
||||
State(client): State<Arc<PileClient>>,
|
||||
Query(params): Query<SearchQuery>,
|
||||
) -> Markup {
|
||||
let start = Instant::now();
|
||||
let query = params.q.trim().to_lowercase();
|
||||
let page = params.page;
|
||||
let mut query_invalid = false;
|
||||
let mut list_error = false;
|
||||
|
||||
let mut all_keys: Vec<(String, String)> = Vec::new();
|
||||
let mut filtered_total = 0;
|
||||
let mut grand_total = 0;
|
||||
|
||||
match query.is_empty() {
|
||||
true => {
|
||||
match client
|
||||
.dataset("books")
|
||||
.list_items(page * PAGE_SIZE, PAGE_SIZE)
|
||||
.await
|
||||
{
|
||||
Err(error) => {
|
||||
list_error = true;
|
||||
warn!(message = "error while listing items", ?error);
|
||||
}
|
||||
|
||||
Ok(resp) => {
|
||||
all_keys = resp
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| (item.source, item.key))
|
||||
.collect();
|
||||
filtered_total = resp.total;
|
||||
grand_total = resp.total;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false => match client.dataset("books").lookup(&query, Some(512)).await {
|
||||
Err(_error) => {
|
||||
query_invalid = true;
|
||||
}
|
||||
|
||||
Ok(resp) => {
|
||||
let mut results = resp.results;
|
||||
results.sort_unstable_by(|a, b| f32::total_cmp(&b.score, &a.score));
|
||||
|
||||
filtered_total = results.len();
|
||||
grand_total = results.len();
|
||||
all_keys = results.into_iter().map(|r| (r.source, r.key)).collect();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// For empty query the server already paginated; for non-empty we slice locally.
|
||||
let page_items: Vec<&(String, String)> = if query.is_empty() {
|
||||
all_keys.iter().collect()
|
||||
} else {
|
||||
all_keys
|
||||
.iter()
|
||||
.skip(page * PAGE_SIZE)
|
||||
.take(PAGE_SIZE)
|
||||
.collect()
|
||||
};
|
||||
|
||||
let has_more = (page + 1) * PAGE_SIZE < filtered_total;
|
||||
let next_page = page + 1;
|
||||
|
||||
let encoded_q: String = url::form_urlencoded::byte_serialize(query.as_bytes()).collect();
|
||||
let next_url = format!("{PILE_PREFIX}/search?q={}&page={}", encoded_q, next_page);
|
||||
let elapsed_ms = start.elapsed().as_millis();
|
||||
|
||||
let mut msg = Vec::new();
|
||||
if query_invalid {
|
||||
msg.push("invalid query");
|
||||
}
|
||||
if list_error {
|
||||
msg.push("list error");
|
||||
}
|
||||
if filtered_total == 0 {
|
||||
msg.push("no results");
|
||||
}
|
||||
|
||||
if page == 0 {
|
||||
html! {
|
||||
div id="search-results" {
|
||||
p class="search-meta" {
|
||||
"Filtered " (filtered_total) "/" (grand_total) " items in " (elapsed_ms) "ms"
|
||||
@if !msg.is_empty() {
|
||||
span style="color:var(--orange)" {
|
||||
(format!("\u{00A0}\u{00A0}({})", msg.join(", ")))
|
||||
}
|
||||
}
|
||||
}
|
||||
div class="result-grid" {
|
||||
@for (source, key) in &page_items {
|
||||
(result_item(source, key))
|
||||
}
|
||||
@if has_more {
|
||||
div
|
||||
class="result-sentinel"
|
||||
hx-get=(next_url)
|
||||
hx-trigger="revealed"
|
||||
hx-swap="outerHTML"
|
||||
hx-target="this"
|
||||
{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
html! {
|
||||
@for (source, key) in &page_items {
|
||||
(result_item(source, key))
|
||||
}
|
||||
@if has_more {
|
||||
div
|
||||
class="result-sentinel"
|
||||
hx-get=(next_url)
|
||||
hx-trigger="revealed"
|
||||
hx-swap="outerHTML"
|
||||
hx-target="this"
|
||||
{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn result_item(source: &str, key: &str) -> Markup {
|
||||
let enc_source: String = url::form_urlencoded::byte_serialize(source.as_bytes()).collect();
|
||||
let enc_key: String = url::form_urlencoded::byte_serialize(key.as_bytes()).collect();
|
||||
let enc_path: String =
|
||||
url::form_urlencoded::byte_serialize("$.pdf.pages[0]".as_bytes()).collect();
|
||||
let thumb_url =
|
||||
format!("{PILE_PREFIX}/api/field?source={enc_source}&key={enc_key}&path={enc_path}");
|
||||
let item_url =
|
||||
format!("{PILE_PREFIX}/api/item?source={enc_source}&key={enc_key}&download=false");
|
||||
html! {
|
||||
div class="result-item" {
|
||||
div class="result-item-thumb"
|
||||
data-preview=(thumb_url)
|
||||
onmouseenter="showPreview(this)"
|
||||
onmouseleave="hidePreview()"
|
||||
{
|
||||
img src=(thumb_url) alt="" onerror="this.style.visibility='hidden'" {}
|
||||
}
|
||||
div class="result-item-info" {
|
||||
span class="result-item-key"
|
||||
data-key=(key)
|
||||
onclick="navigator.clipboard.writeText(this.dataset.key)"
|
||||
title="Click to copy"
|
||||
{ (key) }
|
||||
a class="result-item-link" href=(item_url) target="_blank" { "item" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub static CSS_PILE: ServableWithRoute<StaticAsset> = ServableWithRoute::new(
|
||||
|| format!("/css/{}/main.css", *CACHE_BUST_STR),
|
||||
StaticAsset {
|
||||
bytes: grass::include!("crates/service/service-pile/css/main.scss").as_bytes(),
|
||||
mime: mime::TEXT_CSS,
|
||||
ttl: StaticAsset::DEFAULT_TTL,
|
||||
},
|
||||
);
|
||||
|
||||
fn build_server() -> ServableRouter {
|
||||
ServableRouter::new()
|
||||
.with_404(&pages::NOT_FOUND)
|
||||
.add_page("/", &pages::INDEX)
|
||||
//
|
||||
.add_page_with_route(&CSS_PILE)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[expect(clippy::unwrap_used)]
|
||||
fn server_builds_without_panic() {
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async {
|
||||
let _server = build_server();
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user