diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml deleted file mode 100644 index d880dfd..0000000 --- a/.github/workflows/deploy-site.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: deploy-site -on: - push: - branches: - - main - -jobs: - cargo-build: - name: vercel deploy - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - uses: hecrj/setup-rust-action@v1 - with: - rust-version: stable - - - name: 'install wasm32-unknown-unknown' - run: rustup target add wasm32-unknown-unknown - - - uses: jetli/wasm-bindgen-action@v0.1.0 - with: - version: 'latest' - - - uses: actions-rs/install@v0.1 - with: - crate: rapper - version: latest - - - uses: jetli/trunk-action@v0.1.0 - with: - version: 'latest' - - - name: build - run: cd app && trunk build --release -d ../dist - - - uses: amondnet/vercel-action@v20 - with: - vercel-cli: vercel - vercel-token: ${{ secrets.VERCEL_TOKEN }} - vercel-args: --prod - vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} - vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} diff --git a/.github/workflows/fmt.yml b/.github/workflows/fmt.yml new file mode 100644 index 0000000..a46a9a9 --- /dev/null +++ b/.github/workflows/fmt.yml @@ -0,0 +1,25 @@ +name: Check code style +on: + pull_request: + paths: + - "**/*.rs" + push: + branches: [main] + paths: + - "**/*.rs" + +jobs: + format: + name: cargo fmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly + components: rustfmt + + - name: Run fmt + run: cargo +nightly fmt --all -- --check --unstable-features diff --git a/.gitignore b/.gitignore index 103c181..d15bc1b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,14 @@ -**/*.rs.bk -.vercel +# Build +**/target +**/dist +database.json +output.css -# Dependencies -pkg -/target +# Deps +node_modules -# Dist -dist +# Database +zzhack.db # Platform .DS_Store diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index fa4b96a..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,8 +0,0 @@ - -{ - "editor.formatOnSave": true, - "editor.tabSize": 4, - "[rust]": { - "editor.defaultFormatter": "rust-lang.rust-analyzer" - } -} diff --git a/Cargo.lock b/Cargo.lock index 5f27452..55b86f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,14 +3,12 @@ version = 3 [[package]] -name = "about" -version = "0.1.0" +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ - "services", - "stylist", - "ui", - "utils", - "yew", + "gimli", ] [[package]] @@ -19,1212 +17,4409 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" -version = "0.7.18" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] -name = "anyhow" -version = "1.0.57" +name = "aliasable" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" [[package]] -name = "arrayvec" -version = "0.5.2" +name = "allocator-api2" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] -name = "autocfg" +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[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 = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +dependencies = [ + "windows-sys 0.52.0", +] [[package]] -name = "base-x" -version = "0.2.10" +name = "anstyle-wincon" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc19a4937b4fbd3fe3379793130e42060d10627a360f2127802b10b87e7baf74" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] [[package]] -name = "base64" -version = "0.13.0" +name = "anyhow" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] -name = "bincode" -version = "1.3.3" +name = "anymap2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + +[[package]] +name = "api" +version = "0.1.0" dependencies = [ + "anyhow", + "axum", + "chrono", + "futures", + "gray_matter", + "markdown", + "sea-orm 1.0.1", "serde", + "shared", + "site_config", + "tokio", + "toml 0.8.14", ] [[package]] -name = "bit-set" -version = "0.5.2" +name = "app" +version = "0.0.1" +dependencies = [ + "api", + "cached 0.53.1", + "chrono", + "gray_matter", + "js-sys", + "log", + "reqwest", + "serde", + "serde_json", + "shared", + "site_config", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-macro", + "wasm-logger", + "web-sys", + "yew", + "yew-router", +] + +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + +[[package]] +name = "arrayvec" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" dependencies = [ - "bit-vec", + "quote", + "syn 1.0.109", ] [[package]] -name = "bit-vec" -version = "0.6.3" +name = "async-channel" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] [[package]] -name = "bitflags" -version = "1.3.2" +name = "async-channel" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] [[package]] -name = "boolinator" -version = "2.4.0" +name = "async-executor" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" +checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand 2.1.0", + "futures-lite 2.3.0", + "slab", +] [[package]] -name = "bumpalo" -version = "3.9.1" +name = "async-global-executor" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.3.1", + "async-executor", + "async-io 2.3.3", + "async-lock 3.4.0", + "blocking", + "futures-lite 2.3.0", + "once_cell", + "tokio", +] [[package]] -name = "cfg-if" -version = "0.1.10" +name = "async-io" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite 1.13.0", + "log", + "parking", + "polling 2.8.0", + "rustix 0.37.27", + "slab", + "socket2 0.4.10", + "waker-fn", +] [[package]] -name = "cfg-if" -version = "1.0.0" +name = "async-io" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" +dependencies = [ + "async-lock 3.4.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.3.0", + "parking", + "polling 3.7.2", + "rustix 0.38.34", + "slab", + "tracing", + "windows-sys 0.52.0", +] [[package]] -name = "chrono" -version = "0.4.19" +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-attributes", + "async-channel 1.9.0", + "async-global-executor", + "async-io 1.13.0", + "async-lock 2.8.0", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite 1.13.0", + "gloo-timers 0.2.6", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[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.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.30", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ + "addr2line", + "cc", + "cfg-if", "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bigdecimal" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" +dependencies = [ + "num-bigint", "num-integer", "num-traits", - "time 0.1.44", - "winapi", ] [[package]] -name = "console_error_panic_hook" -version = "0.1.7" +name = "bincode" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" dependencies = [ - "cfg-if 1.0.0", - "wasm-bindgen", + "serde", ] [[package]] -name = "crc32fast" +name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" dependencies = [ - "cfg-if 1.0.0", + "serde", ] [[package]] -name = "css-in-rust" -version = "0.5.0" +name = "bitvec" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f578b86ea91652cbb72c954b4b1f47ff952986e52405461a683330e8e96474af" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "lazy_static", - "nom 5.1.2", - "rand", - "wasm-bindgen", - "web-sys", + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel 2.3.1", + "async-task", + "futures-io", + "futures-lite 2.3.0", + "piper", +] + +[[package]] +name = "boolinator" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" + +[[package]] +name = "borsh" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" +dependencies = [ + "once_cell", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", + "syn 2.0.71", + "syn_derive", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" + +[[package]] +name = "cached" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8466736fe5dbcaf8b8ee24f9bbefe43c884dc3e9ff7178da70f55bffca1133c" +dependencies = [ + "ahash 0.8.11", + "cached_proc_macro 0.22.0", + "cached_proc_macro_types", + "hashbrown 0.14.5", + "instant", + "once_cell", + "thiserror", +] + +[[package]] +name = "cached" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4d73155ae6b28cf5de4cfc29aeb02b8a1c6dab883cb015d15cd514e42766846" +dependencies = [ + "ahash 0.8.11", + "cached_proc_macro 0.23.0", + "cached_proc_macro_types", + "hashbrown 0.14.5", + "once_cell", + "thiserror", + "web-time", +] + +[[package]] +name = "cached_proc_macro" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "575f32e012222055211b70f5b0601f951f84523410a0e65c81f2744a6042450d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.71", +] + +[[package]] +name = "cached_proc_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f42a145ed2d10dce2191e1dcf30cfccfea9026660e143662ba5eec4017d5daa" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.71", +] + +[[package]] +name = "cached_proc_macro_types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" + +[[package]] +name = "cc" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "clap" +version = "4.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.71", +] + +[[package]] +name = "clap_lex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[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-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.71", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.71", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "educe" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4bd92664bf78c4d3dba9b7cdafce6fa15b13ed3ed16175218196942e99168a8" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.71", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +dependencies = [ + "serde", +] + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "entry" +version = "0.0.1" +dependencies = [ + "api", + "app", + "axum", + "clap", + "env_logger", + "futures", + "hyper 0.14.30", + "jemallocator", + "log", + "site_config", + "tokio", + "tower", + "tower-http", + "wasm-bindgen-futures", + "wasm-logger", + "yew", +] + +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.1", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand 2.1.0", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gloo" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d" +dependencies = [ + "gloo-console 0.2.3", + "gloo-dialogs 0.1.1", + "gloo-events 0.1.2", + "gloo-file 0.2.3", + "gloo-history 0.1.5", + "gloo-net 0.3.1", + "gloo-render 0.1.1", + "gloo-storage 0.2.2", + "gloo-timers 0.2.6", + "gloo-utils 0.1.7", + "gloo-worker 0.2.1", +] + +[[package]] +name = "gloo" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd35526c28cc55c1db77aed6296de58677dbab863b118483a27845631d870249" +dependencies = [ + "gloo-console 0.3.0", + "gloo-dialogs 0.2.0", + "gloo-events 0.2.0", + "gloo-file 0.3.0", + "gloo-history 0.2.2", + "gloo-net 0.4.0", + "gloo-render 0.2.0", + "gloo-storage 0.3.0", + "gloo-timers 0.3.0", + "gloo-utils 0.2.0", + "gloo-worker 0.4.0", +] + +[[package]] +name = "gloo-console" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f" +dependencies = [ + "gloo-utils 0.1.7", + "js-sys", + "serde", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-console" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a17868f56b4a24f677b17c8cb69958385102fa879418052d60b50bc1727e261" +dependencies = [ + "gloo-utils 0.2.0", + "js-sys", + "serde", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-dialogs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-dialogs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4748e10122b01435750ff530095b1217cf6546173459448b83913ebe7815df" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-events" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-events" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c26fb45f7c385ba980f5fa87ac677e363949e065a083722697ef1b2cc91e41" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-file" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7" +dependencies = [ + "gloo-events 0.1.2", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-file" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97563d71863fb2824b2e974e754a81d19c4a7ec47b09ced8a0e6656b6d54bd1f" +dependencies = [ + "futures-channel", + "gloo-events 0.2.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-history" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f" +dependencies = [ + "gloo-events 0.1.2", + "gloo-utils 0.1.7", + "serde", + "serde-wasm-bindgen 0.5.0", + "serde_urlencoded", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-history" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "903f432be5ba34427eac5e16048ef65604a82061fe93789f2212afc73d8617d6" +dependencies = [ + "getrandom", + "gloo-events 0.2.0", + "gloo-utils 0.2.0", + "serde", + "serde-wasm-bindgen 0.6.5", + "serde_urlencoded", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils 0.1.7", + "http 0.2.12", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ac9e8288ae2c632fa9f8657ac70bfe38a1530f345282d7ba66a1f70b72b7dc4" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils 0.2.0", + "http 0.2.12", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-render" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-render" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56008b6744713a8e8d98ac3dcb7d06543d5662358c9c805b4ce2167ad4649833" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-storage" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480" +dependencies = [ + "gloo-utils 0.1.7", + "js-sys", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-storage" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc8031e8c92758af912f9bc08fbbadd3c6f3cfcbf6b64cdf3d6a81f0139277a" +dependencies = [ + "gloo-utils 0.2.0", + "js-sys", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-worker" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a" +dependencies = [ + "anymap2", + "bincode", + "gloo-console 0.2.3", + "gloo-utils 0.1.7", + "js-sys", + "serde", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-worker" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76495d3dd87de51da268fa3a593da118ab43eb7f8809e17eb38d3319b424e400" +dependencies = [ + "bincode", + "futures", + "gloo-utils 0.2.0", + "gloo-worker-macros", + "js-sys", + "pinned", + "serde", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-worker-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956caa58d4857bc9941749d55e4bd3000032d8212762586fa5705632967140e7" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 2.0.71", +] + +[[package]] +name = "gray_matter" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ee6a6070bad7c953b0c8be9367e9372181fed69f3e026c4eb5160d8b3c0222" +dependencies = [ + "serde", + "serde_json", + "toml 0.5.11", + "yaml-rust2", +] + +[[package]] +name = "h2" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash 0.8.11", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "html-escape" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" +dependencies = [ + "utf8-width", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.7", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.4.1", + "hyper-util", + "rustls 0.23.11", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[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 1.4.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.4.1", + "pin-project-lite", + "socket2 0.5.7", + "tokio", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "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 = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "implicit-clone" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8a9aa791c7b5a71b636b7a68207fdebf171ddfc593d9c8506ec4cbc527b6a84" +dependencies = [ + "implicit-clone-derive", + "indexmap", +] + +[[package]] +name = "implicit-clone-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9311685eb9a34808bbb0608ad2fcab9ae216266beca5848613e95553ac914e3b" +dependencies = [ + "quote", + "syn 2.0.71", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", +] + +[[package]] +name = "inherent" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0122b7114117e64a63ac49f752a5ca4624d534c7b1c7de796ac196381cd2d947" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jemalloc-sys" +version = "0.5.4+5.3.0-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac6c1946e1cea1788cbfde01c993b52a10e2da07f4bac608228d1bed20bfebf2" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "jemallocator" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0de374a9f8e63150e6f5e8a60cc14c668226d7a347d8aee1a45766e3c4dd3bc" +dependencies = [ + "jemalloc-sys", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libsqlite3-sys" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +dependencies = [ + "value-bag", +] + +[[package]] +name = "markdown" +version = "0.1.0" +dependencies = [ + "pulldown-cmark", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "migration" +version = "0.1.0" +dependencies = [ + "async-std", + "sea-orm-migration", +] + +[[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 = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "object" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.6.0", + "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.71", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-float" +version = "3.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ouroboros" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2ba07320d39dfea882faa70554b4bd342a5f273ed59ba7c1c6b4c840492c954" +dependencies = [ + "aliasable", + "ouroboros_macro", + "static_assertions", +] + +[[package]] +name = "ouroboros_macro" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.71", +] + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.3", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pinned" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a829027bd95e54cfe13e3e258a1ae7b645960553fb82b75ff852c29688ee595b" +dependencies = [ + "futures", + "rustversion", + "thiserror", +] + +[[package]] +name = "piper" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" +dependencies = [ + "atomic-waker", + "fastrand 2.1.0", + "futures-io", +] + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "polling" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix 0.38.34", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.71", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prokio" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b55e106e5791fa5a13abd13c85d6127312e8e09098059ca2bc9b03ca4cf488" +dependencies = [ + "futures", + "gloo 0.8.1", + "num_cpus", + "once_cell", + "pin-project", + "pinned", + "tokio", + "tokio-stream", + "wasm-bindgen-futures", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pulldown-cmark" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8746739f11d39ce5ad5c2520a9b75285310dbfe78c541ccf832d38615765aec0" +dependencies = [ + "bitflags 2.6.0", + "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.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "reqwest" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 2.1.2", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-socks", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rkyv" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rust_decimal" +version = "1.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1790d1c4c0ca81211399e0e0af16333276f375209e71a37b67698a373db5b47a" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand", + "rkyv", + "serde", + "serde_json", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.37.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys 0.4.14", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "ring", + "rustls-webpki 0.101.7", + "sct", +] + +[[package]] +name = "rustls" +version = "0.23.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.102.5", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.102.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", ] [[package]] -name = "discard" -version = "1.0.4" +name = "rustversion" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] -name = "either" -version = "1.6.1" +name = "ryu" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] -name = "fancy-regex" -version = "0.7.1" +name = "schannel" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6b8560a05112eb52f04b00e5d3790c0dd75d9d980eb8a122fb23b92a623ccf" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "bit-set", - "regex", + "windows-sys 0.52.0", ] [[package]] -name = "fastrand" -version = "1.7.0" +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "instant", + "ring", + "untrusted", ] [[package]] -name = "flate2" -version = "1.0.23" +name = "sea-bae" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af" +checksum = "3bd3534a9978d0aa7edd2808dc1f8f31c4d0ecd31ddf71d997b3c98e9f3c9114" dependencies = [ - "cfg-if 1.0.0", - "crc32fast", - "libc", - "miniz_oxide", + "heck 0.4.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.71", ] [[package]] -name = "fnv" -version = "1.0.7" +name = "sea-orm" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +checksum = "c8814e37dc25de54398ee62228323657520b7f29713b8e238649385dbe473ee0" +dependencies = [ + "async-stream", + "async-trait", + "futures", + "log", + "ouroboros", + "sea-orm-macros 0.12.15", + "sea-query 0.30.7", + "sea-query-binder 0.5.0", + "serde", + "sqlx", + "strum 0.25.0", + "thiserror", + "tracing", + "url", +] [[package]] -name = "form_urlencoded" +name = "sea-orm" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "ea1fee0cf8528dbe6eda29d5798afc522a63b75e44c5b15721e6e64af9c7cc4b" dependencies = [ - "matches", - "percent-encoding", + "async-stream", + "async-trait", + "bigdecimal", + "chrono", + "futures", + "log", + "ouroboros", + "rust_decimal", + "sea-orm-macros 1.0.1", + "sea-query 0.31.0", + "sea-query-binder 0.6.0", + "serde", + "serde_json", + "sqlx", + "strum 0.26.3", + "thiserror", + "time", + "tracing", + "url", + "uuid", ] [[package]] -name = "futures-channel" -version = "0.3.21" +name = "sea-orm-cli" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "620bc560062ae251b1366bde43b3f1508445cab5c2c8cbdb397034638ab1b357" dependencies = [ - "futures-core", + "chrono", + "clap", + "dotenvy", + "glob", + "regex", + "sea-schema", + "tracing", + "tracing-subscriber", + "url", ] [[package]] -name = "futures-core" -version = "0.3.21" +name = "sea-orm-macros" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "5e115c6b078e013aa963cc2d38c196c2c40b05f03d0ac872fe06b6e0d5265603" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "sea-bae", + "syn 2.0.71", + "unicode-ident", +] [[package]] -name = "getopts" -version = "0.2.21" +name = "sea-orm-macros" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +checksum = "8737b566799ed0444f278d13c300c4c6f1a91782f60ff5825a591852d5502030" dependencies = [ - "unicode-width", + "heck 0.4.1", + "proc-macro2", + "quote", + "sea-bae", + "syn 2.0.71", + "unicode-ident", ] [[package]] -name = "getrandom" -version = "0.1.16" +name = "sea-orm-migration" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +checksum = "ee8269bc6ff71afd6b78aa4333ac237a69eebd2cdb439036291e64fb4b8db23c" dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", + "async-trait", + "clap", + "dotenvy", + "futures", + "sea-orm 0.12.15", + "sea-orm-cli", + "sea-schema", + "tracing", + "tracing-subscriber", ] [[package]] -name = "global" -version = "0.1.0" +name = "sea-query" +version = "0.30.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4166a1e072292d46dc91f31617c2a1cdaf55a8be4b5c9f4bf2ba248e3ac4999b" dependencies = [ - "log", - "services", - "wasm-logger", - "yew", + "derivative", + "inherent", + "ordered-float", + "sea-query-derive", ] [[package]] -name = "gloo" -version = "0.2.1" +name = "sea-query" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ce6f2dfa9f57f15b848efa2aade5e1850dc72986b87a2b0752d44ca08f4967" +checksum = "7e5073b2cfed767511a57d18115f3b3d8bcb5690bf8c89518caec6cb22c0cd74" dependencies = [ - "gloo-console-timer", - "gloo-events", - "gloo-file 0.1.0", - "gloo-timers", + "bigdecimal", + "chrono", + "educe", + "inherent", + "ordered-float", + "rust_decimal", + "serde_json", + "time", + "uuid", ] [[package]] -name = "gloo" -version = "0.4.2" +name = "sea-query-binder" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23947965eee55e3e97a5cd142dd4c10631cc349b48cecca0ed230fd296f568cd" +checksum = "36bbb68df92e820e4d5aeb17b4acd5cc8b5d18b2c36a4dd6f4626aabfa7ab1b9" dependencies = [ - "gloo-console", - "gloo-dialogs", - "gloo-events", - "gloo-file 0.2.1", - "gloo-render", - "gloo-storage", - "gloo-timers", - "gloo-utils", + "sea-query 0.30.7", + "sqlx", ] [[package]] -name = "gloo-console" -version = "0.2.1" +name = "sea-query-binder" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3907f786f65bbb4f419e918b0c5674175ef1c231ecda93b2dbd65fd1e8882637" +checksum = "754965d4aee6145bec25d0898e5c931e6c22859789ce62fd85a42a15ed5a8ce3" dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", - "web-sys", + "bigdecimal", + "chrono", + "rust_decimal", + "sea-query 0.31.0", + "serde_json", + "sqlx", + "time", + "uuid", ] [[package]] -name = "gloo-console-timer" -version = "0.1.0" +name = "sea-query-derive" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b48675544b29ac03402c6dffc31a912f716e38d19f7e74b78b7e900ec3c941ea" +checksum = "25a82fcb49253abcb45cdcb2adf92956060ec0928635eb21b4f7a6d8f25ab0bc" dependencies = [ - "web-sys", + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.71", + "thiserror", ] [[package]] -name = "gloo-dialogs" -version = "0.1.1" +name = "sea-schema" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6" +checksum = "30d148608012d25222442d1ebbfafd1228dbc5221baf4ec35596494e27a2394e" dependencies = [ - "wasm-bindgen", - "web-sys", + "futures", + "sea-query 0.30.7", + "sea-schema-derive", ] [[package]] -name = "gloo-events" -version = "0.1.2" +name = "sea-schema-derive" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc" +checksum = "c6f686050f76bffc4f635cda8aea6df5548666b830b52387e8bc7de11056d11e" dependencies = [ - "wasm-bindgen", - "web-sys", + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "gloo-file" -version = "0.1.0" +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "security-framework" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f9fecfe46b5dc3cc46f58e98ba580cc714f2c93860796d002eb3527a465ef49" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "gloo-events", - "js-sys", - "wasm-bindgen", - "web-sys", + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", ] [[package]] -name = "gloo-file" -version = "0.2.1" +name = "security-framework-sys" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa5d6084efa4a2b182ef3a8649cb6506cb4843f22cf907c6e0a799944248ae90" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ - "futures-channel", - "gloo-events", - "js-sys", - "wasm-bindgen", - "web-sys", + "core-foundation-sys", + "libc", ] [[package]] -name = "gloo-render" -version = "0.1.1" +name = "serde" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ - "wasm-bindgen", - "web-sys", + "serde_derive", ] [[package]] -name = "gloo-storage" -version = "0.2.1" +name = "serde-wasm-bindgen" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1caa4ba51c99de680dee3ad99c32ca45e9f13311be72079154d222c3f9a6b6f5" +checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" dependencies = [ - "gloo-utils", "js-sys", "serde", - "serde_json", - "thiserror", "wasm-bindgen", - "web-sys", ] [[package]] -name = "gloo-timers" -version = "0.2.4" +name = "serde-wasm-bindgen" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" dependencies = [ - "futures-channel", - "futures-core", "js-sys", + "serde", "wasm-bindgen", ] [[package]] -name = "gloo-utils" -version = "0.1.3" +name = "serde_derive" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c0bbef55e98d946adbd89f3c65a497cf9adb995a73b99573f30180e8813ab21" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ - "js-sys", - "wasm-bindgen", - "web-sys", + "proc-macro2", + "quote", + "syn 2.0.71", ] [[package]] -name = "hashbrown" -version = "0.11.2" +name = "serde_json" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +dependencies = [ + "itoa", + "ryu", + "serde", +] [[package]] -name = "home" -version = "0.1.0" +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" dependencies = [ - "services", - "stylist", - "ui", - "utils", - "yew", + "itoa", + "serde", ] [[package]] -name = "indexmap" -version = "1.8.1" +name = "serde_spanned" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ - "autocfg", - "hashbrown", + "serde", ] [[package]] -name = "instant" -version = "0.1.12" +name = "serde_urlencoded" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ - "cfg-if 1.0.0", - "js-sys", - "wasm-bindgen", - "web-sys", + "form_urlencoded", + "itoa", + "ryu", + "serde", ] [[package]] -name = "itertools" -version = "0.10.3" +name = "sha1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "either", + "cfg-if", + "cpufeatures", + "digest", ] [[package]] -name = "itoa" -version = "1.0.1" +name = "sha2" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] [[package]] -name = "js-sys" -version = "0.3.57" +name = "sharded-slab" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ - "wasm-bindgen", + "lazy_static", ] [[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +name = "shared" +version = "0.1.0" +dependencies = [ + "serde", +] [[package]] -name = "lazycell" -version = "1.3.0" +name = "signal-hook-registry" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] [[package]] -name = "lexical-core" -version = "0.7.6" +name = "signature" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "arrayvec", - "bitflags", - "cfg-if 1.0.0", - "ryu", - "static_assertions", + "digest", + "rand_core", ] [[package]] -name = "libc" -version = "0.2.125" +name = "simdutf8" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" [[package]] -name = "line-wrap" -version = "0.1.1" +name = "site_config" +version = "0.0.1" +dependencies = [ + "cached 0.52.0", + "serde", + "serde_derive", + "shared", + "toml 0.8.14", + "wasm-bindgen", +] + +[[package]] +name = "slab" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "safemem", + "autocfg", ] [[package]] -name = "linked-hash-map" -version = "0.5.4" +name = "smallvec" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] -name = "links" -version = "0.1.0" +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ - "router", - "services", - "stylist", - "ui", - "utils", - "yew", + "libc", + "winapi", ] [[package]] -name = "litrs" -version = "0.2.3" +name = "socket2" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9275e0933cf8bb20f008924c0cb07a0692fe54d8064996520bf998de9eb79aa" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ - "proc-macro2", + "libc", + "windows-sys 0.52.0", ] [[package]] -name = "log" -version = "0.4.17" +name = "spin" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" dependencies = [ - "cfg-if 1.0.0", + "lock_api", ] [[package]] -name = "matches" -version = "0.1.9" +name = "spki" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] [[package]] -name = "material-yew" -version = "0.1.0" -source = "git+https://github.com/hamza1311/material-yew#21341e30bdbd0e99a45655ce54d285dda37e7baf" +name = "sqlformat" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f" dependencies = [ - "gloo 0.2.1", - "js-sys", - "paste", - "wasm-bindgen", - "web-sys", - "yew", + "nom", + "unicode_categories", ] [[package]] -name = "memchr" -version = "2.5.0" +name = "sqlx" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] [[package]] -name = "memory_units" -version = "0.4.0" +name = "sqlx-core" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" +checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" +dependencies = [ + "ahash 0.8.11", + "atoi", + "bigdecimal", + "byteorder", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener 2.5.3", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashlink", + "hex", + "indexmap", + "log", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "rust_decimal", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "time", + "tokio", + "tokio-stream", + "tracing", + "url", + "uuid", + "webpki-roots", +] [[package]] -name = "minimal-lexical" -version = "0.2.1" +name = "sqlx-macros" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 1.0.109", +] [[package]] -name = "miniz_oxide" -version = "0.5.1" +name = "sqlx-macros-core" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" dependencies = [ - "adler", + "dotenvy", + "either", + "heck 0.4.1", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 1.0.109", + "tempfile", + "tokio", + "url", ] [[package]] -name = "nom" -version = "5.1.2" +name = "sqlx-mysql" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" dependencies = [ - "lexical-core", + "atoi", + "base64 0.21.7", + "bigdecimal", + "bitflags 2.6.0", + "byteorder", + "bytes", + "chrono", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", "memchr", - "version_check", + "once_cell", + "percent-encoding", + "rand", + "rsa", + "rust_decimal", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "time", + "tracing", + "uuid", + "whoami", ] [[package]] -name = "nom" -version = "7.1.1" +name = "sqlx-postgres" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" dependencies = [ + "atoi", + "base64 0.21.7", + "bigdecimal", + "bitflags 2.6.0", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", "memchr", - "minimal-lexical", -] - -[[package]] -name = "not_found" -version = "0.1.0" -dependencies = [ - "stylist", - "yew", + "num-bigint", + "once_cell", + "rand", + "rust_decimal", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "time", + "tracing", + "uuid", + "whoami", ] [[package]] -name = "num-integer" -version = "0.1.45" +name = "sqlx-sqlite" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa" dependencies = [ - "autocfg", - "num-traits", + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core", + "time", + "tracing", + "url", + "urlencoding", + "uuid", ] [[package]] -name = "num-traits" -version = "0.2.15" +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] -name = "num_threads" -version = "0.1.6" +name = "stringprep" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" dependencies = [ - "libc", + "unicode-bidi", + "unicode-normalization", + "unicode-properties", ] [[package]] -name = "once_cell" -version = "1.10.0" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "paste" -version = "1.0.7" +name = "strum" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" [[package]] -name = "percent-encoding" -version = "2.1.0" +name = "strum" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" [[package]] -name = "plist" -version = "1.3.1" +name = "subtle" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225" -dependencies = [ - "base64", - "indexmap", - "line-wrap", - "serde", - "time 0.3.9", - "xml-rs", -] +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] -name = "post" -version = "0.1.0" +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "gloo-timers", - "services", - "stylist", - "ui", - "utils", - "web-sys", - "yew", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - -[[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "syn" +version = "2.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" dependencies = [ - "proc-macro-error-attr", "proc-macro2", "quote", - "syn", - "version_check", + "unicode-ident", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "syn_derive" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" dependencies = [ + "proc-macro-error", "proc-macro2", "quote", - "version_check", + "syn 2.0.71", ] [[package]] -name = "proc-macro2" -version = "1.0.38" +name = "sync_wrapper" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa" -dependencies = [ - "unicode-xid", -] +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] -name = "projects" -version = "0.1.0" -dependencies = [ - "services", - "stylist", - "ui", - "utils", - "yew", -] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" [[package]] -name = "pulldown-cmark" -version = "0.8.0" +name = "system-configuration" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "bitflags", - "getopts", - "memchr", - "unicase", + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", ] [[package]] -name = "quote" -version = "1.0.18" +name = "system-configuration-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ - "proc-macro2", + "core-foundation-sys", + "libc", ] [[package]] -name = "rand" -version = "0.7.3" +name = "tap" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom", - "libc", - "rand_chacha", - "rand_core", - "rand_hc", - "rand_pcg", -] +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] -name = "rand_chacha" -version = "0.2.2" +name = "tempfile" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ - "ppv-lite86", - "rand_core", + "cfg-if", + "fastrand 2.1.0", + "rustix 0.38.34", + "windows-sys 0.52.0", ] [[package]] -name = "rand_core" -version = "0.5.1" +name = "termcolor" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ - "getrandom", + "winapi-util", ] [[package]] -name = "rand_hc" -version = "0.2.0" +name = "thiserror" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" dependencies = [ - "rand_core", + "thiserror-impl", ] [[package]] -name = "rand_pcg" -version = "0.2.1" +name = "thiserror-impl" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" dependencies = [ - "rand_core", + "proc-macro2", + "quote", + "syn 2.0.71", ] [[package]] -name = "regex" -version = "1.5.5" +name = "thread_local" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "cfg-if", + "once_cell", ] [[package]] -name = "regex-syntax" -version = "0.6.25" +name = "time" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] [[package]] -name = "route-recognizer" -version = "0.3.1" +name = "time-core" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] -name = "router" -version = "0.1.0" +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ - "yew-router", + "num-conv", + "time-core", ] [[package]] -name = "rustc_version" -version = "0.2.3" +name = "tinyvec" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ - "semver", + "tinyvec_macros", ] [[package]] -name = "ryu" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" - -[[package]] -name = "safemem" -version = "0.3.3" +name = "tinyvec_macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] -name = "same-file" -version = "1.0.6" +name = "tokio" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ - "winapi-util", + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.7", + "tokio-macros", + "windows-sys 0.48.0", ] [[package]] -name = "scoped-tls-hkt" -version = "0.1.2" +name = "tokio-macros" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e9d7eaddb227e8fbaaa71136ae0e1e913ca159b86c7da82f3e8f0044ad3a63" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] [[package]] -name = "semver" -version = "0.9.0" +name = "tokio-native-tls" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ - "semver-parser", + "native-tls", + "tokio", ] [[package]] -name = "semver-parser" -version = "0.7.0" +name = "tokio-rustls" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.11", + "rustls-pki-types", + "tokio", +] [[package]] -name = "serde" -version = "1.0.137" +name = "tokio-socks" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0" dependencies = [ - "serde_derive", + "either", + "futures-util", + "thiserror", + "tokio", ] [[package]] -name = "serde-wasm-bindgen" -version = "0.3.1" +name = "tokio-stream" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "618365e8e586c22123d692b72a7d791d5ee697817b65a218cdf12a98870af0f7" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ - "fnv", - "js-sys", - "serde", - "wasm-bindgen", + "futures-core", + "pin-project-lite", + "tokio", ] [[package]] -name = "serde_derive" -version = "1.0.137" +name = "tokio-util" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ - "proc-macro2", - "quote", - "syn", + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", ] [[package]] -name = "serde_json" -version = "1.0.81" +name = "toml" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ - "itoa", - "ryu", "serde", ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "toml" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ - "form_urlencoded", - "itoa", - "ryu", "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.15", ] [[package]] -name = "services" -version = "0.1.0" +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ - "chrono", - "lazy_static", - "log", - "material-yew", - "once_cell", - "pulldown-cmark", - "regex", "serde", - "serde_json", - "syntect", - "urlencoding", - "wasm-logger", - "web-sys", - "yew", ] [[package]] -name = "sha1" -version = "0.6.1" +name = "toml_edit" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "sha1_smol", + "indexmap", + "toml_datetime", + "winnow 0.5.40", ] [[package]] -name = "sha1_smol" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" - -[[package]] -name = "slab" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stdweb" -version = "0.4.20" +name = "toml_edit" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "discard", - "rustc_version", - "serde", - "serde_json", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", + "indexmap", + "toml_datetime", + "winnow 0.5.40", ] [[package]] -name = "stdweb-derive" -version = "0.5.3" +name = "toml_edit" +version = "0.22.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" dependencies = [ - "proc-macro2", - "quote", + "indexmap", "serde", - "serde_derive", - "syn", + "serde_spanned", + "toml_datetime", + "winnow 0.6.13", ] [[package]] -name = "stdweb-internal-macros" -version = "0.2.9" +name = "tower" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn", + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", ] [[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - -[[package]] -name = "stylist" -version = "0.10.0" +name = "tower-http" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60e9891bea9a4ce255d4acb69f29cbaf935c7feb1d884f351f420f1ee3c58371" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" dependencies = [ - "fastrand", - "gloo-events", - "instant", - "once_cell", - "stylist-core", - "stylist-macros", - "wasm-bindgen", - "web-sys", - "yew", + "bitflags 1.3.2", + "bytes", + "futures-core", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", ] [[package]] -name = "stylist-core" -version = "0.10.0" +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "333985f39743918cb904b0fda040e333702d3bad8badd52070285733d84858a8" -dependencies = [ - "nom 7.1.1", - "once_cell", - "thiserror", - "wasm-bindgen", -] +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] -name = "stylist-macros" -version = "0.10.0" +name = "tracing" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "871fd12661cfc46fbf90d4075121d895fa6c21016882599a8b2704e3c208ab8d" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "itertools", - "litrs", "log", - "nom 7.1.1", - "proc-macro-error", - "proc-macro2", - "quote", - "stylist-core", - "syn", + "pin-project-lite", + "tracing-attributes", + "tracing-core", ] [[package]] -name = "syn" -version = "1.0.92" +name = "tracing-attributes" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "syn 2.0.71", ] [[package]] -name = "syntect" -version = "4.6.0" +name = "tracing-core" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b20815bbe80ee0be06e6957450a841185fcf690fe0178f14d77a05ce2caa031" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ - "bincode", - "bitflags", - "fancy-regex", - "flate2", - "fnv", - "lazy_static", - "lazycell", - "plist", - "regex-syntax", - "serde", - "serde_derive", - "serde_json", - "walkdir", - "yaml-rust", + "once_cell", ] [[package]] -name = "thiserror" -version = "1.0.31" +name = "tracing-subscriber" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ - "thiserror-impl", + "matchers", + "once_cell", + "regex", + "sharded-slab", + "thread_local", + "tracing", + "tracing-core", ] [[package]] -name = "thiserror-impl" -version = "1.0.31" +name = "try-lock" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] -name = "time" -version = "0.1.44" +name = "typenum" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] -name = "time" -version = "0.3.9" +name = "unicase" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ - "itoa", - "libc", - "num_threads", + "version_check", ] [[package]] -name = "ui" -version = "0.1.0" -dependencies = [ - "global", - "log", - "material-yew", - "router", - "services", - "stylist", - "urlencoding", - "utils", - "wasm-logger", - "web-sys", - "yew", - "yew-router", -] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] -name = "unicase" -version = "2.6.0" +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ - "version_check", + "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] -name = "unicode-xid" -version = "0.2.3" +name = "unicode_categories" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] [[package]] name = "urlencoding" -version = "2.1.0" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b90931029ab9b034b300b797048cf23723400aa757e8a2bfb9d748102f9821" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] -name = "utils" -version = "0.1.0" +name = "utf8-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ - "chrono", - "global", - "js-sys", - "services", - "wasm-bindgen", - "web-sys", - "yew", + "serde", ] +[[package]] +name = "value-bag" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" + +[[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.4" @@ -1232,62 +4427,64 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] -name = "walkdir" -version = "2.3.2" +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + +[[package]] +name = "want" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "same-file", - "winapi", - "winapi-util", + "try-lock", ] [[package]] name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" +name = "wasite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.80" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ - "cfg-if 1.0.0", - "serde", - "serde_json", + "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.80" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.71", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.30" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "wasm-bindgen", "web-sys", @@ -1295,9 +4492,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.80" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1305,22 +4502,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.80" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.71", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.80" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-logger" @@ -1335,24 +4532,38 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.57" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] -name = "wee_alloc" -version = "0.4.5" +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ - "cfg-if 0.1.10", - "libc", - "memory_units", - "winapi", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "whoami" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +dependencies = [ + "redox_syscall 0.4.1", + "wasite", ] [[package]] @@ -1373,11 +4584,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -1387,33 +4598,223 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "xml-rs" -version = "0.8.4" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] [[package]] -name = "yaml-rust" -version = "0.4.5" +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yaml-rust2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +checksum = "8902160c4e6f2fb145dbe9d6760a75e3c9522d8bf796ed7047c85919ac7115f8" dependencies = [ - "linked-hash-map", + "arraydeque", + "encoding_rs", + "hashlink", ] [[package]] name = "yew" -version = "0.19.3" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a1ccb53e57d3f7d847338cf5758befa811cabe207df07f543c06f502f9998cd" +checksum = "5f1a03f255c70c7aa3e9c62e15292f142ede0564123543c1cc0c7a4f31660cac" dependencies = [ + "base64ct", + "bincode", "console_error_panic_hook", - "gloo 0.4.2", - "gloo-utils", + "futures", + "gloo 0.10.0", + "html-escape", + "implicit-clone", "indexmap", "js-sys", - "scoped-tls-hkt", + "prokio", + "rustversion", + "serde", "slab", + "thiserror", + "tokio", + "tracing", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -1422,32 +4823,32 @@ dependencies = [ [[package]] name = "yew-macro" -version = "0.19.3" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fab79082b556d768d6e21811869c761893f0450e1d550a67892b9bce303b7bb" +checksum = "02fd8ca5166d69e59f796500a2ce432ff751edecbbb308ca59fd3fe4d0343de2" dependencies = [ "boolinator", - "lazy_static", + "once_cell", + "prettyplease", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 2.0.71", ] [[package]] name = "yew-router" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155804f6f3aa309f596d5c3fa14486a94e7756f1edd7634569949e401d5099f2" +checksum = "4ca1d5052c96e6762b4d6209a8aded597758d442e6c479995faf0c7b5538e0c6" dependencies = [ - "gloo 0.4.2", - "gloo-utils", + "gloo 0.10.0", "js-sys", "route-recognizer", "serde", - "serde-wasm-bindgen", "serde_urlencoded", - "thiserror", + "tracing", + "urlencoding", "wasm-bindgen", "web-sys", "yew", @@ -1456,49 +4857,37 @@ dependencies = [ [[package]] name = "yew-router-macro" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39049d193b52eaad4ffc80916bf08806d142c90b5edcebd527644de438a7e19a" +checksum = "42bfd190a07ca8cfde7cd4c52b3ac463803dc07323db8c34daa697e86365978c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.71", ] [[package]] -name = "zzhack" -version = "0.1.0" +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "about", - "anyhow", - "base64", - "chrono", - "css-in-rust", - "global", - "home", - "js-sys", - "lazy_static", - "links", - "log", - "material-yew", - "not_found", - "once_cell", - "post", - "projects", - "pulldown-cmark", - "regex", - "router", - "serde", - "serde_json", - "services", - "stdweb", - "stylist", - "ui", - "utils", - "wasm-bindgen", - "wasm-logger", - "web-sys", - "wee_alloc", - "yew", - "yew-router", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", ] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index 2e54fb3..d1d015e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,3 @@ [workspace] -members = [ - "app", - "ui", - "utils", - "services", - "global", - "pages/home", - "pages/projects", - "pages/not_found", - "pages/home", - "pages/links", - "pages/post", - "router", -] +members = [ "api","app", "entry", "markdown", "shared", "site_config", "migration"] diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 0ad25db..0000000 --- a/LICENSE +++ /dev/null @@ -1,661 +0,0 @@ - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU Affero General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see -. diff --git a/README.md b/README.md index 69d1bcc..a2caba4 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,59 @@ -![zzhack_banner](https://raw.githubusercontent.com/zzhack-stack/zzhack/main/doc/zzhack_banner.png) -

zzhack

+# zzhack +WIP -zzhack is a simple but beauty WASM App template, which based on by Rust & Yew, if you want to create your own WASM site using zzhack template, just get start by [zzhack-cli](https://github.com/zzhack-stack/zzhack-cli) +## TODO +**Design** +- [x] Nav +- [ ] Homepage +- [ ] Posts page +- [ ] Post page +- [ ] 404 page +- [ ] About page +- [ ] Links page +- [ ] Error page -![sketch](https://raw.githubusercontent.com/zzhack-stack/zzhack/main/doc/zzhack_sketch.png) -## License -GNU GPLv3. +## Migrate database +The zzhack use SQLite as database, and use `sea-orm` to do migration, you may need to execute migrate before start server: +```shell +DATABASE_URL=sqlite://zzhack.db?mode=rwc sea-orm-cli migrate up +``` + +### Auto generate entities from database +`sea-orm-cli` provide ability to auto-generate entities and corresponding relations code from database, if you want to update the entities code, please run the following command: + +```shell +DATABASE_URL=sqlite://zzhack.db?mode=rwc sea-orm-cli generate entity --with-serde serialize -o ./api/src/database/models +``` + + +## How to start dev server +SSR is a experimental feature of Yew. you need to build both client bundle and server source code when the source code was changed. + +We recommaned you install `cargo-watch` to trigger command execution. +```shell +Cargo install cargo-watch +``` + +Go to the root of project. Build client bundle and watch: +```shell +cargo watch -C entry -i dist -i public -i styles -i zzhack.db -i assets -- trunk build +``` + +Then build server bin and watch: +```shell +cargo watch -C entry -- cargo run --features=ssr --bin zzhack_main -- --dir dist +``` + + +### TailwindCSS +zzhack depends on `TailwindCSS` for CSS compilation, before this step you may need to install NPM dependencies: +```shell +pnpm i +``` + +And then run the following command to get start: +```shell +npx tailwindcss -i ./entry/styles/input.css -o ./entry/styles/output.css -- --watch +``` + diff --git a/api/Cargo.toml b/api/Cargo.toml new file mode 100644 index 0000000..5c185a7 --- /dev/null +++ b/api/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "api" +version = "0.1.0" +edition = "2021" + +[dependencies] +axum = "0.6" +chrono = "0.4.38" +gray_matter = "0.2.8" +serde = "1.0.204" +site_config = { path = "../site_config" } +anyhow = "1.0.86" +shared = { path = "../shared" } +markdown = { path = "../markdown"} +tokio = "1.38.0" +toml = "0.8.14" +sea-orm = { version = "1.0.1", features = ["macros", "runtime-tokio-rustls", "sqlx-sqlite"] } +futures = "0.3.30" + diff --git a/api/src/controllers.rs b/api/src/controllers.rs new file mode 100644 index 0000000..2b8666c --- /dev/null +++ b/api/src/controllers.rs @@ -0,0 +1,4 @@ +pub mod dynamic; +pub mod links; +pub mod post; +pub mod tag; diff --git a/api/src/controllers/dynamic.rs b/api/src/controllers/dynamic.rs new file mode 100644 index 0000000..5d1aefb --- /dev/null +++ b/api/src/controllers/dynamic.rs @@ -0,0 +1,14 @@ +use axum::{extract::Path, http::StatusCode, Json}; + +use crate::{ + error::ResponseResultExt, services::dynamic_posts_service::get_dynamic_post_rendered_content, +}; + +pub async fn get_dynamic_post_content( + Path(path): Path, +) -> Result { + let rendered_content = + get_dynamic_post_rendered_content(&path).into_response_result(StatusCode::BAD_REQUEST)?; + + Ok(rendered_content) +} diff --git a/api/src/controllers/links.rs b/api/src/controllers/links.rs new file mode 100644 index 0000000..0223362 --- /dev/null +++ b/api/src/controllers/links.rs @@ -0,0 +1,11 @@ +use axum::{http::StatusCode, Json}; +use shared::links::LinksConfig; + +use crate::{error::ResponseResultExt, services::links_service}; + +pub async fn get_links() -> Result, (StatusCode, String)> { + let links_config = + links_service::get_links_config().into_response_result(StatusCode::BAD_REQUEST)?; + + Ok(Json(links_config)) +} diff --git a/api/src/controllers/post.rs b/api/src/controllers/post.rs new file mode 100644 index 0000000..599d544 --- /dev/null +++ b/api/src/controllers/post.rs @@ -0,0 +1,2 @@ +pub mod get_post_detail; +pub mod get_posts; diff --git a/api/src/controllers/post/get_post_detail.rs b/api/src/controllers/post/get_post_detail.rs new file mode 100644 index 0000000..6cbb549 --- /dev/null +++ b/api/src/controllers/post/get_post_detail.rs @@ -0,0 +1,18 @@ +use crate::{error::ResponseResultExt, services::post_service, AppState}; +use axum::{ + extract::{Path, State}, + http::StatusCode, + Json, +}; +use shared::post::PostDetail; + +pub async fn get_post_detail( + state: State, + Path(id): Path, +) -> Result, (StatusCode, String)> { + let detail = post_service::get_post_detail(&state.conn, id) + .await + .into_response_result(StatusCode::BAD_REQUEST)?; + + Ok(Json(detail)) +} diff --git a/api/src/controllers/post/get_posts.rs b/api/src/controllers/post/get_posts.rs new file mode 100644 index 0000000..23aad95 --- /dev/null +++ b/api/src/controllers/post/get_posts.rs @@ -0,0 +1,52 @@ +use axum::{ + extract::{Query, State}, + http::StatusCode, + Json, +}; +use serde::{Deserialize, Serialize}; +use shared::post::{PaginationPostsRes, Post}; + +use crate::{ + error::ResponseResultExt, + services::post_service::{get_pagination_posts, get_posts_by_tag_id, get_posts_count}, + AppState, +}; + +#[derive(Serialize, Deserialize)] +pub struct QueryParams { + page_limit: u64, + page: u64, + tag_id: Option, +} + +pub async fn get_posts( + state: State, + query_params: Query, +) -> Result>, (StatusCode, String)> { + let total = get_posts_count(&state.conn) + .await + .into_response_result(StatusCode::INTERNAL_SERVER_ERROR)?; + let has_next = (query_params.page + 1) * query_params.page_limit < total; + + let posts = match query_params.tag_id { + None => get_pagination_posts(&state.conn, query_params.page_limit, query_params.page) + .await + .into_response_result(StatusCode::BAD_REQUEST), + Some(tag_id) => get_posts_by_tag_id( + &state.conn, + tag_id, + query_params.page_limit, + query_params.page, + ) + .await + .into_response_result(StatusCode::BAD_REQUEST), + }?; + + Ok(Json(PaginationPostsRes { + total, + has_next, + page_limit: query_params.page_limit, + page: query_params.page_limit, + posts, + })) +} diff --git a/api/src/controllers/tag.rs b/api/src/controllers/tag.rs new file mode 100644 index 0000000..fc9e7ed --- /dev/null +++ b/api/src/controllers/tag.rs @@ -0,0 +1,24 @@ +use crate::{error::ResponseResultExt, services::tag_service::get_all_tags, AppState}; +use axum::{ + extract::{Query, State}, + http::StatusCode, + Json, +}; +use serde::Deserialize; +use shared::tag::Tag; + +#[derive(Deserialize)] +pub struct QueryParams { + post_id: Option, +} + +pub async fn get_tags( + state: State, + query_params: Query, +) -> Result>, (StatusCode, String)> { + let tags = get_all_tags(&state.conn, query_params.post_id) + .await + .into_response_result(StatusCode::INTERNAL_SERVER_ERROR)?; + + Ok(Json(tags)) +} diff --git a/api/src/dao.rs b/api/src/dao.rs new file mode 100644 index 0000000..9d8520b --- /dev/null +++ b/api/src/dao.rs @@ -0,0 +1,3 @@ +pub mod post; +pub mod post_tags; +pub mod tag; diff --git a/api/src/dao/post.rs b/api/src/dao/post.rs new file mode 100644 index 0000000..9395bf1 --- /dev/null +++ b/api/src/dao/post.rs @@ -0,0 +1,119 @@ +use crate::{ + database::models::{prelude::Tags, tags}, + utils::vector_convert::convert_vecs, +}; +use sea_orm::{ + sea_query::OnConflict, ColumnTrait, DatabaseConnection, DeleteResult, EntityTrait, + InsertResult, PaginatorTrait, QueryFilter, QuerySelect, +}; +use shared::post::{IntoPost, Post, PostDetail}; + +use crate::database::{ + connection::DBResult, + models::posts::{ActiveModel, Column, Entity, Model}, +}; + +pub async fn get_post_by_id( + db: &DatabaseConnection, + id: i32, +) -> DBResult)>> { + Entity::find_by_id(id).find_with_related(Tags).all(db).await +} + +pub async fn get_posts_by_tag_id( + db: &DatabaseConnection, + tag_id: i32, + page_limit: u64, + page: u64, +) -> DBResult)>> { + Tags::find_by_id(tag_id) + .find_with_related(Entity) + .offset(page) + .limit(page_limit) + .all(db) + .await +} + +pub async fn get_post_by_path(db: &DatabaseConnection, path: &str) -> DBResult> { + Entity::find().filter(Column::Path.eq(path)).one(db).await +} + +pub async fn get_posts_count(db: &DatabaseConnection) -> DBResult { + Entity::find().count(db).await +} + +pub async fn get_posts_by_page( + db: &DatabaseConnection, + page: u64, + page_limit: u64, +) -> DBResult> { + Entity::find() + .columns([ + Column::Id, + Column::Path, + Column::Title, + Column::Spoiler, + Column::CreatedAt, + Column::UpdatedAt, + ]) + .to_owned() + .paginate(db, page_limit) + .fetch_page(page) + .await +} + +pub async fn delete_posts_by_paths( + db: &DatabaseConnection, + local_paths: &Vec, +) -> DBResult { + Entity::delete_many() + .filter(Column::Path.is_not_in(local_paths)) + .exec(db) + .await +} + +pub async fn upsert_post( + db: &DatabaseConnection, + post: ActiveModel, +) -> DBResult> { + Entity::insert(post) + .on_conflict( + OnConflict::column(Column::Path) + .update_columns([ + Column::Content, + Column::Title, + Column::Spoiler, + Column::UpdatedAt, + ]) + .to_owned(), + ) + .exec(db) + .await +} + +impl IntoPost for Model { + fn into_post>(self, tags: Vec) -> Post { + Post { + id: self.id, + path: self.path, + spoiler: self.spoiler.unwrap_or_default(), + title: self.title, + created_at: self.created_at, + updated_at: self.updated_at, + tags: convert_vecs(tags), + } + } +} + +impl IntoPost for Model { + fn into_post>(self, tags: Vec) -> PostDetail { + PostDetail { + id: self.id, + content: self.content, + title: self.title, + created_at: self.created_at, + updated_at: self.updated_at, + tags: convert_vecs(tags), + } + } +} diff --git a/api/src/dao/post_tags.rs b/api/src/dao/post_tags.rs new file mode 100644 index 0000000..7a5f592 --- /dev/null +++ b/api/src/dao/post_tags.rs @@ -0,0 +1,91 @@ +use crate::{ + database::models::{ + post_tags::{ActiveModel, Column, Entity}, + tags::{Column as TagsColumn, Entity as Tags}, + }, + utils::{ + gray_matter, + helpers::{filter_record_not_insert_error, parse_load_many_result}, + vector_convert::convert_vecs, + }, +}; +use sea_orm::{ + sea_query::OnConflict, ColumnTrait, Condition, ConnectionTrait, EntityTrait, QueryFilter, Set, +}; + +pub async fn insert_post_tags( + db: &T, + tags: &Vec, + post_id: i32, +) { + // let tags_text = tags + // .into_iter() + // .map(|tag| tag.text()) + // .collect::>(); + // let tags = Tags::find().all(db).await.unwrap(); + // let new_tags = tags_text + // .iter() + // .filter(|tag_text| { + // !tags + // .into_iter() + // .map(|tag| tag.text) + // .collect::>() + // .contains(tag_text) + // }) + // .collect::>(); + + println!("post_id: {:?}", post_id); + + filter_record_not_insert_error( + Entity::insert_many( + [1, 2] + .into_iter() + .map(|tag_id| ActiveModel { + tag_id: Set(tag_id), + post_id: Set(post_id), + ..Default::default() + }) + .collect::>(), + ) + .on_empty_do_nothing() + .on_conflict( + OnConflict::columns([Column::PostId, Column::TagId]) + .do_nothing() + .to_owned(), + ) + .exec(db) + .await, + ); +} + +// When users delete some tag from a post, we should delete the corresponding row in the post_tags table. +// And if the deleted tag is not used by any other post, we should also delete the tag from the tags table. +pub async fn delete_tags_by_post_id( + db: &T, + tags: &Vec, + post_id: i32, +) { + let need_delete_tags = Entity::find() + .filter(Column::PostId.eq(post_id)) + .find_with_related(Tags) + .filter(TagsColumn::Text.is_not_in(convert_vecs::(tags.clone()))) + .all(db) + .await + .unwrap(); + + let need_delete_tag_ids = parse_load_many_result(need_delete_tags) + .into_iter() + .map(|tag| tag.id) + .collect::>(); + + // Delete tags in tags table, which has been deleted from disk + Entity::delete_many() + .filter( + Condition::all() + .add(Column::PostId.eq(post_id)) + .add(Column::TagId.is_in(need_delete_tag_ids)), + ) + .exec(db) + .await + .unwrap(); +} diff --git a/api/src/dao/tag.rs b/api/src/dao/tag.rs new file mode 100644 index 0000000..f413ea8 --- /dev/null +++ b/api/src/dao/tag.rs @@ -0,0 +1,100 @@ +use futures::future::join_all; +use sea_orm::ActiveValue::NotSet; +use sea_orm::{ + sea_query::OnConflict, ConnectionTrait, DatabaseConnection, DbErr, EntityTrait, Set, + TransactionError, TransactionTrait, +}; +use shared::tag::Tag; + +use crate::utils::gray_matter; +use crate::{ + database::{ + connection::DBResult, + models::{ + prelude::Posts, + tags::{ActiveModel, Column, Entity, Model}, + }, + }, + utils::helpers::parse_load_many_result, +}; + +use super::post_tags::insert_post_tags; + +impl Into for &gray_matter::Tag { + fn into(self) -> ActiveModel { + match self { + gray_matter::Tag::Text(text) => ActiveModel { + text: Set(text.to_string()), + color: NotSet, + ..Default::default() + }, + gray_matter::Tag::CustomColor { text, color } => ActiveModel { + text: Set(text.to_string()), + color: Set(Some(color.to_string())), + ..Default::default() + }, + } + } +} + +async fn upsert_tags(db: &T, tags: &Vec) -> Vec { + let tags_active_model_futures = tags.into_iter().map(|tag| async { + let active_model: ActiveModel = tag.into(); + + Entity::insert(active_model) + .on_conflict( + OnConflict::column(Column::Text) + .update_column(Column::Color) + .to_owned(), + ) + .exec(db) + .await + }); + + join_all(tags_active_model_futures) + .await + .into_iter() + // Performing an upsert statement without inserting or updating any of the row will result in a DbErr::RecordNotInserted error. + .filter(|insert_result| !matches!(insert_result, Err(DbErr::RecordNotInserted))) + .map(|insert_result| insert_result.unwrap().last_insert_id) + .collect::>() +} + +pub async fn upsert_tags_with_post_id( + db: &DatabaseConnection, + tags: Vec, + post_id: i32, +) -> DBResult<(), TransactionError> { + db.transaction::<_, (), DbErr>(|txn| { + Box::pin(async move { + upsert_tags(txn, &tags).await; + insert_post_tags(txn, &tags, post_id).await; + + Ok(()) + }) + }) + .await +} + +pub async fn get_all_tags(db: &DatabaseConnection) -> DBResult> { + Entity::find().all(db).await +} + +pub async fn get_tags_by_post_id(db: &DatabaseConnection, post_id: i32) -> DBResult> { + let results = Posts::find_by_id(post_id) + .find_with_related(Entity) + .all(db) + .await?; + + Ok(parse_load_many_result(results)) +} + +impl Into for Model { + fn into(self) -> Tag { + Tag { + id: self.id, + text: self.text, + color: self.color, + } + } +} diff --git a/api/src/database.rs b/api/src/database.rs new file mode 100644 index 0000000..a90d425 --- /dev/null +++ b/api/src/database.rs @@ -0,0 +1,3 @@ +pub mod connection; +pub mod initialize; +pub mod models; diff --git a/api/src/database/connection.rs b/api/src/database/connection.rs new file mode 100644 index 0000000..b63a26b --- /dev/null +++ b/api/src/database/connection.rs @@ -0,0 +1,10 @@ +use sea_orm::{Database, DatabaseConnection, DbErr}; + +pub type DBResult = Result; + +pub async fn get_db_connection() -> DatabaseConnection { + // By default, sea_orm using sqlx::Pool for connection pool. + Database::connect("sqlite://../zzhack.db?mode=rwc") + .await + .expect("Connect to SQLite failed") +} diff --git a/api/src/database/initialize.rs b/api/src/database/initialize.rs new file mode 100644 index 0000000..cd8b70c --- /dev/null +++ b/api/src/database/initialize.rs @@ -0,0 +1,113 @@ +use crate::{ + dao::{ + post::{delete_posts_by_paths, get_post_by_path, upsert_post}, + post_tags::delete_tags_by_post_id, + tag::upsert_tags_with_post_id, + }, + utils::{ + gray_matter::{get_post_content, get_post_front_matter, Tag}, + post::get_markdown_path, + }, +}; +use chrono::{DateTime, Utc}; +use sea_orm::{DatabaseConnection, Set}; +use std::{ + fs::{read_dir, read_to_string, DirEntry}, + path::PathBuf, + time::SystemTime, +}; + +use super::models::posts::ActiveModel; + +fn format_system_time_to_rfc2822(time: SystemTime) -> String { + let time: DateTime = time.into(); + + time.to_rfc2822() +} + +// Create a new tag if it does not exist in the database +// Delete the tag if it is not used by the post which id is post_id, +// but keep the tag in the tags table +async fn upsert_tags(db: &DatabaseConnection, tags: Option>, post_id: i32) { + let tags = tags.unwrap_or_default(); + + upsert_tags_with_post_id(db, tags.clone(), post_id) + .await + .unwrap(); + + delete_tags_by_post_id(db, &tags.clone(), post_id).await; +} + +async fn upsert_posts(db: &DatabaseConnection, dir_entries: &Vec) -> anyhow::Result<()> { + for dir_entry in dir_entries { + let metadata = dir_entry.metadata()?; + let created_at = format_system_time_to_rfc2822(metadata.created().unwrap()); + let updated_at = format_system_time_to_rfc2822(metadata.modified().unwrap()); + let dir_path = dir_entry.path(); + let path = get_markdown_path(dir_path.clone()); + let stringify_dir_path = dir_path.to_string_lossy().to_string(); + + // If updated_at is the same, then we don't need to update the post + if let Some(post) = get_post_by_path(db, &stringify_dir_path).await? { + if post.updated_at == updated_at { + continue; + } + } + + let content = read_to_string(path.clone())?; + let front_matter = get_post_front_matter(&content); + let content = get_post_content(&content); + let tags = front_matter.tags; + + println!("Upserting tags: {:?}", tags); + + let post_active_model = ActiveModel { + path: Set(stringify_dir_path.clone()), + content: Set(markdown::parse::parse_markdown(&content)), + title: Set(front_matter.title), + spoiler: Set(Some(front_matter.spoiler)), + created_at: Set(created_at), + updated_at: Set(updated_at.clone()), + ..Default::default() + }; + + upsert_post(db, post_active_model).await?; + let post = get_post_by_path(db, &stringify_dir_path).await?; + + upsert_tags(db, tags, post.unwrap().id).await; + } + + Ok(()) +} + +// Delete posts that is not in local_paths, which means these posts has been +// deleted on disk, so we need to delete corresponding post from database +async fn delete_posts( + conn: &DatabaseConnection, + dir_entries: &Vec, +) -> anyhow::Result<()> { + let posts_paths = dir_entries + .into_iter() + .map(|dir_entry| dir_entry.path().to_string_lossy().to_string()) + .collect::>(); + + delete_posts_by_paths(conn, &posts_paths).await?; + + Ok(()) +} + +// Regularly users just need to modified the posts folder to +// create post, the function will map these posts into database +// thus, users can access these posts through API +pub async fn initialize(conn: &DatabaseConnection) -> anyhow::Result<()> { + let site_config = site_config::get_site_config(); + let dir_entries = read_dir(PathBuf::from("..").join(site_config.root.posts_folder_name)) + .expect("Please make sure the posts folder exists") + .map(|dir_entry| dir_entry.unwrap()) + .collect::>(); + + upsert_posts(conn, &dir_entries).await?; + delete_posts(conn, &dir_entries).await?; + + Ok(()) +} diff --git a/api/src/database/models/mod.rs b/api/src/database/models/mod.rs new file mode 100644 index 0000000..4d21d28 --- /dev/null +++ b/api/src/database/models/mod.rs @@ -0,0 +1,7 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 + +pub mod prelude; + +pub mod post_tags; +pub mod posts; +pub mod tags; diff --git a/api/src/database/models/post_tags.rs b/api/src/database/models/post_tags.rs new file mode 100644 index 0000000..69ef1ce --- /dev/null +++ b/api/src/database/models/post_tags.rs @@ -0,0 +1,47 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 + +use sea_orm::entity::prelude::*; +use serde::Serialize; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize)] +#[sea_orm(table_name = "post_tags")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub post_id: i32, + #[sea_orm(primary_key, auto_increment = false)] + pub tag_id: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::posts::Entity", + from = "Column::PostId", + to = "super::posts::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Posts, + #[sea_orm( + belongs_to = "super::tags::Entity", + from = "Column::TagId", + to = "super::tags::Column::Id", + on_update = "Cascade", + on_delete = "Cascade" + )] + Tags, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Posts.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Tags.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/api/src/database/models/posts.rs b/api/src/database/models/posts.rs new file mode 100644 index 0000000..4d8fc85 --- /dev/null +++ b/api/src/database/models/posts.rs @@ -0,0 +1,41 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 + +use sea_orm::entity::prelude::*; +use serde::Serialize; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize)] +#[sea_orm(table_name = "posts")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub title: String, + #[sea_orm(unique)] + pub path: String, + pub spoiler: Option, + pub content: String, + pub created_at: String, + pub updated_at: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::post_tags::Entity")] + PostTags, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::PostTags.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::post_tags::Relation::Tags.def() + } + fn via() -> Option { + Some(super::post_tags::Relation::Posts.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/api/src/database/models/prelude.rs b/api/src/database/models/prelude.rs new file mode 100644 index 0000000..6c85ebd --- /dev/null +++ b/api/src/database/models/prelude.rs @@ -0,0 +1,5 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 + +pub use super::post_tags::Entity as PostTags; +pub use super::posts::Entity as Posts; +pub use super::tags::Entity as Tags; diff --git a/api/src/database/models/tags.rs b/api/src/database/models/tags.rs new file mode 100644 index 0000000..e50f74d --- /dev/null +++ b/api/src/database/models/tags.rs @@ -0,0 +1,37 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 + +use sea_orm::entity::prelude::*; +use serde::Serialize; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize)] +#[sea_orm(table_name = "tags")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + #[sea_orm(unique)] + pub text: String, + pub color: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::post_tags::Entity")] + PostTags, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::PostTags.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + super::post_tags::Relation::Posts.def() + } + fn via() -> Option { + Some(super::post_tags::Relation::Tags.def().rev()) + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/api/src/error.rs b/api/src/error.rs new file mode 100644 index 0000000..f656bd7 --- /dev/null +++ b/api/src/error.rs @@ -0,0 +1,12 @@ +use anyhow::Result; +use axum::http::StatusCode; + +pub trait ResponseResultExt { + fn into_response_result(self, status_code: StatusCode) -> Result; +} + +impl ResponseResultExt for Result { + fn into_response_result(self, status_code: StatusCode) -> Result { + self.map_err(|err| (status_code, err.to_string())) + } +} diff --git a/api/src/lib.rs b/api/src/lib.rs new file mode 100644 index 0000000..0c783c6 --- /dev/null +++ b/api/src/lib.rs @@ -0,0 +1,27 @@ +pub mod controllers; +pub mod dao; +pub mod database; +mod error; +mod routes; +pub mod services; +pub mod utils; + +use axum::Router; +use routes::{ + dynamic_posts::get_dynamic_posts_routes, links::get_links_routes, post::get_posts_routes, + tag::get_tags_routes, +}; +use sea_orm::DatabaseConnection; + +#[derive(Clone)] +pub struct AppState { + pub conn: DatabaseConnection, +} + +pub fn get_api_routes() -> Router { + Router::new() + .nest("/posts", get_posts_routes()) + .nest("/links", get_links_routes()) + .nest("/dynamic", get_dynamic_posts_routes()) + .nest("/tags", get_tags_routes()) +} diff --git a/api/src/routes.rs b/api/src/routes.rs new file mode 100644 index 0000000..6915e0e --- /dev/null +++ b/api/src/routes.rs @@ -0,0 +1,4 @@ +pub mod dynamic_posts; +pub mod links; +pub mod post; +pub mod tag; diff --git a/api/src/routes/dynamic_posts.rs b/api/src/routes/dynamic_posts.rs new file mode 100644 index 0000000..e4b8850 --- /dev/null +++ b/api/src/routes/dynamic_posts.rs @@ -0,0 +1,7 @@ +use axum::{routing::get, Router}; + +use crate::{controllers::dynamic::get_dynamic_post_content, AppState}; + +pub fn get_dynamic_posts_routes() -> Router { + Router::new().route("/post/*path", get(get_dynamic_post_content)) +} diff --git a/api/src/routes/links.rs b/api/src/routes/links.rs new file mode 100644 index 0000000..b9659b4 --- /dev/null +++ b/api/src/routes/links.rs @@ -0,0 +1,6 @@ +use crate::{controllers::links::get_links, AppState}; +use axum::{routing::get, Router}; + +pub fn get_links_routes() -> Router { + Router::new().route("/", get(get_links)) +} diff --git a/api/src/routes/post.rs b/api/src/routes/post.rs new file mode 100644 index 0000000..493cbf1 --- /dev/null +++ b/api/src/routes/post.rs @@ -0,0 +1,11 @@ +use crate::{ + controllers::post::{get_post_detail::get_post_detail, get_posts::get_posts}, + AppState, +}; +use axum::{routing::get, Router}; + +pub fn get_posts_routes() -> Router { + Router::new() + .route("/", get(get_posts)) + .route("/:id", get(get_post_detail)) +} diff --git a/api/src/routes/tag.rs b/api/src/routes/tag.rs new file mode 100644 index 0000000..2cf6068 --- /dev/null +++ b/api/src/routes/tag.rs @@ -0,0 +1,6 @@ +use crate::{controllers::tag::get_tags, AppState}; +use axum::{routing::get, Router}; + +pub fn get_tags_routes() -> Router { + Router::new().route("/", get(get_tags)) +} diff --git a/api/src/services.rs b/api/src/services.rs new file mode 100644 index 0000000..be0311c --- /dev/null +++ b/api/src/services.rs @@ -0,0 +1,4 @@ +pub mod dynamic_posts_service; +pub mod links_service; +pub mod post_service; +pub mod tag_service; diff --git a/api/src/services/dynamic_posts_service.rs b/api/src/services/dynamic_posts_service.rs new file mode 100644 index 0000000..9a12955 --- /dev/null +++ b/api/src/services/dynamic_posts_service.rs @@ -0,0 +1,15 @@ +use anyhow::Result; +use markdown::parse::parse_markdown; +use std::{fs::read_to_string, path::PathBuf}; + +pub fn get_dynamic_post_rendered_content(path: &str) -> Result { + let config = site_config::get_site_config(); + let about_me_content = read_to_string( + PathBuf::from("..") + .join(config.root.dynamic_pages_folder_name) + .join(path) + .with_extension("md"), + )?; + + Ok(parse_markdown(&about_me_content)) +} diff --git a/api/src/services/links_service.rs b/api/src/services/links_service.rs new file mode 100644 index 0000000..5c27d4f --- /dev/null +++ b/api/src/services/links_service.rs @@ -0,0 +1,13 @@ +use anyhow::{anyhow, Result}; +use shared::links::LinksConfig; +use std::fs::read_to_string; + +const SIDE_CONFIG_PATH: &'static str = "../links.toml"; + +pub fn get_links_config() -> Result { + let config_string = read_to_string(SIDE_CONFIG_PATH) + .map_err(|_| anyhow!("Read links failed, cannot find links.toml"))?; + let config = toml::from_str::(&config_string)?; + + Ok(config) +} diff --git a/api/src/services/post_service.rs b/api/src/services/post_service.rs new file mode 100644 index 0000000..a70fadf --- /dev/null +++ b/api/src/services/post_service.rs @@ -0,0 +1,66 @@ +use crate::dao::tag::get_tags_by_post_id; +use crate::dao::{self}; +use crate::database::models::posts::Model; +use crate::utils::helpers::parse_load_many_result; +use anyhow::{bail, Result}; +use futures::future::join_all; +use sea_orm::DatabaseConnection; +use shared::post::{IntoPost, Post, PostDetail}; + +pub async fn get_pagination_posts( + conn: &DatabaseConnection, + page_limit: u64, + page: u64, +) -> Result> { + let posts = dao::post::get_posts_by_page(conn, page, page_limit) + .await? + .into_iter() + .map(|post| async { + let tags = get_tags_by_post_id(conn, post.id).await.unwrap(); + + post.into_post(tags) + }); + let posts_with_tags = join_all(posts).await; + + Ok(posts_with_tags) +} + +pub async fn get_posts_by_tag_id( + db: &DatabaseConnection, + tag_id: i32, + page_limit: u64, + page: u64, +) -> Result> { + let result = dao::post::get_posts_by_tag_id(db, tag_id, page_limit, page * page_limit).await?; + + if result.len() == 0 { + Ok(vec![]) + } else { + let (tag, posts) = result[0].clone(); + + let posts = posts + .into_iter() + .map(move |post_model| post_model.into_post(vec![tag.clone()])) + .collect::>(); + + Ok(posts) + } +} + +pub async fn get_posts_count(conn: &DatabaseConnection) -> Result { + let posts_count = dao::post::get_posts_count(conn).await?; + + Ok(posts_count) +} + +pub async fn get_post_detail(conn: &DatabaseConnection, id: i32) -> Result { + let result = dao::post::get_post_by_id(conn, id).await?; + + if result.len() == 0 { + bail!("Cannot find post with id {}", id) + } + + let (post_model, tags) = result[0].clone(); + + Ok(post_model.into_post(tags)) +} diff --git a/api/src/services/tag_service.rs b/api/src/services/tag_service.rs new file mode 100644 index 0000000..250761b --- /dev/null +++ b/api/src/services/tag_service.rs @@ -0,0 +1,19 @@ +use sea_orm::DatabaseConnection; +use shared::tag::Tag; + +use crate::{ + dao::{self, tag}, + utils::vector_convert::convert_vecs, +}; + +pub async fn get_all_tags( + db: &DatabaseConnection, + post_id: Option, +) -> anyhow::Result> { + let tags = match post_id { + Some(post_id) => dao::tag::get_tags_by_post_id(db, post_id).await, + None => tag::get_all_tags(db).await, + }?; + + Ok(convert_vecs(tags)) +} diff --git a/api/src/utils.rs b/api/src/utils.rs new file mode 100644 index 0000000..c6dc50c --- /dev/null +++ b/api/src/utils.rs @@ -0,0 +1,4 @@ +pub mod gray_matter; +pub mod helpers; +pub mod post; +pub mod vector_convert; diff --git a/api/src/utils/gray_matter.rs b/api/src/utils/gray_matter.rs new file mode 100644 index 0000000..bd80cee --- /dev/null +++ b/api/src/utils/gray_matter.rs @@ -0,0 +1,52 @@ +use gray_matter::{engine::YAML, Matter, ParsedEntity}; +use serde::Deserialize; + +#[derive(Deserialize, Debug, Clone)] +#[serde(untagged)] +pub enum Tag { + Text(String), + CustomColor { text: String, color: String }, +} + +impl Tag { + pub fn text(&self) -> String { + match self { + Tag::Text(text) => text, + Tag::CustomColor { text, .. } => text, + } + .to_string() + } +} + +impl Into for Tag { + fn into(self) -> String { + self.text() + } +} + +#[derive(Deserialize, Debug)] +pub struct PostFrontMatter { + pub title: String, + pub spoiler: String, + pub tags: Option>, +} + +fn parse_gray_matter(content: &str) -> ParsedEntity { + let matter = Matter::::new(); + let result = matter.parse(content); + + result +} + +pub fn get_post_front_matter(content: &str) -> PostFrontMatter { + parse_gray_matter(content) + .data + .expect("Failed to parse front matter") + .deserialize::() + .unwrap() +} + +// Return post content without front matter +pub fn get_post_content(content: &str) -> String { + parse_gray_matter(content).content +} diff --git a/api/src/utils/helpers.rs b/api/src/utils/helpers.rs new file mode 100644 index 0000000..5ec6399 --- /dev/null +++ b/api/src/utils/helpers.rs @@ -0,0 +1,21 @@ +use sea_orm::DbErr; + +// SeaORM return Vec<(Model, Vec)>, but in most cases, we just need the second Model vector +pub fn parse_load_many_result(result: Vec<(T, Vec)>) -> Vec { + if result.len() == 0 { + vec![] + } else { + let (_, inner) = result[0].clone(); + + inner + } +} + +// SearORM will return DbErr::RecordNotInserted error when execute DoNothing on_conflict +// So we need to filter this error to avoid panic +pub fn filter_record_not_insert_error(result: Result) { + match result { + Err(err) if err != DbErr::RecordNotInserted => panic!("{}", err), + _ => (), + } +} diff --git a/api/src/utils/post.rs b/api/src/utils/post.rs new file mode 100644 index 0000000..808ab8a --- /dev/null +++ b/api/src/utils/post.rs @@ -0,0 +1,67 @@ +use std::{ + fs::{read_dir, read_to_string}, + path::PathBuf, +}; + +use super::gray_matter::get_post_front_matter; + +const DEFAULT_POST_NAME: &'static str = "index.md"; + +// Basically the type of posts are combine with directory or single Markdown file +// if the path is directory, return self/index.md. +// if the path is markdown file return itself +// if the path is markdown file without extension, return itself.md +pub fn get_markdown_path(post_path: PathBuf) -> PathBuf { + if post_path.is_dir() { + return post_path.join(DEFAULT_POST_NAME); + } + + let mut os_string = post_path.clone().into_os_string(); + + if os_string.to_str().unwrap().ends_with(".md") { + return post_path; + } + + os_string.push(".md"); + os_string.into() +} + +const AVERAGE_WORD_PER_MINUTE: usize = 400; +const MIN_READ_MINUTES: usize = 1; + +// Calculate the word per minute +pub fn calc_read_minutes(content: &str) -> usize { + let read_minutes = content.len() / AVERAGE_WORD_PER_MINUTE; + + if read_minutes < MIN_READ_MINUTES { + MIN_READ_MINUTES + } else { + read_minutes + } +} + +struct PostBriefInfo { + name: String, + create_at: String, + spoiler: String, + read_minutes: usize, +} + +fn get_posts_list(current_page: usize) -> Vec { + read_dir("../posts") + .unwrap() + .map(|post| { + let post_entry = post.unwrap(); + let content = read_to_string(get_markdown_path(post_entry.path())).unwrap(); + let gray_matter = get_post_front_matter(&content); + let read_minutes = calc_read_minutes(&content); + + PostBriefInfo { + name: gray_matter.title, + spoiler: gray_matter.spoiler, + create_at: "Tue Jul 09 2024 20:48:31 GMT+0800 (China Standard Time)".to_string(), + read_minutes, + } + }) + .collect::>() +} diff --git a/api/src/utils/vector_convert.rs b/api/src/utils/vector_convert.rs new file mode 100644 index 0000000..3659a34 --- /dev/null +++ b/api/src/utils/vector_convert.rs @@ -0,0 +1,6 @@ +pub fn convert_vecs(v: Vec) -> Vec +where + T: Into, +{ + v.into_iter().map(Into::into).collect() +} diff --git a/app/.gitignore b/app/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/app/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/app/Cargo.toml b/app/Cargo.toml index 72776e7..c17dfd4 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -1,45 +1,35 @@ [package] -name = "zzhack" -version = "0.1.0" -authors = ["mistricky "] -edition = "2018" +name = "app" +version = "0.0.1" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -yew = "0.19.3" -wasm-bindgen = "0.2.80" -material-yew = { git = "https://github.com/hamza1311/material-yew", features = ["full"] } -stdweb = "0.4.20" -css-in-rust = "0.5.0" -web-sys = { version = "0.3", features = ["HtmlMetaElement", "Document", "Element", "DocumentFragment", "HtmlTemplateElement", "MediaQueryList"] } -pulldown-cmark = "0.8.0" -once_cell = "1.8.0" -anyhow = "1.0.41" -serde = "1.0.126" -regex = "1.5.4" -serde_json = "1.0.64" -chrono = "0.4.19" -yew-router = "0.16.0" -base64 = "0.13.0" -js-sys = "0.3.55" -lazy_static = "1.4.0" -stylist = {version = "0.10", features = ["yew_integration"]} -wasm-logger = "0.2.0" -log = "0.4.17" -wee_alloc = "0.4.5" -services = {path = "../services"} -global = {path = "../global"} -utils = {path = "../utils"} -ui = {path = "../ui"} -post = {path = "../pages/post"} -home = {path = "../pages/home"} -about = {path = "../pages/about"} -not_found = {path = "../pages/not_found"} -projects = {path = "../pages/projects"} -links = {path = "../pages/links"} -router = {path = "../router"} +gray_matter = "0.2.8" +js-sys = "0.3.69" +log = "0.4.22" +serde = "1.0.204" +serde_json = "1.0.120" +web-sys = "0.3.69" +yew = { version = "0.21.0", features = ["hydration", "ssr"]} +yew-router = "0.18.0" +shared = {path = "../shared"} +reqwest = { version = "0.12.5", features = ["json", "socks"] } +wasm-bindgen = "0.2.92" +wasm-bindgen-macro = "0.2.92" +site_config = { path = "../site_config" } +chrono = "0.4.38" +cached = "0.53.1" -[profile.release] -lto = true -panic = 'abort' -codegen-units = 1 +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +api = {path = "../api"} + +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen-futures = "0.4.42" +wasm-bindgen = "0.2.92" +wasm-logger = "0.2.0" +[features] +hydration = ["yew/hydration"] +ssr = ["yew/ssr"] diff --git a/app/README.md b/app/README.md deleted file mode 100644 index e16730e..0000000 --- a/app/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# zzhack -My personal blog site which based on by Rust & Yew, the motivation for build this site is just I want a place where I can thinking and write it down. - -## UI Design -Not surprisingly, I designed three versions of the design, actually as you can see the current version is 3.0.0 version. - -![sketch](https://raw.githubusercontent.com/zzhack-stack/zzhack/main/doc/zzhack_sketch.png) - -It's worth to mention that, oh I love dark mode, so the `zzhack` also support dark mode of course. - -![zzhack dark mode](https://raw.githubusercontent.com/zzhack-stack/zzhack/main/doc/zzhack_dark_mode.png) - -![zzhack light mode](https://raw.githubusercontent.com/zzhack-stack/zzhack/main/doc/zzhack_light_mode.png) - -If you have some advice for design, please contact me by [email](Mailto:mist.zzh@gmail.com), also if you want my sketches of zzhack, just send me email. - -## Links -The zzhack have a full page to display links, so if you wanna add your website to zzhack, please comment in [here](https://github.com/zzhack-stack/zzhack/issues/4). - -![zzhack links](https://raw.githubusercontent.com/zzhack-stack/zzhack/main/doc/zzhack_links.png) - - -## License -MIT. diff --git a/app/Trunk.toml b/app/Trunk.toml deleted file mode 100644 index 4f65bc1..0000000 --- a/app/Trunk.toml +++ /dev/null @@ -1,5 +0,0 @@ -# rapper reduce -t ./templates/md_parser.template -i ./templates/md_parser_iteration.template --target ./posts/ -e md -d ./services/src/posts.rs -[[hooks]] -stage = "pre_build" -command = "rapper" -command_arguments = ["reduce", "-t", "../templates/md_parser.template", "-i", "../templates/md_parser_iteration.template", "--target", "../posts/", "-e", "md", "-d", "../services/src/posts.rs"] diff --git a/app/assets/images/$dark_$mobile_banner.png b/app/assets/images/$dark_$mobile_banner.png deleted file mode 100644 index 5478a73..0000000 Binary files a/app/assets/images/$dark_$mobile_banner.png and /dev/null differ diff --git a/app/assets/images/$dark_$mobile_banner.svg b/app/assets/images/$dark_$mobile_banner.svg deleted file mode 100644 index 78ea935..0000000 --- a/app/assets/images/$dark_$mobile_banner.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - Group 2 - - - - - - - - - - - - - Do more useless things - - - - - \ No newline at end of file diff --git a/app/assets/images/$dark_$mobile_projects_banner.svg b/app/assets/images/$dark_$mobile_projects_banner.svg deleted file mode 100644 index da7e56d..0000000 --- a/app/assets/images/$dark_$mobile_projects_banner.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - Group - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @mistricky - - - - - \ No newline at end of file diff --git a/app/assets/images/$dark_banner.png b/app/assets/images/$dark_banner.png deleted file mode 100644 index 12c5e1b..0000000 Binary files a/app/assets/images/$dark_banner.png and /dev/null differ diff --git a/app/assets/images/$dark_banner.svg b/app/assets/images/$dark_banner.svg deleted file mode 100644 index f82a1f6..0000000 --- a/app/assets/images/$dark_banner.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - Group 2 - - - - - - - - - - - - - Do more useless things - - - - - \ No newline at end of file diff --git a/app/assets/images/$dark_close_btn.svg b/app/assets/images/$dark_close_btn.svg deleted file mode 100644 index 0b3a55f..0000000 --- a/app/assets/images/$dark_close_btn.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/$dark_drawer.svg b/app/assets/images/$dark_drawer.svg deleted file mode 100644 index 7f4d702..0000000 --- a/app/assets/images/$dark_drawer.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - Group - - - - - - - - - - - \ No newline at end of file diff --git a/app/assets/images/$dark_github.svg b/app/assets/images/$dark_github.svg deleted file mode 100644 index 598f29a..0000000 --- a/app/assets/images/$dark_github.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - icons/github_dark - - - - - - - - \ No newline at end of file diff --git a/app/assets/images/$dark_projects_banner.svg b/app/assets/images/$dark_projects_banner.svg deleted file mode 100644 index c59444d..0000000 --- a/app/assets/images/$dark_projects_banner.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - Group 2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @mistricky - - - - - \ No newline at end of file diff --git a/app/assets/images/$dark_setting.svg b/app/assets/images/$dark_setting.svg deleted file mode 100644 index 07ddff7..0000000 --- a/app/assets/images/$dark_setting.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - foo - - - - - - - - - \ No newline at end of file diff --git a/app/assets/images/$dark_zzhack_logo.png b/app/assets/images/$dark_zzhack_logo.png deleted file mode 100644 index 2d1b5a3..0000000 Binary files a/app/assets/images/$dark_zzhack_logo.png and /dev/null differ diff --git a/app/assets/images/$dark_zzhack_logo.svg b/app/assets/images/$dark_zzhack_logo.svg deleted file mode 100644 index 4a196e7..0000000 --- a/app/assets/images/$dark_zzhack_logo.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - r - - - - zzhack - - - - \ No newline at end of file diff --git a/app/assets/images/$light_$mobile_banner.png b/app/assets/images/$light_$mobile_banner.png deleted file mode 100644 index b3a9129..0000000 Binary files a/app/assets/images/$light_$mobile_banner.png and /dev/null differ diff --git a/app/assets/images/$light_$mobile_banner.svg b/app/assets/images/$light_$mobile_banner.svg deleted file mode 100644 index f4a5308..0000000 --- a/app/assets/images/$light_$mobile_banner.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - Group 2 - - - - - - - - - - - - - Do more useless things - - - - - \ No newline at end of file diff --git a/app/assets/images/$light_$mobile_projects_banner.svg b/app/assets/images/$light_$mobile_projects_banner.svg deleted file mode 100644 index acabd1b..0000000 --- a/app/assets/images/$light_$mobile_projects_banner.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - Group 2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @mistricky - - - - - \ No newline at end of file diff --git a/app/assets/images/$light_banner.png b/app/assets/images/$light_banner.png deleted file mode 100644 index 1b2eb7a..0000000 Binary files a/app/assets/images/$light_banner.png and /dev/null differ diff --git a/app/assets/images/$light_banner.svg b/app/assets/images/$light_banner.svg deleted file mode 100644 index aff2e6d..0000000 --- a/app/assets/images/$light_banner.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - Group 4 - - - - - - - - - - - - - Do more useless things - - - - - \ No newline at end of file diff --git a/app/assets/images/$light_close_btn.svg b/app/assets/images/$light_close_btn.svg deleted file mode 100644 index 989837c..0000000 --- a/app/assets/images/$light_close_btn.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/images/$light_drawer.svg b/app/assets/images/$light_drawer.svg deleted file mode 100644 index 559896b..0000000 --- a/app/assets/images/$light_drawer.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - Group - - - - - - - - - - - - \ No newline at end of file diff --git a/app/assets/images/$light_github.svg b/app/assets/images/$light_github.svg deleted file mode 100644 index efc8977..0000000 --- a/app/assets/images/$light_github.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - icons/github_dark - - - - - - - - \ No newline at end of file diff --git a/app/assets/images/$light_projects_banner.svg b/app/assets/images/$light_projects_banner.svg deleted file mode 100644 index adbfcdb..0000000 --- a/app/assets/images/$light_projects_banner.svg +++ /dev/null @@ -1,54 +0,0 @@ - - - Group 2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @mistricky - - - - - \ No newline at end of file diff --git a/app/assets/images/$light_setting.svg b/app/assets/images/$light_setting.svg deleted file mode 100644 index 50a9464..0000000 --- a/app/assets/images/$light_setting.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - foo - - - - - - - - - \ No newline at end of file diff --git a/app/assets/images/$light_zzhack_logo.png b/app/assets/images/$light_zzhack_logo.png deleted file mode 100644 index 2766833..0000000 Binary files a/app/assets/images/$light_zzhack_logo.png and /dev/null differ diff --git a/app/assets/images/$light_zzhack_logo.svg b/app/assets/images/$light_zzhack_logo.svg deleted file mode 100644 index 110d637..0000000 --- a/app/assets/images/$light_zzhack_logo.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - r - - - - zzhack - - - - \ No newline at end of file diff --git a/app/assets/images/$mobile_links_banner.svg b/app/assets/images/$mobile_links_banner.svg deleted file mode 100644 index 686972d..0000000 --- a/app/assets/images/$mobile_links_banner.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - Group - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/assets/images/about_compare.svg b/app/assets/images/about_compare.svg deleted file mode 100644 index 44f8229..0000000 --- a/app/assets/images/about_compare.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - Group 5 - - - - - - - - - - zzhack - - - - - \ No newline at end of file diff --git a/app/assets/images/about_design_lang.svg b/app/assets/images/about_design_lang.svg deleted file mode 100644 index 35bdffb..0000000 --- a/app/assets/images/about_design_lang.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - Bitmap - - - - - - \ No newline at end of file diff --git a/app/assets/images/about_zzhack.svg b/app/assets/images/about_zzhack.svg deleted file mode 100644 index edd1969..0000000 --- a/app/assets/images/about_zzhack.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - Group - - - - - - Version 5 - - - zzhack - - - - - \ No newline at end of file diff --git a/app/assets/images/auto_mode_skeleton.png b/app/assets/images/auto_mode_skeleton.png deleted file mode 100644 index 013190d..0000000 Binary files a/app/assets/images/auto_mode_skeleton.png and /dev/null differ diff --git a/app/assets/images/avatar.png b/app/assets/images/avatar.png deleted file mode 100644 index a1aa917..0000000 Binary files a/app/assets/images/avatar.png and /dev/null differ diff --git a/app/assets/images/dark_mode.svg b/app/assets/images/dark_mode.svg deleted file mode 100644 index 68d23f1..0000000 --- a/app/assets/images/dark_mode.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - dark_mode - - - - - - - - - - - \ No newline at end of file diff --git a/app/assets/images/dark_mode_skeleton.png b/app/assets/images/dark_mode_skeleton.png deleted file mode 100644 index c630246..0000000 Binary files a/app/assets/images/dark_mode_skeleton.png and /dev/null differ diff --git a/app/assets/images/discord.svg b/app/assets/images/discord.svg deleted file mode 100644 index e1717f7..0000000 --- a/app/assets/images/discord.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - Bitmap - - - - - - \ No newline at end of file diff --git a/app/assets/images/gmail.svg b/app/assets/images/gmail.svg deleted file mode 100644 index 0b2c752..0000000 --- a/app/assets/images/gmail.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - Bitmap - - - - - - \ No newline at end of file diff --git a/app/assets/images/guide_post.png b/app/assets/images/guide_post.png deleted file mode 100644 index f4a85b1..0000000 Binary files a/app/assets/images/guide_post.png and /dev/null differ diff --git a/app/assets/images/label.png b/app/assets/images/label.png deleted file mode 100644 index 69fe778..0000000 Binary files a/app/assets/images/label.png and /dev/null differ diff --git a/app/assets/images/light_mode.svg b/app/assets/images/light_mode.svg deleted file mode 100644 index 422e023..0000000 --- a/app/assets/images/light_mode.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - light_mode - - - - - - - - - - - \ No newline at end of file diff --git a/app/assets/images/light_mode_skeleton.png b/app/assets/images/light_mode_skeleton.png deleted file mode 100644 index 8a41323..0000000 Binary files a/app/assets/images/light_mode_skeleton.png and /dev/null differ diff --git a/app/assets/images/links_banner.svg b/app/assets/images/links_banner.svg deleted file mode 100644 index fa05995..0000000 --- a/app/assets/images/links_banner.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - Group - - - - - - - - - - - - - - - diff --git a/app/assets/images/page_not_found.png b/app/assets/images/page_not_found.png deleted file mode 100644 index 0ab056c..0000000 Binary files a/app/assets/images/page_not_found.png and /dev/null differ diff --git a/app/assets/images/switch_theme_guide.png b/app/assets/images/switch_theme_guide.png deleted file mode 100644 index 63bdd5e..0000000 Binary files a/app/assets/images/switch_theme_guide.png and /dev/null differ diff --git a/app/assets/images/switch_theme_guide.svg b/app/assets/images/switch_theme_guide.svg deleted file mode 100644 index 871137d..0000000 --- a/app/assets/images/switch_theme_guide.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - Group 6 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/assets/images/twitter.svg b/app/assets/images/twitter.svg deleted file mode 100644 index da15202..0000000 --- a/app/assets/images/twitter.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - Bitmap - - - - - - \ No newline at end of file diff --git a/app/assets/images/wechat.svg b/app/assets/images/wechat.svg deleted file mode 100644 index 3dfdf0d..0000000 --- a/app/assets/images/wechat.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - Bitmap - - - - - - \ No newline at end of file diff --git a/app/assets/images/zzhack_favicon.svg b/app/assets/images/zzhack_favicon.svg deleted file mode 100644 index b746f28..0000000 --- a/app/assets/images/zzhack_favicon.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - Group - - - - - z - - - - \ No newline at end of file diff --git a/app/assets/sources/blog_cover.svg b/app/assets/sources/blog_cover.svg deleted file mode 100644 index 45d1744..0000000 --- a/app/assets/sources/blog_cover.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - Artboard - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/assets/sources/dark_mode_skeleton.png b/app/assets/sources/dark_mode_skeleton.png deleted file mode 100644 index c630246..0000000 Binary files a/app/assets/sources/dark_mode_skeleton.png and /dev/null differ diff --git a/app/assets/sources/dynamic_dispatch.png b/app/assets/sources/dynamic_dispatch.png deleted file mode 100644 index ff37def..0000000 Binary files a/app/assets/sources/dynamic_dispatch.png and /dev/null differ diff --git a/app/assets/sources/into_the_wild.jpg b/app/assets/sources/into_the_wild.jpg deleted file mode 100644 index 7fb2c11..0000000 Binary files a/app/assets/sources/into_the_wild.jpg and /dev/null differ diff --git a/app/assets/sources/issues_dispatch.png b/app/assets/sources/issues_dispatch.png deleted file mode 100644 index aceca6a..0000000 Binary files a/app/assets/sources/issues_dispatch.png and /dev/null differ diff --git a/app/assets/sources/links_cover.png b/app/assets/sources/links_cover.png deleted file mode 100644 index 160255d..0000000 Binary files a/app/assets/sources/links_cover.png and /dev/null differ diff --git a/app/assets/sources/links_cover.svg b/app/assets/sources/links_cover.svg deleted file mode 100644 index 6ee7fef..0000000 --- a/app/assets/sources/links_cover.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - Artboard - - - - - \ No newline at end of file diff --git a/app/assets/sources/mlog_cover.png b/app/assets/sources/mlog_cover.png deleted file mode 100644 index bf54904..0000000 Binary files a/app/assets/sources/mlog_cover.png and /dev/null differ diff --git a/app/assets/sources/provider_dispatch.png b/app/assets/sources/provider_dispatch.png deleted file mode 100644 index 643a873..0000000 Binary files a/app/assets/sources/provider_dispatch.png and /dev/null differ diff --git a/app/assets/sources/static_dispatch.png b/app/assets/sources/static_dispatch.png deleted file mode 100644 index 85206ad..0000000 Binary files a/app/assets/sources/static_dispatch.png and /dev/null differ diff --git a/app/assets/sources/wasm_fib.png b/app/assets/sources/wasm_fib.png deleted file mode 100644 index a7a960d..0000000 Binary files a/app/assets/sources/wasm_fib.png and /dev/null differ diff --git a/app/assets/sources/yew_logo.png b/app/assets/sources/yew_logo.png deleted file mode 100644 index 26eb8fb..0000000 Binary files a/app/assets/sources/yew_logo.png and /dev/null differ diff --git a/app/assets/styles/base.scss b/app/assets/styles/base.scss deleted file mode 100644 index b880e50..0000000 --- a/app/assets/styles/base.scss +++ /dev/null @@ -1,104 +0,0 @@ -:root { - --mdc-theme-primary: #7ECCB4; - - mwc-icon-button::part("mdc-icon-button") { - background: red; - } - - .light { - --primary-color: #7ECCB4; - --base-color: #fff; - --underlay-color: #F3F7FD; - --text-color: #3F3D55; - --card-color: #fff; - --mask-color: rgba(0, 0, 0, 0.45); - --shallow-gray: rgba(112, 112, 112, 0.06); - --sub-text-color: rgba(63, 61, 85, 0.63); - --primary-shadow-color: rgb(127 214 194 / 22%); - --card-shadow-color: rgba(149, 157, 165, 0.2); - --label-color: rgba(126, 204, 180, 0.36); - --tip-color: rgba(63, 61, 85, 0.39); - --code-block-bg: #282c34; - --alert-color: rgba(32, 136, 255, 0.2); - --blue: #4794FE; - --code-color: var(--text-color); - --code-bg: rgba(126, 204, 180, 0.35); - } - - .dark { - --code-bg: rgba(126, 204, 180, 0.35); - --code-color: var(--text-color); - --blue: #4794FE; - --alert-color: rgba(32, 136, 255, 0.2); - --code-block-bg: #282c34; - --label-color: rgba(126, 204, 180, 0.36); - --card-shadow-color: rgba(149, 157, 165, 0); - --primary-color: #7ECCB4; - --primary-shadow-color: rgb(127 214 194 / 22%); - --base-color: #31363F; - --underlay-color: #24292E; - --text-color: #D6D8DA; - --card-color: #31363F; - --mask-color: rgba(0, 0, 0, 0.83); - --shallow-gray: rgba(216, 216, 216, 0.08); - --sub-text-color: #768390; - --tip-color: #57606A; - } -} - -.text { - color: var(--normal-text-color); -} - -.article-text { - color: var(--article-text-color); -} - -.mini-container { - width: 600px; - margin: auto; -} - -.article-container { - width: 760px; - margin: auto; -} - -.non-style-link { - text-decoration: none; -} - -.articles { - background: var(--base-color); - border-radius: 3px; - box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px; - width: 100%; - padding: 10px 15px; -} - -.card { - border: 1px solid var(--border-color); - transition: box-shadow 0.3s; - box-shadow: rgba(9, 30, 66, 0.25) 0px 0px 0px; - - &:hover { - box-shadow: rgba(50, 50, 93, 0.25) 0px 50px 100px -20px, rgba(0, 0, 0, 0.3) 0px 30px 60px -30px; - border-color: transparent; - } -} - -@media (max-width: 600px) { - .container, .mini-container { - width: 100%; - overflow: hidden; - padding: 0 20px; - box-sizing: border-box; - } - - .article-container { - width: 100%; - overflow: hidden; - padding: 10px 30px; - box-sizing: border-box; - } -} diff --git a/app/assets/styles/markdown.scss b/app/assets/styles/markdown.scss deleted file mode 100644 index 0af23e9..0000000 --- a/app/assets/styles/markdown.scss +++ /dev/null @@ -1,194 +0,0 @@ -.markdown-heading { - font-size: 25px; - transition: 0.1s all; - cursor: pointer; - position: relative; - - &:hover { - color: var(--mdc-theme-primary); - } -} - -.markdown-heading-anchor { - text-decoration: none; -} - -.markdown-img-container { - max-width: 660px; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - margin: 30px 0; - - .markdown-img { - max-width: 660px; - max-height: 400px; - border-radius: 5px; - } - - .markdown-img-alt { - margin-top: 10px; - } -} - -blockquote { - border-left: 8px solid var(--mdc-theme-primary); - margin: 0; - padding: 10px; - background: rgba(57, 113, 245, 0.1); - border-radius: 5px; - padding-left: 15px; - - p { - color: var(--blockquote-color) !important; - font-size: 14px; - margin: 0; - } -} - -.markdown-spotlight { - display: flex; - justify-content: center; - align-items: center; - margin: 50px 0; - - .markdown-spotlight__content { - text-align: center; - font-style: italic; - margin: 0 20px; - font-size: 18px; - } - - .markdown-spotlight__icon { - width: 25px; - } -} - -.markdown-github-render-block-container { - .markdown-github-render-block-link { - text-decoration: none; - } - - .markdown-github-render-block { - max-width: 450px; - width: fit-content; - overflow: hidden; - border-radius: 5px; - background: var(--mdc-theme-primary); - padding: 15px; - box-sizing: border-box; - display: flex; - margin: 25px 0; - align-items: center; - box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; - - .markdown-github-render-block-icon { - width: 50px; - } - - .markdown-github-render-block-info { - margin-left: 15px; - - .markdown-github-render-block-repo { - color: white; - font-size: 16px; - } - - .markdown-github-render-block-desc { - color: white; - font-size: 14px; - line-height: 1.5; - } - } - - .markdown-github-render-block-goto { - width: 35px; - height: 35px; - flex-shrink: 0; - background: #fff; - border-radius: 50%; - display: flex; - justify-content: center; - align-items: center; - box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; - } - } -} - -.markdown-code { - background: var(--code-bg); - padding: 3px 7px; - border-radius: 5px; - font-size: 14px; - color: var(--code-color) !important; -} - -.markdown-code-block { - padding: 10px 15px; - background: var(--code-block-bg); - border-radius: 5px; - box-shadow: rgba(0, 0, 0, 0.02) 0px 1px 3px 0px, rgba(27, 31, 35, 0.15) 0px 0px 0px 1px; - - .markdown-mac-control-bars { - display: flex; - align-items: center; - - .control-bar { - width: 13px; - height: 13px; - margin-right: 8px; - background: #fff; - border-radius: 50%; - } - - .markdown-mac-close-bar { - @extend .control-bar; - - background: #ff5f57; - } - .markdown-mac-min-bar { - @extend .control-bar; - - background: #febc2e; - } - .markdown-mac-max-bar { - @extend .control-bar; - - background: #28c840; - } - } - - pre { - background-color: var(--code-block-bg) !important; - white-space: break-spaces; - } -} - -.markdown-container { - * { - color: var(--article-text-color); - } -} - -@media (max-width: 600px){ - .markdown-img-container { - max-width: 100%; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - - .markdown-img { - max-width: 100%; - border-radius: 5px; - } - } - - .markdown-spotlight { - .markdown-spotlight__icon { - width: 18px; - } - } - -} diff --git a/app/assets/styles/one-dark.scss b/app/assets/styles/one-dark.scss deleted file mode 100644 index 43c897b..0000000 --- a/app/assets/styles/one-dark.scss +++ /dev/null @@ -1,91 +0,0 @@ -/* -Atom One Dark by Daniel Gamage -Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax -base: #282c34 -mono-1: #abb2bf -mono-2: #818896 -mono-3: #5c6370 -hue-1: #56b6c2 -hue-2: #61aeee -hue-3: #c678dd -hue-4: #98c379 -hue-5: #e06c75 -hue-5-2: #be5046 -hue-6: #d19a66 -hue-6-2: #e6c07b -*/ - -.hljs { - color: #abb2bf; - background: #282c34; - } - - .hljs-comment, - .hljs-quote { - color: #5c6370; - font-style: italic; - } - - .hljs-doctag, - .hljs-keyword, - .hljs-formula { - color: #c678dd; - } - - .hljs-section, - .hljs-name, - .hljs-selector-tag, - .hljs-deletion, - .hljs-subst { - color: #e06c75; - } - - .hljs-literal { - color: #56b6c2; - } - - .hljs-string, - .hljs-regexp, - .hljs-addition, - .hljs-attribute, - .hljs-meta .hljs-string { - color: #98c379; - } - - .hljs-attr, - .hljs-variable, - .hljs-template-variable, - .hljs-type, - .hljs-selector-class, - .hljs-selector-attr, - .hljs-selector-pseudo, - .hljs-number { - color: #d19a66; - } - - .hljs-symbol, - .hljs-bullet, - .hljs-link, - .hljs-meta, - .hljs-selector-id, - .hljs-title { - color: #61aeee; - } - - .hljs-built_in, - .hljs-title.class_, - .hljs-class .hljs-title { - color: #e6c07b; - } - - .hljs-emphasis { - font-style: italic; - } - - .hljs-strong { - font-weight: bold; - } - - .hljs-link { - text-decoration: underline; - } \ No newline at end of file diff --git a/app/assets/zzhack-logo.dark.png b/app/assets/zzhack-logo.dark.png new file mode 100644 index 0000000..887b601 Binary files /dev/null and b/app/assets/zzhack-logo.dark.png differ diff --git a/app/assets/zzhack-logo.light.png b/app/assets/zzhack-logo.light.png new file mode 100644 index 0000000..8bf959c Binary files /dev/null and b/app/assets/zzhack-logo.light.png differ diff --git a/app/configs/md_translate_rs.template b/app/configs/md_translate_rs.template deleted file mode 100644 index e6d2a37..0000000 --- a/app/configs/md_translate_rs.template +++ /dev/null @@ -1 +0,0 @@ -static const content: &'static str = r#"{{TEMPLATE}}"#; diff --git a/app/configs/rs_aggregation.template b/app/configs/rs_aggregation.template deleted file mode 100644 index 8c52df5..0000000 --- a/app/configs/rs_aggregation.template +++ /dev/null @@ -1,3 +0,0 @@ -pub static posts: [&'static str; {{TRAVERSE_COUNT}}] = [ - {{TEMPLATE}} -]; diff --git a/app/configs/rs_iteration.template b/app/configs/rs_iteration.template deleted file mode 100644 index edf1e7a..0000000 --- a/app/configs/rs_iteration.template +++ /dev/null @@ -1 +0,0 @@ -r#"{{TEMPLATE}}"#, diff --git a/app/index.html b/app/index.html deleted file mode 100644 index 38f9dd8..0000000 --- a/app/index.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - zzhack - - - - - - - - - diff --git a/app/src/app.rs b/app/src/app.rs deleted file mode 100644 index 257169c..0000000 --- a/app/src/app.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::routes::RouteOutlet; -use global::theme_context::ThemeProvider; -use yew::prelude::*; - -#[function_component(App)] -pub fn app() -> Html { - html! { - - - - } -} diff --git a/app/src/components.rs b/app/src/components.rs new file mode 100644 index 0000000..6f4706f --- /dev/null +++ b/app/src/components.rs @@ -0,0 +1,6 @@ +pub mod card; +pub mod load_more; +pub mod nav; +pub mod post_item; +pub mod tag; +pub mod theme_img; diff --git a/app/src/components/card.rs b/app/src/components/card.rs new file mode 100644 index 0000000..d8ecf0c --- /dev/null +++ b/app/src/components/card.rs @@ -0,0 +1 @@ +pub mod outline_card; diff --git a/app/src/components/card/outline_card.rs b/app/src/components/card/outline_card.rs new file mode 100644 index 0000000..0dfcfa1 --- /dev/null +++ b/app/src/components/card/outline_card.rs @@ -0,0 +1,19 @@ +use yew::prelude::*; + +#[derive(Properties, PartialEq, Clone)] +pub struct LoadMoreProps { + pub children: Html, + #[prop_or_default] + pub class: String, +} + +#[function_component] +pub fn OutlineCard(props: &LoadMoreProps) -> Html { + html! { +
+
+ {props.children.clone()} +
+
+ } +} diff --git a/app/src/components/load_more.rs b/app/src/components/load_more.rs new file mode 100644 index 0000000..877b4f1 --- /dev/null +++ b/app/src/components/load_more.rs @@ -0,0 +1,20 @@ +use log::info; +use yew::prelude::*; + +#[derive(Properties, PartialEq, Clone)] +pub struct LoadMoreProps { + pub onload: Callback<()>, +} + +#[function_component] +pub fn LoadMore(props: &LoadMoreProps) -> Html { + let onload = props.onload.clone(); + let handle_load_more_click = Callback::from(move |_| { + info!("Hello"); + onload.emit(()); + }); + + html! { + + } +} diff --git a/app/src/components/nav.rs b/app/src/components/nav.rs new file mode 100644 index 0000000..a5da168 --- /dev/null +++ b/app/src/components/nav.rs @@ -0,0 +1,34 @@ +use shared::site_config::NavConfig; +use yew::prelude::*; + +use crate::{components::theme_img::ThemeImg, icons::GitHubIcon}; + +#[derive(Properties, PartialEq)] +pub struct NavProps { + pub nav_configs: Vec, +} + +#[function_component] +pub fn Nav() -> Html { + let site_config = site_config::get_site_config(); + let rendered_nav_items = site_config.nav.iter().map(|config| { + html! { + {&config.text} + } + }); + + html! { + + } +} diff --git a/app/src/components/post_item.rs b/app/src/components/post_item.rs new file mode 100644 index 0000000..e4bbd92 --- /dev/null +++ b/app/src/components/post_item.rs @@ -0,0 +1,42 @@ +use shared::post::Post; +use yew::prelude::*; + +use crate::components::tag::Tag; + +#[derive(Properties, PartialEq)] +pub struct PostItemProps { + pub post: Post, +} + +#[function_component] +pub fn PostItem(props: &PostItemProps) -> Html { + let post = &props.post; + let tags = post + .tags + .iter() + .map(|tag| { + html! { + {tag.text.clone()} + } + }) + .collect::>(); + + html! { + +
+
+
{&post.title}
+
+ {for tags} +
+
+
+ {&post.spoiler} +
+
+ {&post.created_at} +
+
+
+ } +} diff --git a/app/src/components/tag.rs b/app/src/components/tag.rs new file mode 100644 index 0000000..adcf542 --- /dev/null +++ b/app/src/components/tag.rs @@ -0,0 +1,21 @@ +use yew::prelude::*; + +#[derive(Properties, PartialEq)] +pub struct TagProps { + pub children: Html, + pub color: AttrValue, +} + +#[function_component] +pub fn Tag(props: &TagProps) -> Html { + let color = props.color.clone(); + let dynamic_color = format!("bg-[{}]", "#ff0000"); + let a = format!( + "text-xs mr-1.5 px-1.5 bg-opacity-25 rounded flex justify-center items-center {}", + dynamic_color + ); + + html! { +
{props.children.clone()}
+ } +} diff --git a/app/src/components/theme_img.rs b/app/src/components/theme_img.rs new file mode 100644 index 0000000..041483b --- /dev/null +++ b/app/src/components/theme_img.rs @@ -0,0 +1,30 @@ +use crate::utils::theme::{parse_theme_icon, with_class_prop}; +use yew::prelude::*; + +#[derive(Properties, PartialEq)] +pub struct ThemeImgProps { + pub src: String, + #[prop_or_default] + pub class: Option, + pub alt: String, +} + +#[function_component] +pub fn ThemeImg(props: &ThemeImgProps) -> Html { + let theme_icon = parse_theme_icon(&props.src); + + html! { + <> + {props.alt.to_string()} + {props.alt.to_string()} + + } +} diff --git a/app/src/http.rs b/app/src/http.rs new file mode 100644 index 0000000..8d55fdf --- /dev/null +++ b/app/src/http.rs @@ -0,0 +1,42 @@ +use reqwest::Response; + +pub struct HTTP { + base_url: String, +} + +impl HTTP { + pub fn new() -> HTTP { + HTTP { + base_url: Self::get_base_url("/api"), + } + } + + #[cfg(debug_assertions)] + #[cfg(not(target_arch = "wasm32"))] + fn get_port() -> usize { + site_config::get_site_config().server.dev_port + } + + #[cfg(not(debug_assertions))] + fn get_port() -> usize { + site_config::get_site_config().server.prod_port + } + + #[cfg(not(target_arch = "wasm32"))] + pub fn get_base_url(prefix: &'static str) -> String { + format!("http://localhost:{}{prefix}", Self::get_port()) + } + + #[cfg(target_arch = "wasm32")] + pub fn get_base_url(prefix: &'static str) -> String { + format!("{}{prefix}", web_sys::window().unwrap().origin()) + } + + fn with_base_url(&self, path: &str) -> String { + format!("{}{path}", self.base_url) + } + + pub async fn get(&self, path: &str) -> reqwest::Result { + reqwest::get(self.with_base_url(path)).await + } +} diff --git a/app/src/i18n.rs b/app/src/i18n.rs new file mode 100644 index 0000000..d6edf7c --- /dev/null +++ b/app/src/i18n.rs @@ -0,0 +1 @@ +pub mod translate; diff --git a/app/src/i18n/en.json b/app/src/i18n/en.json new file mode 100644 index 0000000..c7acc90 --- /dev/null +++ b/app/src/i18n/en.json @@ -0,0 +1,3 @@ +{ + "Hello": "World" +} diff --git a/app/src/i18n/translate.rs b/app/src/i18n/translate.rs new file mode 100644 index 0000000..315418b --- /dev/null +++ b/app/src/i18n/translate.rs @@ -0,0 +1,25 @@ +use cached::proc_macro::cached; +use serde_json::Value; + +const EN: &'static str = include_str!("en.json"); +const ZH: &'static str = include_str!("zh.json"); + +#[cached] +fn get_parsed_config() -> Value { + let site_config = site_config::get_site_config(); + let language = site_config.root.language.as_str(); + let language_source = match language { + "zh" => ZH, + "en" => EN, + _ => panic!("Unsupported language"), + }; + + serde_json::from_str(language_source).expect("Wrong format of language json") +} + +pub fn t(key: &str) -> String { + let config = get_parsed_config(); + let value = config.get(key).expect("Key not found"); + + value.as_str().expect("Value is not a string").to_string() +} diff --git a/app/src/i18n/zh.json b/app/src/i18n/zh.json new file mode 100644 index 0000000..14255a9 --- /dev/null +++ b/app/src/i18n/zh.json @@ -0,0 +1,3 @@ +{ + "Hello": "你好" +} diff --git a/app/src/icons.rs b/app/src/icons.rs new file mode 100644 index 0000000..a27b70c --- /dev/null +++ b/app/src/icons.rs @@ -0,0 +1,18 @@ +use yew::prelude::*; + +#[derive(Properties, PartialEq)] +pub struct IconProps { + #[prop_or(AttrValue::Static("none"))] + pub color: AttrValue, + pub height: String, + pub width: String, +} + +#[function_component] +pub fn GitHubIcon(props: &IconProps) -> Html { + html! { + + + + } +} diff --git a/app/src/lib.rs b/app/src/lib.rs new file mode 100644 index 0000000..5858fc5 --- /dev/null +++ b/app/src/lib.rs @@ -0,0 +1,8 @@ +mod components; +mod http; +mod i18n; +mod icons; +mod pages; +pub mod portal; +mod routes; +mod utils; diff --git a/app/src/main.rs b/app/src/main.rs deleted file mode 100644 index b5560e9..0000000 --- a/app/src/main.rs +++ /dev/null @@ -1,18 +0,0 @@ -mod app; -mod routes; - -extern crate lazy_static; -extern crate wee_alloc; - -use app::App; -use yew::start_app; - -// Use `wee_alloc` as the global allocator. -#[global_allocator] -static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; - -fn main() { - wasm_logger::init(wasm_logger::Config::default()); - - start_app::(); -} diff --git a/app/src/pages.rs b/app/src/pages.rs new file mode 100644 index 0000000..cde5a85 --- /dev/null +++ b/app/src/pages.rs @@ -0,0 +1,5 @@ +pub mod dynamic; +pub mod links; +pub mod not_found; +pub mod post; +pub mod posts; diff --git a/app/src/pages/dynamic.rs b/app/src/pages/dynamic.rs new file mode 100644 index 0000000..c9bdddd --- /dev/null +++ b/app/src/pages/dynamic.rs @@ -0,0 +1,48 @@ +use yew::prelude::*; + +use crate::utils::inner_html::parse_str_to_element; + +#[cfg(feature = "ssr")] +async fn fetch_dynamic_post_rendered_content(path: &str) -> String { + use crate::http::HTTP; + + HTTP::new() + .get(&format!("/dynamic/post/{path}")) + .await + .unwrap() + .text() + .await + .unwrap() +} + +#[derive(Properties, PartialEq)] +pub struct DynamicProps { + pub path: String, +} + +#[function_component] +pub fn Content(props: &DynamicProps) -> HtmlResult { + let path = props.path.clone(); + let prepared_dynamic_post_content = use_prepared_state!((), async move |_| -> String { + fetch_dynamic_post_rendered_content(&path).await + })? + .unwrap(); + + Ok(html! { +
+ {parse_str_to_element(&prepared_dynamic_post_content)} + +
+ }) +} + +#[function_component] +pub fn Dynamic(props: &DynamicProps) -> Html { + let fallback = html! {
{"Loading..."}
}; + + html! { + + + + } +} diff --git a/app/src/pages/links.rs b/app/src/pages/links.rs new file mode 100644 index 0000000..e5ae619 --- /dev/null +++ b/app/src/pages/links.rs @@ -0,0 +1,45 @@ +use shared::links::LinksConfig; +use yew::prelude::*; + +#[cfg(feature = "ssr")] +async fn fetch_links() -> LinksConfig { + use crate::http::HTTP; + + HTTP::new() + .get("/links") + .await + .unwrap() + .json::() + .await + .unwrap() +} + +#[function_component] +pub fn Content() -> HtmlResult { + let prepared_links = + use_prepared_state!((), async move |_| -> LinksConfig { fetch_links().await })?.unwrap(); + let rendered_links = prepared_links.links.iter().map(|link| { + html! { +
+ +
{&link.name}
+
{&link.description}
+
+ } + }); + + Ok(html! { + {for rendered_links} + }) +} + +#[function_component] +pub fn Links() -> Html { + let fallback = html! {
{"Loading..."}
}; + + html! { + + + + } +} diff --git a/app/src/pages/not_found.rs b/app/src/pages/not_found.rs new file mode 100644 index 0000000..0fff13e --- /dev/null +++ b/app/src/pages/not_found.rs @@ -0,0 +1,6 @@ +use yew::prelude::*; + +#[function_component] +pub fn NotFound() -> Html { + html! {"Not Found"} +} diff --git a/app/src/pages/post.rs b/app/src/pages/post.rs new file mode 100644 index 0000000..95e9e9f --- /dev/null +++ b/app/src/pages/post.rs @@ -0,0 +1,49 @@ +use crate::http::HTTP; +use crate::i18n::translate::t; +use shared::post::PostDetail; +use yew::functional::use_prepared_state; +use yew::prelude::*; + +use crate::utils::inner_html::parse_str_to_element; + +#[derive(Properties, PartialEq)] +pub struct PostProps { + pub id: usize, +} + +#[cfg(feature = "ssr")] +async fn fetch_post_detail(id: usize) -> PostDetail { + HTTP::new() + .get(&format!("/posts/{id}")) + .await + .unwrap() + .json::() + .await + .unwrap() +} + +#[function_component] +fn Content(props: &PostProps) -> HtmlResult { + let id = props.id.clone(); + let parepared_post_detail = use_prepared_state!((), async move |_| -> PostDetail { + fetch_post_detail(id).await + })? + .unwrap(); + + Ok(html! { +
+ {parse_str_to_element(&parepared_post_detail.content)} +
+ }) +} + +#[function_component] +pub fn Post(props: &PostProps) -> Html { + let fallback = html! {
{"Loading..."}
}; + + html! { + + + + } +} diff --git a/app/src/pages/posts.rs b/app/src/pages/posts.rs new file mode 100644 index 0000000..a8d1a61 --- /dev/null +++ b/app/src/pages/posts.rs @@ -0,0 +1,94 @@ +use std::rc::Rc; + +use crate::components::card::outline_card::OutlineCard; +use crate::components::{load_more::LoadMore, post_item::PostItem}; +use crate::http::HTTP; +use shared::post::{PaginationPostsRes, Post}; +use yew::{platform::spawn_local, prelude::*}; + +#[derive(Properties, PartialEq)] +struct PostItemProps { + title: String, + spoiler: String, +} + +#[cfg(feature = "ssr")] +async fn fetch_posts(page_limit: usize, page: usize) -> PaginationPostsRes { + HTTP::new() + .get(&format!("/posts?page_limit={page_limit}&page={page}")) + .await + .unwrap() + .json::>() + .await + .unwrap() +} + +const PAGE_LIMIT: usize = 10; +const INIT_PAGE: usize = 0; + +#[function_component] +pub fn Content() -> HtmlResult { + let page = use_state(|| INIT_PAGE); + let page_cloned = page.clone(); + + // The prepared_state macro return a Rc object, the Yew fetch data on server side + // and send this data to browser, then call hydrate, if we want to redefine a state + // that inited by prepared_state, we should extract value from Rc functor + let prepared_pagination_posts = + use_prepared_state!((), async move |_| -> PaginationPostsRes { + fetch_posts(PAGE_LIMIT, *page_cloned).await + })? + .unwrap(); + let pagination_posts = + Rc::try_unwrap(prepared_pagination_posts).unwrap_or_else(|rc| (*rc).clone()); + let has_load_more = use_state(|| pagination_posts.has_next); + let has_load_more_cloned = has_load_more.clone(); + let posts = use_state(|| pagination_posts.posts); + let posts_clone = posts.clone(); + let rendered_posts = posts_clone.iter().map(|post| { + html! { + + } + }); + + let handle_load_more_click = Callback::from(move |_| { + page.set(*page + 1); + let page = page.clone(); + let posts = posts.clone(); + let has_load_more = has_load_more_cloned.clone(); + + // Spawn local is used for convert JS Promise to Future + // the block of code which only running in browser + spawn_local(async move { + let pagination_posts = fetch_posts(PAGE_LIMIT, *page).await; + + posts.set([(*posts).clone(), pagination_posts.posts].concat()); + has_load_more.set(pagination_posts.has_next); + }); + }); + + Ok(html! { + +
+ {for rendered_posts} + if *has_load_more { + + } +
+
+ }) +} + +#[function_component] +pub fn Posts() -> HtmlResult { + let fallback = html! {
{"Loading..."}
}; + + // Any component want to do data fetch, there must have a Suspense component + // wrap the content component as children, because Yew need a vdom placeholder + // when start hydrate + Ok(html! { + + + + }) +} diff --git a/app/src/portal.rs b/app/src/portal.rs new file mode 100644 index 0000000..f11724d --- /dev/null +++ b/app/src/portal.rs @@ -0,0 +1,69 @@ +use std::collections::HashMap; + +use crate::components::nav::Nav; + +use super::routes::{switch, Routes}; +use shared::site_config::Config; +use yew::prelude::*; +use yew_router::history::{AnyHistory, History, MemoryHistory}; +use yew_router::prelude::*; + +#[derive(Properties, PartialEq, Eq, Debug)] +pub struct BrowserAppProps { + pub config: Config, +} + +#[function_component] +pub fn App() -> Html { + let cb = Callback::from(|_| { + let document = web_sys::window() + .unwrap() + .document() + .unwrap() + .document_element() + .unwrap(); + + document.set_class_name("dark"); + }); + + html! { +
+
+
+ +
+ } +} + +#[function_component] +pub fn BrowserApp() -> Html { + html! { + + + + } +} + +#[derive(Properties, PartialEq, Eq, Debug)] +pub struct ServerAppProps { + pub url: AttrValue, + pub queries: HashMap, +} + +#[function_component] +pub fn ServerApp(props: &ServerAppProps) -> Html { + let history = AnyHistory::from(MemoryHistory::new()); + + // Sync server route state to browser route state + history + .push_with_query(&*props.url, &props.queries) + .unwrap(); + + html! { + + + + } +} diff --git a/app/src/posts.rs b/app/src/posts.rs deleted file mode 100644 index e3a58eb..0000000 --- a/app/src/posts.rs +++ /dev/null @@ -1,30 +0,0 @@ -pub static posts: [&'static str; 9] = [ - r#"# A -abc -"#, -r#"# A -abc -"#, -r#"# A -abc -"#, -r#"# A -abc -"#, -r#"# A -abc -"#, -r#"# A -abc -"#, -r#"# A -abc -"#, -r#"# A -abc -"#, -r#"# A -abc -"#, - -]; diff --git a/app/src/routes.rs b/app/src/routes.rs index 3051de5..7cfc62b 100644 --- a/app/src/routes.rs +++ b/app/src/routes.rs @@ -1,36 +1,34 @@ -use post::Post; -use ui::layout::BaseLayout; use yew::prelude::*; use yew_router::prelude::*; -use about::About; -use home::Home; -use links::Links; -use not_found::NotFound; -use projects::Projects; -use router::RootRoutes; +use crate::pages::{dynamic::Dynamic, links::Links, not_found::NotFound, post::Post, posts::Posts}; -fn switch(routes: &RootRoutes) -> Html { - match routes { - RootRoutes::Home | RootRoutes::Root => html! { }, - RootRoutes::Projects => html! { }, - RootRoutes::About => html! { }, - RootRoutes::Post { filename } => html! {}, - RootRoutes::NotFound => html! { }, - RootRoutes::Technology => html! { - to={RootRoutes::Home}/> - }, - RootRoutes::Links => html! {}, - } +#[derive(Routable, PartialEq, Eq, Clone, Debug)] +pub enum Routes { + #[at("/post/:id")] + Post { id: usize }, + #[at("/posts")] + Posts, + #[at("/links")] + Links, + #[at("/")] + Home, + #[at("/pages/*path")] + Dynamic { path: String }, + #[not_found] + #[at("/not_found")] + NotFound, } -#[function_component(RouteOutlet)] -pub fn route_outlet() -> Html { - html! { - - - render={Switch::render(switch)} /> - - +pub fn switch(routes: Routes) -> Html { + match routes { + Routes::Post { id } => html! {}, + Routes::Posts => { + html! {} + } + Routes::Home => html! { to={Routes::Posts} />}, + Routes::NotFound => html! {}, + Routes::Links => html! {}, + Routes::Dynamic { path } => html! {}, } } diff --git a/app/src/utils.rs b/app/src/utils.rs new file mode 100644 index 0000000..7dfec95 --- /dev/null +++ b/app/src/utils.rs @@ -0,0 +1,3 @@ +pub mod inner_html; +pub mod site_config; +pub mod theme; diff --git a/app/src/utils/inner_html.rs b/app/src/utils/inner_html.rs new file mode 100644 index 0000000..4b6430c --- /dev/null +++ b/app/src/utils/inner_html.rs @@ -0,0 +1,6 @@ +use yew::virtual_dom::VNode; +use yew::Html; + +pub fn parse_str_to_element(content: &str) -> Html { + VNode::from_html_unchecked(content.to_string().into()) +} diff --git a/app/src/utils/site_config.rs b/app/src/utils/site_config.rs new file mode 100644 index 0000000..ae8941c --- /dev/null +++ b/app/src/utils/site_config.rs @@ -0,0 +1,14 @@ +use shared::site_config::Config; + +#[cfg(target_arch = "wasm32")] +pub fn get_config() -> Config { + let local_storage = web_sys::window().unwrap().local_storage().unwrap().unwrap(); + let stringify_site_config = local_storage.get_item("site_config").unwrap().unwrap(); + + serde_json::from_str::(&stringify_site_config).unwrap() +} + +#[cfg(not(target_arch = "wasm32"))] +pub fn get_config() -> Config { + site_config::get_site_config() +} diff --git a/app/src/utils/theme.rs b/app/src/utils/theme.rs new file mode 100644 index 0000000..08ad777 --- /dev/null +++ b/app/src/utils/theme.rs @@ -0,0 +1,39 @@ +use std::{ffi::OsStr, path::Path}; + +#[derive(Clone)] +pub struct ThemeIcon { + pub dark: String, + pub light: String, +} + +// A theme icon format looks like: +// filename.[light|dark].extension +// raw_icon_path is part of theme icon path which without theme suffix: +// filename.extension +// this function will parse raw_icon_path to theme_icon_path: +// { +// dark: "filename.dark.extension", +// light: "filename.light.extension +// } +pub fn parse_theme_icon(raw_icon_path: &str) -> ThemeIcon { + let raw_path = Path::new(raw_icon_path); + let file_path_without_extension = raw_path.with_extension(""); + let file_stem_with_path = file_path_without_extension.to_str().unwrap(); + let extension = raw_path.extension().and_then(OsStr::to_str).unwrap(); + let create_theme_icon = + |theme: &'static str| format!("{file_stem_with_path}.{theme}.{extension}"); + + ThemeIcon { + light: create_theme_icon("light"), + dark: create_theme_icon("dark"), + } +} + +// In some scenarios, we need to append a class to the existing class in components, +// this util function help concat the class prop to existing class +pub fn with_class_prop(raw_class: &str, class_prop: &Option) -> String { + match class_prop { + Some(class_prop_value) => format!("{raw_class} {class_prop_value}"), + None => raw_class.to_string(), + } +} diff --git a/doc/zzhack_banner.png b/doc/zzhack_banner.png deleted file mode 100644 index 5c9148d..0000000 Binary files a/doc/zzhack_banner.png and /dev/null differ diff --git a/doc/zzhack_dark_mode.png b/doc/zzhack_dark_mode.png deleted file mode 100644 index 521e9af..0000000 Binary files a/doc/zzhack_dark_mode.png and /dev/null differ diff --git a/doc/zzhack_light_mode.png b/doc/zzhack_light_mode.png deleted file mode 100644 index 05dceea..0000000 Binary files a/doc/zzhack_light_mode.png and /dev/null differ diff --git a/doc/zzhack_links.png b/doc/zzhack_links.png deleted file mode 100644 index dc22759..0000000 Binary files a/doc/zzhack_links.png and /dev/null differ diff --git a/doc/zzhack_sketch.png b/doc/zzhack_sketch.png deleted file mode 100644 index 15c5042..0000000 Binary files a/doc/zzhack_sketch.png and /dev/null differ diff --git a/dynamic_pages/about/me.md b/dynamic_pages/about/me.md new file mode 100644 index 0000000..9e9938d --- /dev/null +++ b/dynamic_pages/about/me.md @@ -0,0 +1 @@ +# Hello! diff --git a/entry/Cargo.toml b/entry/Cargo.toml new file mode 100644 index 0000000..89745e2 --- /dev/null +++ b/entry/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "entry" +version = "0.0.1" +edition = "2021" + +[[bin]] +name = "zzhack_hydrate_bundle" +required-features = ["hydration"] + +[[bin]] +name = "zzhack_main" +required-features = ["ssr"] + +[dependencies] +yew = { version = "0.21.0", features = ["hydration", "ssr"]} +log = "0.4" +futures = { version = "0.3", features = ["std"], default-features = false } +app = { path = "../app", features = ["ssr", "hydration"] } +site_config = { path = "../site_config" } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen-futures = "0.4" +wasm-logger = "0.2" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +tokio = { version = "1.33.0", features = ["full"] } +axum = "0.6" +tower = { version = "0.4", features = ["make"] } +tower-http = { version = "0.3", features = ["fs"] } +env_logger = "0.10" +clap = { version = "4", features = ["derive"] } +hyper = { version = "0.14", features = ["server", "http1"] } +api = { path = "../api"} + +[target.'cfg(unix)'.dependencies] +jemallocator = "0.5" + +[features] +hydration = ["yew/hydration"] +ssr = ["yew/ssr"] diff --git a/entry/index.html b/entry/index.html new file mode 100644 index 0000000..1dfeb3e --- /dev/null +++ b/entry/index.html @@ -0,0 +1,14 @@ + + + + + + + + + + + + zzhack + + diff --git a/entry/public/logo.png b/entry/public/logo.png new file mode 100644 index 0000000..fc59948 Binary files /dev/null and b/entry/public/logo.png differ diff --git a/entry/src/bin/zzhack_hydrate_bundle.rs b/entry/src/bin/zzhack_hydrate_bundle.rs new file mode 100644 index 0000000..cff7514 --- /dev/null +++ b/entry/src/bin/zzhack_hydrate_bundle.rs @@ -0,0 +1,10 @@ +use app::portal::{BrowserApp, BrowserAppProps}; + +fn main() { + // let config = site_config::get_site_config(); + + #[cfg(target_arch = "wasm32")] + wasm_logger::init(wasm_logger::Config::new(log::Level::Trace)); + + yew::Renderer::::new().hydrate(); +} diff --git a/entry/src/bin/zzhack_main.rs b/entry/src/bin/zzhack_main.rs new file mode 100644 index 0000000..bc8d10b --- /dev/null +++ b/entry/src/bin/zzhack_main.rs @@ -0,0 +1,146 @@ +use std::collections::HashMap; +use std::convert::Infallible; +use std::future::Future; +use std::path::PathBuf; + +use api::database::{connection::get_db_connection, initialize::initialize}; +use api::get_api_routes; +use app::portal::{ServerApp, ServerAppProps}; +use axum::body::StreamBody; +use axum::error_handling::HandleError; +use axum::extract::{Query, State}; +use axum::handler::HandlerWithoutStateExt; +use axum::http::{StatusCode, Uri}; +use axum::response::IntoResponse; +use axum::routing::get; +use axum::Router; +use clap::Parser; +use futures::stream::{self, StreamExt}; +use hyper::server::Server; +use tower::ServiceExt; +use tower_http::services::ServeDir; + +use yew::platform::Runtime; + +// We use jemalloc as it produces better performance. +#[cfg(unix)] +#[global_allocator] +static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc; + +/// A basic example +#[derive(Parser, Debug)] +struct Opt { + /// the "dist" created by trunk directory to be served for hydration. + #[clap(short, long)] + dir: PathBuf, +} + +async fn render( + url: Uri, + Query(queries): Query>, + State((index_html_before, index_html_after)): State<(String, String)>, +) -> impl IntoResponse { + let url = url.to_string(); + let renderer = yew::ServerRenderer::::with_props(move || ServerAppProps { + url: url.into(), + queries, + }); + + StreamBody::new( + stream::once(async move { index_html_before }) + .chain(renderer.render_stream()) + .chain(stream::once(async move { index_html_after })) + .map(Result::<_, Infallible>::Ok), + ) +} + +// An executor to process requests on the Yew runtime. +// +// By spawning requests on the Yew runtime, +// it processes request on the same thread as the rendering task. +// +// This increases performance in some environments (e.g.: in VM). +#[derive(Clone, Default)] +struct Executor { + inner: Runtime, +} + +impl hyper::rt::Executor for Executor +where + F: Future + Send + 'static, +{ + fn execute(&self, fut: F) { + self.inner.spawn_pinned(move || async move { + fut.await; + }); + } +} + +// Map posts to database and initialize tables +pub fn initialize_data() { + // initialize_tables().unwrap(); +} + +#[cfg(debug_assertions)] +fn get_port() -> usize { + site_config::get_site_config().server.dev_port +} + +#[cfg(not(debug_assertions))] +fn get_port() -> usize { + site_config::get_site_config().server.prod_port +} + +#[tokio::main] +async fn main() { + let exec = Executor::default(); + + env_logger::init(); + + let opts = Opt::parse(); + + let index_html_s = tokio::fs::read_to_string(opts.dir.join("index.html")) + .await + .expect("failed to read index.html"); + + let (index_html_before, index_html_after) = index_html_s.split_once("").unwrap(); + let mut index_html_before = index_html_before.to_owned(); + index_html_before.push_str(""); + + let index_html_after = index_html_after.to_owned(); + + let handle_error = |e| async move { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("error occurred: {e}"), + ) + }; + + let conn = get_db_connection().await; + + initialize(&conn).await.unwrap(); + + let app = Router::new() + .nest("/api", get_api_routes()) + .with_state(api::AppState { conn }) + .fallback_service(HandleError::new( + ServeDir::new(opts.dir) + .append_index_html_on_directories(false) + .fallback( + get(render) + .with_state((index_html_before.clone(), index_html_after.clone())) + .into_service() + .map_err(|err| -> std::io::Error { match err {} }), + ), + handle_error, + )); + + let port = get_port(); + println!("Listening on http://localhost:{port}/"); + + Server::bind(&format!("127.0.0.1:{port}").parse().unwrap()) + .executor(exec) + .serve(app.into_make_service()) + .await + .unwrap(); +} diff --git a/entry/src/lib.rs b/entry/src/lib.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/entry/src/lib.rs @@ -0,0 +1 @@ + diff --git a/entry/styles/input.css b/entry/styles/input.css new file mode 100644 index 0000000..7fd190b --- /dev/null +++ b/entry/styles/input.css @@ -0,0 +1,31 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + body, + html { + height: 100%; + } +} + +@layer components { + .pixel-badge { + box-shadow: + 1px 0 red, + 2px 0 green, + 3px 0 blue; + + position: relative; + } + + .pixel-badge::before { + content: ""; + width: 100%; + height: 5px; + background: red; + position: absolute; + top: 0; + left: 0; + } +} diff --git a/global/.gitignore b/global/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/global/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/global/Cargo.toml b/global/Cargo.toml deleted file mode 100644 index 3b2eed0..0000000 --- a/global/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "global" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -yew = "0.19.3" -services = {path = "../services"} -wasm-logger = "0.2.0" -log = "0.4.17" diff --git a/global/src/lib.rs b/global/src/lib.rs deleted file mode 100644 index 47ae522..0000000 --- a/global/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod theme_context; diff --git a/global/src/theme_context.rs b/global/src/theme_context.rs deleted file mode 100644 index e2122ac..0000000 --- a/global/src/theme_context.rs +++ /dev/null @@ -1,54 +0,0 @@ -use services::theme_service::Theme; -use services::theme_service::ThemeService; -use std::rc::Rc; -use yew::prelude::*; - -pub enum ThemeAction { - UpdateTheme(Theme), -} - -#[derive(Debug, PartialEq, Clone)] -pub struct ThemeState { - pub theme: Theme, -} - -impl Reducible for ThemeState { - type Action = ThemeAction; - - fn reduce(self: Rc, action: Self::Action) -> Rc { - match action { - ThemeAction::UpdateTheme(theme) => { - ThemeService::from_storage().set_theme(&theme); - - // If the theme is auto, convert auto to actually theme before dispatch theme in components tree - Rc::from(ThemeState { - theme: ThemeService::convert_auto_to_actually_theme(theme), - }) - } - } - } -} - -pub type ThemeContext = UseReducerHandle; - -#[derive(Properties, Debug, PartialEq)] -pub struct ThemeProviderProps { - pub children: Children, -} - -#[function_component(ThemeProvider)] -pub fn theme_provider(props: &ThemeProviderProps) -> Html { - let theme = use_reducer_eq(|| { - let theme = ThemeService::from_storage().get_theme().clone(); - - ThemeState { - theme: ThemeService::convert_auto_to_actually_theme(theme), - } - }); - - html! { - context={theme}> - { props.children.clone() } - > - } -} diff --git a/links.toml b/links.toml new file mode 100644 index 0000000..009a61c --- /dev/null +++ b/links.toml @@ -0,0 +1,5 @@ +[[links]] +name = "hello" +description = "world" +avatar = "/public/logo.png" +url = "https://github.com/mistricky" diff --git a/pages/not_found/Cargo.toml b/markdown/Cargo.toml similarity index 62% rename from pages/not_found/Cargo.toml rename to markdown/Cargo.toml index db4c3a5..d692235 100644 --- a/pages/not_found/Cargo.toml +++ b/markdown/Cargo.toml @@ -1,10 +1,9 @@ [package] -name = "not_found" +name = "markdown" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -yew = "0.19.3" -stylist = {version = "0.10", features = ["yew_integration"]} +pulldown-cmark = "0.11.0" diff --git a/markdown/src/lib.rs b/markdown/src/lib.rs new file mode 100644 index 0000000..ea86848 --- /dev/null +++ b/markdown/src/lib.rs @@ -0,0 +1 @@ +pub mod parse; diff --git a/markdown/src/parse.rs b/markdown/src/parse.rs new file mode 100644 index 0000000..5de5b83 --- /dev/null +++ b/markdown/src/parse.rs @@ -0,0 +1,16 @@ +use pulldown_cmark::{html, Parser}; + +// Parse markdown to html string +// features: +// - Render JSX component +pub fn parse_markdown(markdown_content: &str) -> String { + let parser = Parser::new(markdown_content).map(|event| match event { + _ => event, + }); + + let mut html_output = String::new(); + + html::push_html(&mut html_output, parser); + + html_output +} diff --git a/migration/Cargo.toml b/migration/Cargo.toml new file mode 100644 index 0000000..dda708c --- /dev/null +++ b/migration/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "migration" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +name = "migration" +path = "src/lib.rs" + +[dependencies] +async-std = { version = "1", features = ["attributes", "tokio1"] } + +[dependencies.sea-orm-migration] +version = "0.12.0" +features = [ + # Enable at least one `ASYNC_RUNTIME` and `DATABASE_DRIVER` feature if you want to run migration via CLI. + # View the list of supported features at https://www.sea-ql.org/SeaORM/docs/install-and-config/database-and-async-runtime. + # e.g. + "runtime-tokio-rustls", # `ASYNC_RUNTIME` feature + "sqlx-sqlite", # `DATABASE_DRIVER` feature +] diff --git a/migration/README.md b/migration/README.md new file mode 100644 index 0000000..3b438d8 --- /dev/null +++ b/migration/README.md @@ -0,0 +1,41 @@ +# Running Migrator CLI + +- Generate a new migration file + ```sh + cargo run -- generate MIGRATION_NAME + ``` +- Apply all pending migrations + ```sh + cargo run + ``` + ```sh + cargo run -- up + ``` +- Apply first 10 pending migrations + ```sh + cargo run -- up -n 10 + ``` +- Rollback last applied migrations + ```sh + cargo run -- down + ``` +- Rollback last 10 applied migrations + ```sh + cargo run -- down -n 10 + ``` +- Drop all tables from the database, then reapply all migrations + ```sh + cargo run -- fresh + ``` +- Rollback all applied migrations, then reapply all migrations + ```sh + cargo run -- refresh + ``` +- Rollback all applied migrations + ```sh + cargo run -- reset + ``` +- Check the status of all migrations + ```sh + cargo run -- status + ``` diff --git a/migration/src/lib.rs b/migration/src/lib.rs new file mode 100644 index 0000000..b54a305 --- /dev/null +++ b/migration/src/lib.rs @@ -0,0 +1,18 @@ +pub use sea_orm_migration::prelude::*; + +mod m20220101_000001_create_tags; +mod m20240718_144412_create_posts; +mod m20240718_150817_create_posts_tags; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + vec![ + Box::new(m20220101_000001_create_tags::Migration), + Box::new(m20240718_144412_create_posts::Migration), + Box::new(m20240718_150817_create_posts_tags::Migration), + ] + } +} diff --git a/migration/src/m20220101_000001_create_tags.rs b/migration/src/m20220101_000001_create_tags.rs new file mode 100644 index 0000000..02379b4 --- /dev/null +++ b/migration/src/m20220101_000001_create_tags.rs @@ -0,0 +1,41 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Tags::Table) + .if_not_exists() + .col( + ColumnDef::new(Tags::Id) + .big_integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(Tags::Text).string().unique_key().not_null()) + .col(ColumnDef::new(Tags::Color).string()) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Tags::Table).to_owned()) + .await + } +} + +#[derive(DeriveIden)] +enum Tags { + Table, + Id, + Text, + Color, +} diff --git a/migration/src/m20240718_144412_create_posts.rs b/migration/src/m20240718_144412_create_posts.rs new file mode 100644 index 0000000..7c03f25 --- /dev/null +++ b/migration/src/m20240718_144412_create_posts.rs @@ -0,0 +1,49 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Posts::Table) + .if_not_exists() + .col( + ColumnDef::new(Posts::Id) + .big_integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(Posts::Title).string().not_null()) + .col(ColumnDef::new(Posts::Path).unique_key().string().not_null()) + .col(ColumnDef::new(Posts::Spoiler).string()) + .col(ColumnDef::new(Posts::Content).string().not_null()) + .col(ColumnDef::new(Posts::CreatedAt).string().not_null()) + .col(ColumnDef::new(Posts::UpdatedAt).string().not_null()) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Posts::Table).to_owned()) + .await + } +} + +#[derive(DeriveIden)] +enum Posts { + Table, + Id, + Path, + Title, + Spoiler, + CreatedAt, + UpdatedAt, + Content, +} diff --git a/migration/src/m20240718_150817_create_posts_tags.rs b/migration/src/m20240718_150817_create_posts_tags.rs new file mode 100644 index 0000000..cddc5a4 --- /dev/null +++ b/migration/src/m20240718_150817_create_posts_tags.rs @@ -0,0 +1,67 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(PostTags::Table) + .if_not_exists() + .col(ColumnDef::new(PostTags::PostId).integer().not_null()) + .col(ColumnDef::new(PostTags::TagId).integer().not_null()) + .primary_key( + Index::create() + .name("pk-posts_tags") + .col(PostTags::PostId) + .col(PostTags::TagId), + ) + .foreign_key( + ForeignKey::create() + .name("fk-posts_tags-posts_id") + .from(PostTags::Table, PostTags::PostId) + .to(Posts::Table, Posts::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .name("fk-posts_tags-tags_id") + .from(PostTags::Table, PostTags::TagId) + .to(Tags::Table, Tags::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(PostTags::Table).to_owned()) + .await + } +} + +#[derive(DeriveIden)] +enum PostTags { + Table, + PostId, + TagId, +} + +#[derive(DeriveIden)] +enum Posts { + Table, + Id, +} + +#[derive(DeriveIden)] +enum Tags { + Table, + Id, +} diff --git a/migration/src/main.rs b/migration/src/main.rs new file mode 100644 index 0000000..c6b6e48 --- /dev/null +++ b/migration/src/main.rs @@ -0,0 +1,6 @@ +use sea_orm_migration::prelude::*; + +#[async_std::main] +async fn main() { + cli::run_cli(migration::Migrator).await; +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..6bc036c --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "zzhack", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "tailwindcss:start": "tailwindcss -i ./entry/styles/input.css -o ./entry/styles/output.css -- --watch" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@tailwindcss/typography": "^0.5.13", + "tailwindcss": "^3.4.4" + } +} diff --git a/pages/about/.gitignore b/pages/about/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/pages/about/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/pages/about/Cargo.toml b/pages/about/Cargo.toml deleted file mode 100644 index ec26460..0000000 --- a/pages/about/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "about" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -yew = "0.19.3" -ui = {path = "../../ui"} -utils = {path = "../../utils"} -stylist = {version = "0.10", features = ["yew_integration"]} -services = {path = "../../services"} - diff --git a/pages/about/src/lib.rs b/pages/about/src/lib.rs deleted file mode 100644 index 20ae024..0000000 --- a/pages/about/src/lib.rs +++ /dev/null @@ -1,62 +0,0 @@ -use stylist::style; -use ui::contact::ContactType; -use ui::gradient_title::GradientTitle; -use ui::link::Link; -use yew::prelude::*; - -#[function_component(About)] -pub fn about() -> Html { - let style = style!( - r" - padding: 50px 0; - - a { - color: var(--blue); - font-size: 16px; - } - - .illustrate { - margin: 20px 0; - border-radius: 20px; - } - - @media (max-width: 600px) { - .illustrate { - width: 100%; - } - } - " - ) - .unwrap(); - - html! { -
-
- {"zzhack 的诞生"} -
-

- {"嗨!欢迎来到我的应用 zzhack 😎,这是一个兴趣使然的项目,zzhack 被设计为一个注重信息展示的应用,它是序列化和沉淀我思想的地方。"} -

-

- {"如你所见的 zzhack 已是第五个大版本,它已经经过了两次大规模的重构以及 5 次重新设计,最后回归纯真,专注信息展示。"} -

-
- -

- {"这么看下来 zzhack 的确没有什么让人惊讶的亮点,没有额外的用户交互,没有炫酷的交互动画,看上去只是一个平静的展示内容的 web 应用,但是它的确适合作为一个单纯的内容输出的站点,而不被逐渐社交化。"} -

-

- {"zzhack 是一个纯静态的应用,并且开源内容到代码的所有,如果你对它的技术实现感兴趣可以在 "} - {"这里"} - {" 找到它。"} -

-
-
- {"关于我"} -

- {"我叫 Mist,一名全栈工程师。"} -

-
-
- } -} diff --git a/pages/home/.gitignore b/pages/home/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/pages/home/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/pages/home/Cargo.toml b/pages/home/Cargo.toml deleted file mode 100644 index 5862290..0000000 --- a/pages/home/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "home" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -yew = "0.19.3" -ui = {path = "../../ui"} -utils = {path = "../../utils"} -stylist = {version = "0.10", features = ["yew_integration"]} -services = {path = "../../services"} diff --git a/pages/home/src/lib.rs b/pages/home/src/lib.rs deleted file mode 100644 index c397ebe..0000000 --- a/pages/home/src/lib.rs +++ /dev/null @@ -1,100 +0,0 @@ -use services::post_service::post_service::{FilterTag, POST_SERVICE}; -use ui::image::ThemeImage; -use ui::label::Label; -use ui::link::Link; -use ui::post_card::post_card::PostCard; -use utils::use_style; -use yew::prelude::*; - -#[function_component(Home)] -pub fn home() -> Html { - let style = use_style!( - r" - .banner { - width: 100%; - display: flex; - justify-content: center; - margin-top: 40px; - } - - .banner > img { - width: 100%; - } - - .labels { - width: 100%; - display: flex; - flex-wrap: wrap; - margin-top: 33px; - } - - .label { - margin-left: 18px; - } - - .posts { - margin-bottom: 45px; - margin-top: 39px; - display: flex; - flex-wrap: wrap; - margin: 30px -18px 45px -18px; - } - - @media (max-width: 600px) { - .banner { - width: 100%; - margin-top: 32px; - } - - .posts { - display: flex; - flex-direction: column; - align-items: center; - margin: 39px 0 45px 0; - } - } - " - ); - let posts = use_state_eq(|| POST_SERVICE.get_posts()); - let handle_filter_posts_by_label = { - let posts = posts.clone(); - - |tag: FilterTag| { - Callback::from(move |_| { - posts.set(POST_SERVICE.filter_post_by_tag(tag.clone())); - }) - } - }; - let handle_filter_posts_by_rest_label = handle_filter_posts_by_label.clone(); - - html! { -
- -
- -
-
- { - posts.iter().map(|post| { - html! { - - } - }).collect::() - } -
-
- } -} diff --git a/pages/links/Cargo.toml b/pages/links/Cargo.toml deleted file mode 100644 index 2840451..0000000 --- a/pages/links/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "links" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -yew = "0.19.3" -ui = {path = "../../ui"} -utils = {path = "../../utils"} -stylist = {version = "0.10", features = ["yew_integration"]} -services = {path = "../../services"} -router = {path = "../../router"} diff --git a/pages/links/src/lib.rs b/pages/links/src/lib.rs deleted file mode 100644 index 6f0a81c..0000000 --- a/pages/links/src/lib.rs +++ /dev/null @@ -1,111 +0,0 @@ -use router::RootRoutes; -use services::links_service::links_service::LINKS_SERVICE; -use stylist::style; -use ui::image::BaseImage; -use ui::link::Link; -use ui::link_card::LinkCard; -use yew::prelude::*; - -#[function_component(Links)] -pub fn links() -> Html { - let style = style!( - r" - .banner { - position: relative; - margin-top: 63px; - } - - .banner > img { - width: 100%; - height: 165.59px; - } - - .banner__links { - position: absolute; - top: 45px; - left: 35px; - } - - .banner__links-title { - color: #fff; - font-size: 18px; - } - - .banner__links-desc { - color: rgba(255, 255, 255, 0.81); - font-size: 14px; - margin-top: 10px; - width: 500px; - line-height: 12px; - } - - .banner__links-desc > a { - color: var(--blue); - font-size: 14px; - } - - .links { - margin: 0 -15px; - display: flex; - flex-wrap: wrap; - margin-top: 50px; - } - - @media (max-width: 600px) { - .banner__links { - top: auto; - left: auto; - bottom: 30px; - width: 100%; - word-break: break-all; - padding: 0 20px; - box-sizing: border-box; - } - - .banner__links-desc { - width: 100%; - line-height: 20px; - } - - .banner > img { - height: 323.69px; - } - - .links { - width: 100%; - margin: auto; - margin-top: 20px; - margin-bottom: 100px; - } - - .links a { - width: 100%; - } - } - " - ) - .unwrap(); - - html! { -
- - -
- } -} diff --git a/pages/not_found/.gitignore b/pages/not_found/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/pages/not_found/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/pages/not_found/src/lib.rs b/pages/not_found/src/lib.rs deleted file mode 100644 index b7201e3..0000000 --- a/pages/not_found/src/lib.rs +++ /dev/null @@ -1,33 +0,0 @@ -use stylist::style; -use yew::prelude::*; - -#[function_component(NotFound)] -pub fn not_found() -> Html { - let style = style!( - r" - padding-top: 100px; - display: flex; - flex-direction: column; - align-items: center; - - & > img { - width: 400px; - } - - @media (max-width: 600px) { - padding-top: 50px; - - & > img { - width: 100%; - } - } - " - ) - .unwrap(); - - html! { -
- -
- } -} diff --git a/pages/post/Cargo.toml b/pages/post/Cargo.toml deleted file mode 100644 index 820f3a2..0000000 --- a/pages/post/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "post" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -yew = "0.19.3" -stylist = {version = "0.10", features = ["yew_integration"]} -ui = {path = "../../ui"} -services = {path = "../../services"} -utils = {path = "../../utils"} -web-sys = { version = "0.3", features = ["HtmlMetaElement", "Document", "Element", "DocumentFragment", "HtmlTemplateElement", "MediaQueryList"] } -gloo-timers = "0.2.4" diff --git a/pages/post/src/lib.rs b/pages/post/src/lib.rs deleted file mode 100644 index d0c0e03..0000000 --- a/pages/post/src/lib.rs +++ /dev/null @@ -1,105 +0,0 @@ -use gloo_timers::callback::Timeout; -use services::markdown_service::markdown_service::MarkdownService; -use services::post_service::post_service::POST_SERVICE; -use stylist::style; -use ui::post_card_header::PostCardHeader; -use yew::prelude::*; - -#[derive(Properties, Clone, PartialEq)] -pub struct PostProps { - pub filename: String, -} - -#[function_component(Post)] -pub fn post(props: &PostProps) -> Html { - let post = POST_SERVICE.find_post_by_filename(&props.filename).unwrap(); - let style = style!( - r#" - width: 660px; - height: 100%; - padding: 63px 0; - margin: auto; - - .post-header { - width: 180px; - } - - .modified-at { - color: var(--tip-color); - margin-top: 13px; - font-size: 13px; - } - - .title { - font-size: 25px; - color: var(--text-color); - } - - .cover { - width: 660px; - height: 258px; - background-image: url("${cover}"); - background-repeat: no-repeat; - background-size: cover; - background-position: 50% 50%; - border-radius: 5px; - margin: 30px 0; - transition: all 0.2s ease-in; - } - - @media (max-width: 600px) { - width: 100%; - - .cover { - width: 100%; - height: 180px; - } - } - "#, - cover = post.metadata.cover.clone() - ) - .unwrap(); - let post_body = MarkdownService::new(post.raw_content.clone().to_string()); - let post_body = post_body.parse_to_element("base16-ocean.dark"); - - use_effect(move || { - let timeout = Timeout::new(500, move || { - position_heading_by_anchor(); - }); - - || { - timeout.forget(); - } - }); - - html! { -
-
- -
-
{&post.modified_time}
-
-

- {&post.metadata.title} -

-
- {Html::VRef(post_body.clone().into())} -
-
- } -} - -fn position_heading_by_anchor() { - let window = web_sys::window().unwrap(); - let document = window.document().unwrap(); - let location = document.location().unwrap(); - let hash = location.hash().unwrap(); - - if hash != "" { - return; - } - - let heading_ele = document.get_element_by_id(&hash[1..]).unwrap(); - - heading_ele.scroll_into_view(); -} diff --git a/pages/projects/.gitignore b/pages/projects/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/pages/projects/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/pages/projects/Cargo.toml b/pages/projects/Cargo.toml deleted file mode 100644 index e14ab38..0000000 --- a/pages/projects/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "projects" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -yew = "0.19.3" -stylist = {version = "0.10", features = ["yew_integration"]} -ui = {path = "../../ui"} -services = {path = "../../services"} -utils = {path = "../../utils"} diff --git a/pages/projects/src/lib.rs b/pages/projects/src/lib.rs deleted file mode 100644 index 09fa72e..0000000 --- a/pages/projects/src/lib.rs +++ /dev/null @@ -1,111 +0,0 @@ -use services::projects_service::projects_service::Project; -use services::projects_service::projects_service::PROJECTS_SERVICE; -use stylist::style; -use ui::contact::ContactType; -use ui::image::ThemeImage; -use ui::link::Link; -use ui::ProjectCard; -use utils::theme::is_on_mobile; -use yew::prelude::*; - -#[function_component(Projects)] -pub fn projects() -> Html { - let style = style!( - r" - margin-top: 30px; - - & > a > img { - width: 100%; - } - - .alert{ - width: 100%; - background-color: var(--alert-color); - padding: 14px 19px; - box-sizing: border-box; - border-radius: 10px; - margin: 15px 0; - } - - .alert > p { - color: var(--text-color); - line-height: 20px; - font-size: 13px; - } - - .alert > p > a { - line-height: 20px; - font-size: 13px; - color: var(--blue); - } - - .cards { - display: flex; - justify-content: space-between; - margin-top: 40px; - margin-bottom: 45px; - } - - @media (max-width: 600px) { - .cards { - flex-direction: column; - } - } - " - ) - .unwrap(); - let render_project_card = |projects: Vec| -> Html { - projects - .into_iter() - .map(|project| { - html! { - - } - }) - .collect::() - }; - let render_waterfall_flow = || -> Html { - let (odd, even) = PROJECTS_SERVICE.get_projects_by_odd_even(); - - html! { - <> -
- {render_project_card(odd)} -
-
- {render_project_card(even)} -
- - } - }; - let render_linear_flow = || -> Html { - let projects = PROJECTS_SERVICE.get_projects(); - - render_project_card(projects) - }; - - let render_target_vnode = if is_on_mobile() { - render_linear_flow() - } else { - render_waterfall_flow() - }; - - html! { -
- - - -
-

{"我会用业余时间维护一些开源项目,包括不限于奇思妙想的产品,提升开发者体验的工具,库,框架。我目前在思考于 UI Design 和想要用 ❤️ 做好一个产品。"}

-

- {"如果你有任何相关的建议或者有趣问题的讨论,欢迎直接通过 "} - {"邮件"} - {" 联系我。"} -

-
-
- {render_target_vnode} -
-
- } -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..5e69df8 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,883 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@tailwindcss/typography': + specifier: ^0.5.13 + version: 0.5.13(tailwindcss@3.4.4) + tailwindcss: + specifier: ^3.4.4 + version: 3.4.4 + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@tailwindcss/typography@0.5.13': + resolution: {integrity: sha512-ADGcJ8dX21dVVHIwTRgzrcunY6YY9uSlAHHGVKvkA+vLc5qLwEszvKts40lx7z0qc4clpjclwLeK5rVCV2P/uw==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + foreground-child@3.2.1: + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} + engines: {node: '>=14'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.14.0: + resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.2: + resolution: {integrity: sha512-qH3nOSj8q/8+Eg8LUPOq3C+6HWkpUioIjDsq1+D4zY91oZvpPttw8GwtF1nReRYKXl+1AORyFqtm2f5Q1SB6/Q==} + engines: {node: 14 >=14.21 || 16 >=16.20 || >=18} + + jiti@1.21.6: + resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + hasBin: true + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + lilconfig@3.1.2: + resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lodash.castarray@4.4.0: + resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.0.1: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-nested@6.0.1: + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + + postcss-selector-parser@6.1.0: + resolution: {integrity: sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} + engines: {node: ^10 || ^12 || >=14} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tailwindcss@3.4.4: + resolution: {integrity: sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==} + engines: {node: '>=14.0.0'} + hasBin: true + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + yaml@2.4.5: + resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} + engines: {node: '>= 14'} + hasBin: true + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@tailwindcss/typography@0.5.13(tailwindcss@3.4.4)': + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: 3.4.4 + + ansi-regex@5.0.1: {} + + ansi-regex@6.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@5.0.2: {} + + balanced-match@1.0.2: {} + + binary-extensions@2.3.0: {} + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + camelcase-css@2.0.1: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@4.1.1: {} + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cssesc@3.0.0: {} + + didyoumean@1.2.2: {} + + dlv@1.1.3: {} + + eastasianwidth@0.2.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + fast-glob@3.3.2: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.7 + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + foreground-child@3.2.1: + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.2.1 + jackspeak: 3.4.2 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.0 + path-scurry: 1.11.1 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.14.0: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + isexe@2.0.0: {} + + jackspeak@3.4.2: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@1.21.6: {} + + lilconfig@2.1.0: {} + + lilconfig@3.1.2: {} + + lines-and-columns@1.2.4: {} + + lodash.castarray@4.4.0: {} + + lodash.isplainobject@4.0.6: {} + + lodash.merge@4.6.2: {} + + lru-cache@10.4.3: {} + + merge2@1.4.1: {} + + micromatch@4.0.7: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + minipass@7.1.2: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.7: {} + + normalize-path@3.0.0: {} + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + package-json-from-dist@1.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + picocolors@1.0.1: {} + + picomatch@2.3.1: {} + + pify@2.3.0: {} + + pirates@4.0.6: {} + + postcss-import@15.1.0(postcss@8.4.39): + dependencies: + postcss: 8.4.39 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + + postcss-js@4.0.1(postcss@8.4.39): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.39 + + postcss-load-config@4.0.2(postcss@8.4.39): + dependencies: + lilconfig: 3.1.2 + yaml: 2.4.5 + optionalDependencies: + postcss: 8.4.39 + + postcss-nested@6.0.1(postcss@8.4.39): + dependencies: + postcss: 8.4.39 + postcss-selector-parser: 6.1.0 + + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@6.1.0: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.4.39: + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + + queue-microtask@1.2.3: {} + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + resolve@1.22.8: + dependencies: + is-core-module: 2.14.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.0.4: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + signal-exit@4.1.0: {} + + source-map-js@1.2.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.0.1 + + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + commander: 4.1.1 + glob: 10.4.5 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + + supports-preserve-symlinks-flag@1.0.0: {} + + tailwindcss@3.4.4: + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.2 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.6 + lilconfig: 2.1.0 + micromatch: 4.0.7 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.1 + postcss: 8.4.39 + postcss-import: 15.1.0(postcss@8.4.39) + postcss-js: 4.0.1(postcss@8.4.39) + postcss-load-config: 4.0.2(postcss@8.4.39) + postcss-nested: 6.0.1(postcss@8.4.39) + postcss-selector-parser: 6.1.0 + resolve: 1.22.8 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-interface-checker@0.1.13: {} + + util-deprecate@1.0.2: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + yaml@2.4.5: {} diff --git a/posts/add_links.md b/posts/add_links.md deleted file mode 100644 index 42d64a3..0000000 --- a/posts/add_links.md +++ /dev/null @@ -1,61 +0,0 @@ -```metadata -{ - "cover": "../app/assets/sources/links_cover.svg", - "tag": "Help", - "title": "如何申请友情链接" -} -``` - -Hii,欢迎来到我的站点,我很乐意跟你连接,你可以向我发起一个 PR 来进行友情链接的添加,你的网站将会被陈列在 [这里](/links)。 - -跟随以下几个步骤来将你的网站添加到 [友情链接](/links) 吧! - -#### 1. Fork & Update links - -Fork [zzhack](https://github.com/zzhack-stack/zzhack) 到你的 GitHub,修改 `/services/links_service/links.json` 添加你的网站信息。 - -```json -{ - "links": [ - { - "name": "Busyops博客", - "addr": "https://busyops.com/", - "desc": "Hello Moon", - "logo": "https://busyops.com/images/avatar.jpg" - }, - { - "name": "Clay 的技术博客", - "addr": "https://www.techgrow.cn", - "desc": "用进废退 | 艺不压身", - "logo": "https://www.techgrow.cn/img/head.jpg" - }, - { - "name": "Christine的博客", - "desc": "虽然我不够优秀,但我从未放弃过努力。", - "logo": "https://christine-only.github.io/blog/logo.png", - "addr": "https://christine-only.github.io/blog/" - }, - { - "name": "Forever丿顾北博客", - "addr": "https://forevergubei.gitee.io/myblod/", - "desc": "一个追寻大佬脚步的小白", - "logo": "https://forevergubei.gitee.io/myblod/logo.png" - } - // { - // "name": "站点名称", - // "addr": "站点链接", - // "desc": "站点描述", - // "logo": "站点 logo" - // }, - // <- 加到这里 :D - ] -} -``` - -#### 2. 提交修改 - -通过 GitHub 创建一个 PR 合并到 `main`,等待 merge 后就能在 [友情链接](/links) 看到你的站点了。 - -#### Final - -如果你嫌上述步骤太麻烦也不要紧,将你的网站信息通过 [Email](mailto:mist.zzh@gmail.com) 发给我,我会在空闲的时候处理 :D。 diff --git a/posts/build_blog.md b/posts/build_blog.md deleted file mode 100644 index e9165d2..0000000 --- a/posts/build_blog.md +++ /dev/null @@ -1,159 +0,0 @@ -```metadata -{ - "cover": "../app/assets/sources/blog_cover.svg", - "tag": "Genesis", - "title": "构建静态纯粹的博客站点", - "pined": true, - "size": "large" -} -``` - -Hello World! - -重构了好久的 zzhack 终于又又又上线了,这么久的从 UI 到整个架构的重构属实太长了,上线的第一篇创世文章想写清楚 zzhack 的构建和设计思路,有关该站点的建设动机可以移步到 [这里](/about)。 - -正如你看到的 zzhack 是一个纯正的 wasm 应用,它由 [Rust](https://www.rust-lang.org/) & [Yew](https://yew.rs/) 来作为技术栈进行搭建。 - -[Rust](https://www.rust-lang.org/) 一个看似跟前端半毛钱不搭边的语言,为什么会渗透到前端的技术栈来,一切都要得益于 [Web Assembly](https://webassembly.org/)(后称 wasm),那什么是 wasm?在 wasm 官网上有这么一段简要的概括: - -> WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications. - -由此可见,wasm 被设计的初衷就是一个编译目标语言(Wasm is designed as a portable compilation target for programming languages),在此之前,很多语言也有尝试过把 JS 本身就当作一个编译目标,让自家的语言也能渗透进前端的技术栈,做到 full-stack language,比如 [Dart](https://dart.dev/) 的 [dart2js](https://dart.dev/tools/dart2js) 编译器,但 dart 的 webtool 做的并不是很理想,其根本原因在于从开发体验到执行性能来看并没有本质上的提升,这就导致大部分开发者还是倾向于用 JS 来进行开发,不仅性能跟 dart2js 编译出的 bundle 相差无几,在开发效率上得益于动态语言的天然优势,也把静态语言远远甩在身后。但 Dart 自家的 Angular 也天然支持 Dart 进行开发,那是另一回事了(作者觉得体验并不如用 TS 来搭配 Angular)。 - -Wasm 出现的一个根本原因在于前端正在从 “脚本化” 踏入 “工程化”,日益复杂的前端项目不仅需要大部分的维护成本,在一些领域现有的性能已经满足不了,出现了动态语言的瓶颈。动态语言让编译过程无法去猜测变量的类型而去进行热优化,每一个类型的动态变更都会导致编译过程需要做一次去优化。当然期间也出现了诸如 [asm.js](http://asmjs.org/) 这样的编译目标,这里就不过多的进行介绍了。 - -Wasm 让在浏览器里能跑的语言多了一种新的可能性并且带来了极高的性能,在一些需要高强度计算的场景可谓是及时雨,下图为fib 递归运算 js 和 wasm 的运行时间对比。 - -``` -fib(n) = fib(n - 1) + fib(n - 2) -``` - -![Wasm fibonacci](../app/assets/sources/wasm_fib.png) - -单个 fib 的例子可以看出计算性能几乎高了一倍。 - -当然这也给前端技术栈带来了更多的可能性,现在的浏览器不光能跑 JS 也能跑 wasm,这样只要有语言能提供编译到 wasm 目标的编译器,那这个语言就可以渗透进前端技术栈了!是不是光听着就兴奋无比!但事实却不尽人意,虽然 wasm 提供了比 js 更强的运算能力,但它的设计初衷并不是为了替代 js,而是作为一种新型的突破浏览器应用运算瓶颈的解决方案,这样一来,wasm 只能负责与计算有关的操作,换言之 wasm 并不能操作 dom,因此我们需要一个更聪明的打包工具链,将 dom 操作和计算运算逻辑分散开来,将有关 dom 的操作全部交给 js,执行运算的逻辑都交付给 wasm,再由 js 来负责调用 wasm 模块。 - -与此同时,现代前端开发体验除了良好的编码体验外,本地服务器 + 热更新是并不可少的,能提供这样接近于 js 的工具链的语言的生态少之又少,目前 Rust 的生态是最成熟的,出现了诸如 [Trunk](https://github.com/thedodd/trunk) 这类优秀的打包工具(该站点就是基于 [Trunk](https://github.com/thedodd/trunk) 打包的)。 - -## ViewModel? -介绍完了工具链,但还需要一个模板。 - -现代前端开发大多是 SPA 居多,后续的 SSR,SSG 等概念其实已经跟传统(直接利用模板引擎在服务端全部渲染完毕)的 SSR 等概念大不相同了。在交互居多的 web2.0 时代,前端要解决的首要难题是模型到视图的映射关系,希望模型的更新能尽快的更新到视图,完成一次视图的响应。现在很多所谓数据驱动的框架都帮我们做了这些事情,那在 Wasm 生态中,有没有诸如此类的框架呢?答案是有的!这就是 [Yew](https://yew.rs/)。 - -![Yew](../app/assets/sources/yew_logo.png) - -[Yew](https://yew.rs/) 是 Rust wasm 生态中的一环,利用 rust 强大的 macro,提供类似 jsx 的模板来表示 UI,提供媲美 React 的 jsx 的模板体验,虽然现在是 `0.19` 版本离稳定还有一段距离,但整个社区强大活力让我感到这个框架强大的生命力。 - -在 Yew 0.19 版本中支持了 function component,对比 0.18 的 component 写法更加简洁,一个 yew fc 大概是这样的: - -[ui/gradient_title.rs](https://github.com/zzhack-stack/zzhack) - -```rust -use stylist::style; -use yew::prelude::*; - -#[derive(Properties, Clone, PartialEq)] -pub struct GradientTitleProps { - pub children: Children, -} - -#[function_component(GradientTitle)] -pub fn gradient_title(props: &GradientTitleProps) -> Html { - let style = style!( - r" - display: flex; - margin-bottom: 21px; - - .gradient-title__content { - font-size: 29px; - position: relative; - } - - .gradient-title__content::before { - content: ''; - width: 120%; - display: block; - height: 21px; - border-radius: 100px; - background: linear-gradient(90deg, #FF4AA8 0%, #F95D66 22%, #FE9C76 100%); - position: absolute; - z-index: -1; - bottom: 0px; - left: -5%; - } - " - ) - .unwrap(); - - html! { -
-
- { props.children.clone() } -
-
- } -} -``` - -需要注意的是这并不是另一个 React 的轮子,它更像一个具有 jsx 模板的 Rust 框架而已。 - -## 说说博客吧! - -聊了那么多技术栈的东西,接下来转身来聊聊博客。 - -想要比较快速的搭建一个 blog,可能会想到 hexo 之类的工具,这些不在本文讨论的范畴内。但如果你想更高强度的 DIY 来搭建你的 blog site,可以接着看下去。 - -博客首先是一个内容展示平台,意味着应该会有不断更新的数据源,而搭建一个博客站点最首要的工作也应该是思考如何处理发布这些数据源的问题,是动态发布,还是静态发布。 - -### 动态发布 -之前 zzhack V1.0 的数据源就是动态发布,这样做的好处是你可以将数据源和站点天然的隔离开来,不仅拥有更新数据源不用重新编译部署的能力,甚至在做跨端(这里指有多个客户端,比如 Web App,Native App 小程序等)的时候也能很方便的拿到数据源。 - -![动态发布](../app/assets/sources/dynamic_dispatch.png) - -在 zzhack v1.0 中,文章数据是由 GitHub issue 进行管理发布的,Issues 是一个天然的 Markdown 编辑器,并且 Issues 的 Labels 也能很好的帮助管理文章,进行分类归档。同时,GitHub API 提供了一套完整的关于 Issue 的 restful API 这样一来,客户端只需要通过 GitHub API 来获取对应的文章数据源进行渲染即可。 - -![Issues 管理文章](../app/assets/sources/issues_dispatch.png) - -这样看起来是很完美,发布渲染一气呵成,但鉴于国内的网络情况,访问 GitHub 实在太慢,经常超时,所以如果大陆的用户来访问 zzhack v1 会经常卡在一个 loading 状态(新文章)... - -在 v3 的时候将这部分进行了改进,弃掉 GitHub Issues 来管理文章转而直接用一个 Git 仓库来管理,当 git 版本变更时触发 CI hook,将文章和文章里包含的静态资源统统上传到国内的 CDN,然后客户端再直接通过 CDN 获取文章数据,这样一来速度就会快很多了。 - -为此我专门用 py 写了个小 [CLI](https://github.com/zzhack-stack/zzhack-provider) 来进行发布动作(Archive)。 - -![脚本发布](../app/assets/sources/provider_dispatch.png) - - -### 静态发布 -静态发布的优势也显而易见,就是简单方便速度快,不需要从远端拿额外的资源,文章和代码一起打包发布。传统的 JS 生态会很容易做这件事,丰富的打包生态只需要一个插件来帮助解析 md 模块,就能在依赖图里见到它们了 :D。 - -但是如果是 Rust 的 wasm 生态,Trunk 要做这样的事儿并不容易,其根本是因为 Trunk 并没有提供类似 Webpack 插件的机制,直接导致无法介入 Trunk 的编译过程,这部分就必须手动来搞了,于是我又写了个 [CLI](https://github.com/zzhack-stack/reducer)... - -其思路是先在本地写文章,然后把所有文章都聚合起来当作一个 rs 文件载入源码,再在 trunk 热更新的时候触发一下载入的动作,应用可以通过 service 来读取这部分的内容,就可以做到愉快的静态发布文章了。 - -![静态发布](../app/assets/sources/static_dispatch.png) - -一个自动生成的 `posts.rs` 看起来像: - -```rust -use std::*; - -#[derive(Clone)] -pub struct PostFile { - pub content: &'static str, - pub modified_time: u128 -} - -pub static POSTS: [PostFile; 1] = [ - PostFile { - content: include_str!("../../posts/build_blog.md"), - modified_time: 1654596087967 - }, - -]; -``` - -## 聊聊 Rust 的开发体验 -最后最后,聊聊 Rust 开发 Web 的体验,虽然在性能上已经碾压 JS,内存控制更是没话说,但是开发体验并不如直接用 JS Stack 来写 WEB,这需要你通过 Rust 的思维来写 UI 的逻辑。 - -但是 Rust 太精致(安全)了,但是 WEB 通常不需要那么精致。 diff --git a/posts/foo.md b/posts/foo.md new file mode 100644 index 0000000..40fe7fe --- /dev/null +++ b/posts/foo.md @@ -0,0 +1,1240 @@ +--- +title: "Global variables" +spoiler: "The limits of my language mean the limits of my world." +tags: + - text: Hello + color: "#000000" + + - World +--- + +# 21.全局变量 Global Variables + +> If only there could be an invention that bottled up a memory, like scent. And it never faded, and it never got stale. And then, when one wanted it, the bottle could be uncorked, and it would be like living the moment all over again. +> +> ​ —— Daphne du Maurier, *Rebecca* + +如果有一种发明能把一段记忆装进瓶子里就好了,像香味一样。它永远不会褪色,也不会变质。然后,当一个人想要的时候,可以打开瓶塞,就像重新活在那个时刻一样。(达芙妮-杜穆里埃,《蝴蝶梦》) + +> The [previous chapter](http://www.craftinginterpreters.com/hash-tables.html) was a long exploration of one big, deep, fundamental computer science data structure. Heavy on theory and concept. There may have been some discussion of big-O notation and algorithms. This chapter has fewer intellectual pretensions. There are no large ideas to learn. Instead, it’s a handful of straightforward engineering tasks. Once we’ve completed them, our virtual machine will support variables. + +上一章对一个大的、深入的、基本的计算机科学数据结构进行了长时间的探索。偏重理论和概念。可能有一些关于大O符号和算法的讨论。这一章没有那么多知识分子的自吹自擂。没有什么伟大的思想需要学习。相反,它是一些简单的工程任务。一旦我们完成了这些任务,我们的虚拟机就可以支持变量。 + +> Actually, it will support only *global* variables. Locals are coming in the [next chapter](http://www.craftinginterpreters.com/local-variables.html). In jlox, we managed to cram them both into a single chapter because we used the same implementation technique for all variables. We built a chain of environments, one for each scope, all the way up to the top. That was a simple, clean way to learn how to manage state. + +事实上,它将只支持*全局*变量。局部变量将在下一章中支持。在jlox中,我们设法将它们塞进了一个章节,因为我们对所有变量都使用了相同的实现技术。我们建立了一个环境链,每个作用域都有一个,一直到顶部作用域。这是学习如何管理状态的一种简单、干净的方法。 + +> But it’s also *slow*. Allocating a new hash table each time you enter a block or call a function is not the road to a fast VM. Given how much code is concerned with using variables, if variables go slow, everything goes slow. For clox, we’ll improve that by using a much more efficient strategy for local variables, but globals aren’t as easily optimized. + +但它也很慢。每次进入一个代码块或调用一个函数时,都要分配一个新的哈希表,这不是通往快速虚拟机的道路。鉴于很多代码都与使用变量有关,如果变量操作缓慢,一切都会变慢。对于clox,我们会通过对局部变量使用更有效的策略来改善这一点,但全局变量不那么容易优化[^1]。 + +This is a common meta-strategy in sophisticated language implementations. Often, the same language feature will have multiple implementation techniques, each tuned for different use patterns. For example, JavaScript VMs often have a faster representation for objects that are used more like instances of classes compared to other objects whose set of properties is more freely modified. C and C++ compilers usually have a variety of ways to compile `switch` statements based on the number of cases and how densely packed the case values are. + +> A quick refresher on Lox semantics: Global variables in Lox are “late bound”, or resolved dynamically. This means you can compile a chunk of code that refers to a global variable before it’s defined. As long as the code doesn’t *execute* before the definition happens, everything is fine. In practice, that means you can refer to later variables inside the body of functions. + +快速复习一下Lox语义:Lox中的全局变量是“后期绑定”的,或者说是动态解析的。这意味着,你可以在全局变量被定义之前,编译引用它的一大块代码。只要代码在定义发生之前没有执行,就没有问题。在实践中,这意味着你可以在函数的主体中引用后面的变量。 + +```c +fun showVariable() { + print global; +} + +var global = "after"; +showVariable(); +``` + +> Code like this might seem odd, but it’s handy for defining mutually recursive functions. It also plays nicer with the REPL. You can write a little function in one line, then define the variable it uses in the next. + +这样的代码可能看起来很奇怪,但它对于定义相互递归的函数很方便。它与REPL的配合也更好。你可以在一行中编写一个小函数,然后在下一行中定义它使用的变量。 + +> Local variables work differently. Since a local variable’s declaration *always* occurs before it is used, the VM can resolve them at compile time, even in a simple single-pass compiler. That will let us use a smarter representation for locals. But that’s for the next chapter. Right now, let’s just worry about globals. + +局部变量的工作方式不同。因为局部变量的声明总是发生在使用之前,虚拟机可以在编译时解析它们,即使是在简单的单遍编译器中。这让我们可以为局部变量使用更聪明的表示形式。但这是下一章的内容。现在,我们只考虑全局变量。 + +> ## 21 . 1 Statements + +## 21.1 语句 + +> Variables come into being using variable declarations, which means now is also the time to add support for statements to our compiler. If you recall, Lox splits statements into two categories. “Declarations” are those statements that bind a new name to a value. The other kinds of statements—control flow, print, etc.—are just called “statements”. We disallow declarations directly inside control flow statements, like this: + +变量是通过变量声明产生的,这意味着现在是时候向编译器中添加对语句的支持了。如果你还记得的话,Lox将语句分为两类。“声明”是那些将一个新名称与值绑定的语句。其它类型的语句——控制流、打印等——只被称为“语句”。我们不允许在控制流语句中直接使用声明,像这样: + +```c +if (monday) var croissant = "yes"; // Error. +``` + +> Allowing it would raise confusing questions around the scope of the variable. So, like other languages, we prohibit it syntactically by having a separate grammar rule for the subset of statements that *are* allowed inside a control flow body. + +允许这种做法会引发围绕变量作用域的令人困惑的问题。因此,像其它语言一样,对于允许出现在控制流主体内的语句子集,我们制定单独的语法规则,从而禁止这种做法。 + +```c +statement → exprStmt + | forStmt + | ifStmt + | printStmt + | returnStmt + | whileStmt + | block ; +``` + +> Then we use a separate rule for the top level of a script and inside a block. + +然后,我们为脚本的顶层和代码块内部使用单独的规则。 + +```c +declaration → classDecl + | funDecl + | varDecl + | statement ; +``` + +> The `declaration` rule contains the statements that declare names, and also includes `statement` so that all statement types are allowed. Since `block` itself is in `statement`, you can put declarations inside a control flow construct by nesting them inside a block. + +`declaration`包含声明名称的语句,也包含`statement`规则,这样所有的语句类型都是允许的。因为`block`本身就在`statement`中,你可以通过将声明嵌套在代码块中的方式将它们放在控制流结构中[^2]。 + +> In this chapter, we’ll cover only a couple of statements and one declaration. + +在本章中,我们只讨论几个语句和一个声明。 + +```c +statement → exprStmt + | printStmt ; + +declaration → varDecl + | statement ; +``` + +> Up to now, our VM considered a “program” to be a single expression since that’s all we could parse and compile. In a full Lox implementation, a program is a sequence of declarations. We’re ready to support that now. + +到目前为止,我们的虚拟机都认为“程序”是一个表达式,因为我们只能解析和编译一条表达式。在完整的Lox实现中,程序是一连串的声明。我们现在已经准备要支持它了。 + +*compiler.c,在compile()方法中替换2行:* + +```c + advance(); + // 替换部分开始 + while (!match(TOKEN_EOF)) { + declaration(); + } + // 替换部分结束 + endCompiler(); +``` + +> We keep compiling declarations until we hit the end of the source file. We compile a single declaration using this: + +我们会一直编译声明语句,直到到达源文件的结尾。我们用这个方法来编译一条声明语句: + +*compiler.c,在expression()方法后添加代码:* + +```c +static void declaration() { + statement(); +} +``` + +> We’ll get to variable declarations later in the chapter, so for now, we simply forward to `statement()`. + +我们将在本章后面讨论变量声明,所以现在,我们直接使用`statement()`。 + +*compiler.c,在declaration()方法后添加代码:* + +```c +static void statement() { + if (match(TOKEN_PRINT)) { + printStatement(); + } +} +``` + +> Blocks can contain declarations, and control flow statements can contain other statements. That means these two functions will eventually be recursive. We may as well write out the forward declarations now. + +代码块可以包含声明,而控制流语句可以包含其它语句。这意味着这两个函数最终是递归的。我们不妨现在就把前置声明写出来。 + +*compiler.c,在expression()方法后添加代码:* + +```c +static void expression(); +// 新增部分开始 +static void statement(); +static void declaration(); +// 新增部分结束 +static ParseRule* getRule(TokenType type); +``` + +> ### 21 . 1 . 1 Print statements + +### 21.1.1 Print语句 + +> We have two statement types to support in this chapter. Let’s start with `print` statements, which begin, naturally enough, with a `print` token. We detect that using this helper function: + +在本章中,我们有两种语句类型需要支持。我们从`print`语句开始,它自然是以`print`标识开头的。我们使用这个辅助函数来检测: + +*compiler.c,在consume()方法后添加代码:* + +```c +static bool match(TokenType type) { + if (!check(type)) return false; + advance(); + return true; +} +``` + +> You may recognize it from jlox. If the current token has the given type, we consume the token and return `true`. Otherwise we leave the token alone and return `false`. This helper function is implemented in terms of this other helper: + +你可能看出它是从jlox来的。如果当前的标识是指定类型,我们就消耗该标识并返回`true`。否则,我们就不处理该标识并返回`false`。这个辅助函数是通过另一个辅助函数实现的: + +*compiler.c,在consume()方法后添加代码:* + +```c +static bool check(TokenType type) { + return parser.current.type == type; +} +``` + +> The `check()` function returns `true` if the current token has the given type. It seems a little silly to wrap this in a function, but we’ll use it more later, and I think short verb-named functions like this make the parser easier to read. + +如果当前标识符合给定的类型,`check()`函数返回`true`。将它封装在一个函数中似乎有点傻,但我们以后会更多地使用它,而且我们认为像这样简短的动词命名的函数使解析器更容易阅读[^3]。 + +> If we did match the `print` token, then we compile the rest of the statement here: + +如果我们确实匹配到了`print`标识,那么我们在下面这个方法中编译该语句的剩余部分: + +*compiler.c,在expression()方法后添加代码:* + +```c +static void printStatement() { + expression(); + consume(TOKEN_SEMICOLON, "Expect ';' after value."); + emitByte(OP_PRINT); +} +``` + +> A `print` statement evaluates an expression and prints the result, so we first parse and compile that expression. The grammar expects a semicolon after that, so we consume it. Finally, we emit a new instruction to print the result. + +`print`语句会对表达式求值并打印出结果,所以我们首先解析并编译这个表达式。语法要求在表达式之后有一个分号,所以我们消耗一个分号标识。最后,我们生成一条新指令来打印结果。 + +*chunk.h,在枚举OpCode中添加代码:* + +```c + OP_NEGATE, + // 新增部分开始 + OP_PRINT, + // 新增部分结束 + OP_RETURN, +``` + +> At runtime, we execute this instruction like so: + +在运行时,我们这样执行这条指令: + +*vm.c,在run()方法中添加代码:* + +```c + break; + // 新增部分开始 + case OP_PRINT: { + printValue(pop()); + printf("\n"); + break; + } + // 新增部分结束 + case OP_RETURN: { +``` + +> When the interpreter reaches this instruction, it has already executed the code for the expression, leaving the result value on top of the stack. Now we simply pop and print it. + +当解释器到达这条指令时,它已经执行了表达式的代码,将结果值留在了栈顶。现在我们只需要弹出该值并打印。 + +> Note that we don’t push anything else after that. This is a key difference between expressions and statements in the VM. Every bytecode instruction has a **stack effect** that describes how the instruction modifies the stack. For example, `OP_ADD` pops two values and pushes one, leaving the stack one element smaller than before. + +请注意,在此之后我们不会再向栈中压入任何内容。这是虚拟机中表达式和语句之间的一个关键区别。每个字节码指令都有**堆栈效应**,这个值用于描述指令如何修改堆栈内容。例如,`OP_ADD`会弹出两个值并压入一个值,使得栈中比之前少了一个元素[^4]。 + +> You can sum the stack effects of a series of instructions to get their total effect. When you add the stack effects of the series of instructions compiled from any complete expression, it will total one. Each expression leaves one result value on the stack. + +你可以把一系列指令的堆栈效应相加,得到它们的总体效应。如果把从任何一个完整的表达式中编译得到的一系列指令的堆栈效应相加,其总数是1。每个表达式会在栈中留下一个结果值。 + +> The bytecode for an entire statement has a total stack effect of zero. Since a statement produces no values, it ultimately leaves the stack unchanged, though it of course uses the stack while it’s doing its thing. This is important because when we get to control flow and looping, a program might execute a long series of statements. If each statement grew or shrank the stack, it might eventually overflow or underflow. + +整个语句对应字节码的总堆栈效应为0。因为语句不产生任何值,所以它最终会保持堆栈不变,尽管它在执行自己的操作时难免会使用堆栈。这一点很重要,因为等我们涉及到控制流和循环时,一个程序可能会执行一长串的语句。如果每条语句都增加或减少堆栈,最终就可能会溢出或下溢。 + +> While we’re in the interpreter loop, we should delete a bit of code. + +在解释器循环中,我们应该删除一些代码。 + +*vm.c,在run()方法中替换2行:* + +```c + case OP_RETURN: { + // 替换部分开始 + // Exit interpreter. + // 替换部分结束 + return INTERPRET_OK; +``` + +> When the VM only compiled and evaluated a single expression, we had some temporary code in `OP_RETURN` to output the value. Now that we have statements and `print`, we don’t need that anymore. We’re one step closer to the complete implementation of clox. + +当虚拟机只编译和计算一条表达式时,我们在`OP_RETURN`中使用一些临时代码来输出值。现在我们已经有了语句和`print`,就不再需要这些了。我们离clox的完全实现又近了一步[^5]。 + +> As usual, a new instruction needs support in the disassembler. + +像往常一样,一条新指令需要反汇编程序的支持。 + +*debug.c,在disassembleInstruction()方法中添加代码:* + +```c + return simpleInstruction("OP_NEGATE", offset); + // 新增部分开始 + case OP_PRINT: + return simpleInstruction("OP_PRINT", offset); + // 新增部分结束 + case OP_RETURN: +``` + +> That’s our `print` statement. If you want, give it a whirl: + +这就是我们的`print`语句。如果你愿意,可以试一试: + +```c +print 1 + 2; +print 3 * 4; +``` + +> Exciting! OK, maybe not thrilling, but we can build scripts that contain as many statements as we want now, which feels like progress. + +令人兴奋!好吧,也许没有那么激动人心,但是我们现在可以构建包含任意多语句的脚本,这感觉是一种进步。 + +> ### 21 . 1 . 2 Expression statements + +### 21.1.2 表达式语句 + +> Wait until you see the next statement. If we *don’t* see a `print` keyword, then we must be looking at an expression statement. + +等待,直到你看到下一条语句。如果没有看到`print`关键字,那么我们看到的一定是一条表达式语句。 + +*compiler.c,在statement()方法中添加代码:* + +```c + printStatement(); + // 新增部分开始 + } else { + expressionStatement(); + // 新增部分结束 + } +``` + +> It’s parsed like so: + +它是这样解析的: + +*compiler.c,在expression()方法后添加代码:* + +```c +static void expressionStatement() { + expression(); + consume(TOKEN_SEMICOLON, "Expect ';' after expression."); + emitByte(OP_POP); +} +``` + +> An “expression statement” is simply an expression followed by a semicolon. They’re how you write an expression in a context where a statement is expected. Usually, it’s so that you can call a function or evaluate an assignment for its side effect, like this: + +“表达式语句”就是一个表达式后面跟着一个分号。这是在需要语句的上下文中写表达式的方式。通常来说,这样你就可以调用函数或执行赋值操作以触发其副作用,像这样: + +```c +brunch = "quiche"; +eat(brunch); +``` + +> Semantically, an expression statement evaluates the expression and discards the result. The compiler directly encodes that behavior. It compiles the expression, and then emits an `OP_POP` instruction. + +从语义上说,表达式语句会对表达式求值并丢弃结果。编译器直接对这种行为进行编码。它会编译表达式,然后生成一条`OP_POP`指令。 + +*chunk.h,在枚举OpCode中添加代码:* + +```c + OP_FALSE, + // 新增部分开始 + OP_POP, + // 新增部分结束 + OP_EQUAL, +``` + +> As the name implies, that instruction pops the top value off the stack and forgets it. + +顾名思义,该指令会弹出栈顶的值并将其遗弃。 + +*vm.c,在run()方法中添加代码:* + +```c + case OP_FALSE: push(BOOL_VAL(false)); break; + // 新增部分开始 + case OP_POP: pop(); break; + // 新增部分结束 + case OP_EQUAL: { +``` + +> We can disassemble it too. + +我们也可以对它进行反汇编。 + +*debug.c,在disassembleInstruction()方法中添加代码:* + +```c + return simpleInstruction("OP_FALSE", offset); + // 新增部分开始 + case OP_POP: + return simpleInstruction("OP_POP", offset); + // 新增部分结束 + case OP_EQUAL: +``` + +> Expression statements aren’t very useful yet since we can’t create any expressions that have side effects, but they’ll be essential when we [add functions later](http://www.craftinginterpreters.com/calls-and-functions.html). The majority of statements in real-world code in languages like C are expression statements. + +表达式语句现在还不是很有用,因为我们无法创建任何有副作用的表达式,但等我们后面添加函数时,它们将是必不可少的。在像C这样的真正语言中,大部分语句都是表达式语句[^6]。 + +> ### 21 . 1 . 3 Error synchronization + +### 21.1.3 错误同步 + +> While we’re getting this initial work done in the compiler, we can tie off a loose end we left [several chapters back](http://www.craftinginterpreters.com/compiling-expressions.html#handling-syntax-errors). Like jlox, clox uses panic mode error recovery to minimize the number of cascaded compile errors that it reports. The compiler exits panic mode when it reaches a synchronization point. For Lox, we chose statement boundaries as that point. Now that we have statements, we can implement synchronization. + +当我们在编译器中完成这些初始化工作时,我们可以把前几章遗留的一个小尾巴处理一下。与jlox一样,clox也使用了恐慌模式下的错误恢复来减少它所报告的级联编译错误。当编译器到达同步点时,就退出恐慌模式。对于Lox来说,我们选择语句边界作为同步点。现在我们有了语句,就可以实现同步了。 + +*compiler.c,在declaration()方法中添加代码:* + +```c + statement(); + // 新增部分开始 + if (parser.panicMode) synchronize(); + // 新增部分结束 +} +``` + +> If we hit a compile error while parsing the previous statement, we enter panic mode. When that happens, after the statement we start synchronizing. + +如果我们在解析前一条语句时遇到编译错误,我们就会进入恐慌模式。当这种情况发生时,我们会在这条语句之后开始同步。 + +*compiler.c,在printStatement()方法后添加代码:* + +```c +static void synchronize() { + parser.panicMode = false; + + while (parser.current.type != TOKEN_EOF) { + if (parser.previous.type == TOKEN_SEMICOLON) return; + switch (parser.current.type) { + case TOKEN_CLASS: + case TOKEN_FUN: + case TOKEN_VAR: + case TOKEN_FOR: + case TOKEN_IF: + case TOKEN_WHILE: + case TOKEN_PRINT: + case TOKEN_RETURN: + return; + + default: + ; // Do nothing. + } + + advance(); + } +} +``` + +> We skip tokens indiscriminately until we reach something that looks like a statement boundary. We recognize the boundary by looking for a preceding token that can end a statement, like a semicolon. Or we’ll look for a subsequent token that begins a statement, usually one of the control flow or declaration keywords. + +我们会不分青红皂白地跳过标识,直到我们到达一个看起来像是语句边界的位置。我们识别边界的方式包括,查找可以结束一条语句的前驱标识,如分号;或者我们可以查找能够开始一条语句的后续标识,通常是控制流或声明语句的关键字之一。 + +> ## 21 . 2 Variable Declarations + +## 21.2 变量声明 + +> Merely being able to *print* doesn’t win your language any prizes at the programming language fair, so let’s move on to something a little more ambitious and get variables going. There are three operations we need to support: + +仅仅能够*打印*并不能为你的语言在编程语言博览会上赢得任何奖项,所以让我们继续做一些更有野心的事,让变量发挥作用。我们需要支持三种操作: + +- > Declaring a new variable using a `var` statement. + + 使用`var`语句声明一个新变量 + +- > Accessing the value of a variable using an identifier expression. + + 使用标识符表达式访问一个变量的值 + +- > Storing a new value in an existing variable using an assignment expression. + + 使用赋值表达式将一个新的值存储在现有的变量中 + +> We can’t do either of the last two until we have some variables, so we start with declarations. + +等我们有了变量以后,才能做后面两件事,所以我们从声明开始。 + +*compiler.c,在declaration()方法中替换1行:* + +```c +static void declaration() { + // 替换部分开始 + if (match(TOKEN_VAR)) { + varDeclaration(); + } else { + statement(); + } + // 替换部分结束 + if (parser.panicMode) synchronize(); +``` + +> The placeholder parsing function we sketched out for the declaration grammar rule has an actual production now. If we match a `var` token, we jump here: +> + +我们为声明语法规则建立的占位解析函数现在已经有了实际的生成式。如果我们匹配到一个`var`标识,就跳转到这里: + +*compiler.c,在expression()方法后添加代码:* + +```c +static void varDeclaration() { + uint8_t global = parseVariable("Expect variable name."); + + if (match(TOKEN_EQUAL)) { + expression(); + } else { + emitByte(OP_NIL); + } + consume(TOKEN_SEMICOLON, + "Expect ';' after variable declaration."); + + defineVariable(global); +} +``` + +> The keyword is followed by the variable name. That’s compiled by `parseVariable()`, which we’ll get to in a second. Then we look for an `=` followed by an initializer expression. If the user doesn’t initialize the variable, the compiler implicitly initializes it to `nil` by emitting an `OP_NIL` instruction. Either way, we expect the statement to be terminated with a semicolon. + +关键字后面跟着变量名。它是由`parseVariable()`编译的,我们马上就会讲到。然后我们会寻找一个`=`,后跟初始化表达式。如果用户没有初始化变量,编译器会生成`OP_NIL`指令隐式地将其初始化为`nil`[^7]。无论哪种方式,我们都希望语句以分号结束。 + +> There are two new functions here for working with variables and identifiers. Here is the first: + +这里有两个新函数用于处理变量和标识符。下面是第一个: + +*compiler.c,在parsePrecedence()方法后添加代码:* + +```c +static void parsePrecedence(Precedence precedence); +// 新增部分开始 +static uint8_t parseVariable(const char* errorMessage) { + consume(TOKEN_IDENTIFIER, errorMessage); + return identifierConstant(&parser.previous); +} +// 新增部分结束 +``` + +> It requires the next token to be an identifier, which it consumes and sends here: + +它要求下一个标识是一个标识符,它会消耗该标识并发送到这里: + +*compiler.c,在parsePrecedence()方法后添加代码:* + +```c +static void parsePrecedence(Precedence precedence); +// 新增部分开始 +static uint8_t identifierConstant(Token* name) { + return makeConstant(OBJ_VAL(copyString(name->start, + name->length))); +} +// 新增部分结束 +``` + +> This function takes the given token and adds its lexeme to the chunk’s constant table as a string. It then returns the index of that constant in the constant table. + +这个函数接受给定的标识,并将其词素作为一个字符串添加到字节码块的常量表中。然后,它会返回该常量在常量表中的索引。 + +> Global variables are looked up *by name* at runtime. That means the VM—the bytecode interpreter loop—needs access to the name. A whole string is too big to stuff into the bytecode stream as an operand. Instead, we store the string in the constant table and the instruction then refers to the name by its index in the table. + +全局变量在运行时是按*名称*查找的。这意味着虚拟机(字节码解释器循环)需要访问该名称。整个字符串太大,不能作为操作数塞进字节码流中。相反,我们将字符串存储到常量表中,然后指令通过该名称在表中的索引来引用它。 + +> This function returns that index all the way to `varDeclaration()` which later hands it over to here: + +这个函数会将索引一直返回给`varDeclaration()`,随后又将其传递到这里: + +*compiler.c,在parseVariable()方法后添加代码:* + +```c +static void defineVariable(uint8_t global) { + emitBytes(OP_DEFINE_GLOBAL, global); +} +``` + +> This outputs the bytecode instruction that defines the new variable and stores its initial value. The index of the variable’s name in the constant table is the instruction’s operand. As usual in a stack-based VM, we emit this instruction last. At runtime, we execute the code for the variable’s initializer first. That leaves the value on the stack. Then this instruction takes that value and stores it away for later. + +它会输出字节码指令,用于定义新变量并存储其初始化值。变量名在常量表中的索引是该指令的操作数。在基于堆栈的虚拟机中,我们通常是最后发出这条指令。在运行时,我们首先执行变量初始化器的代码,将值留在栈中。然后这条指令会获取该值并保存起来,以供日后使用[^8]。 + +> Over in the runtime, we begin with this new instruction: + +在运行时,我们从这条新指令开始: + +*chunk.h,在枚举OpCode中添加代码:* + +```c + OP_POP, + // 新增部分开始 + OP_DEFINE_GLOBAL, + // 新增部分结束 + OP_EQUAL, +``` + +> Thanks to our handy-dandy hash table, the implementation isn’t too hard. + +多亏了我们方便的哈希表,实现起来并不太难。 + +*vm.c,在run()方法中添加代码:* + +```c + case OP_POP: pop(); break; + // 新增部分开始 + case OP_DEFINE_GLOBAL: { + ObjString* name = READ_STRING(); + tableSet(&vm.globals, name, peek(0)); + pop(); + break; + } + // 新增部分结束 + case OP_EQUAL: { +``` + +> We get the name of the variable from the constant table. Then we take the value from the top of the stack and store it in a hash table with that name as the key. + +我们从常量表中获取变量的名称,然后我们从栈顶获取值,并以该名称为键将其存储在哈希表中[^9]。 + +> This code doesn’t check to see if the key is already in the table. Lox is pretty lax with global variables and lets you redefine them without error. That’s useful in a REPL session, so the VM supports that by simply overwriting the value if the key happens to already be in the hash table. + +这段代码并没有检查键是否已经在表中。Lox对全局变量的处理非常宽松,允许你重新定义它们而且不会出错。这在REPL会话中很有用,如果键恰好已经在哈希表中,虚拟机通过简单地覆盖值来支持这一点。 + +> There’s another little helper macro: + +还有另一个小的辅助宏: + +*vm.c,在run()方法中添加代码:* + +```c +#define READ_CONSTANT() (vm.chunk->constants.values[READ_BYTE()]) +// 新增部分开始 +#define READ_STRING() AS_STRING(READ_CONSTANT()) +// 新增部分结束 +#define BINARY_OP(valueType, op) \ +``` + +> It reads a one-byte operand from the bytecode chunk. It treats that as an index into the chunk’s constant table and returns the string at that index. It doesn’t check that the value *is* a string—it just indiscriminately casts it. That’s safe because the compiler never emits an instruction that refers to a non-string constant. + +它从字节码块中读取一个1字节的操作数。它将其视为字节码块的常量表的索引,并返回该索引处的字符串。它不检查该值是否是字符串——它只是不加区分地进行类型转换。这是安全的,因为编译器永远不会发出引用非字符串常量的指令。 + +> Because we care about lexical hygiene, we also undefine this macro at the end of the interpret function. + +因为我们关心词法卫生,所以在解释器函数的末尾也取消了这个宏的定义。 + +*vm.c,在run()方法中添加代码:* + +```c +#undef READ_CONSTANT +// 新增部分开始 +#undef READ_STRING +// 新增部分结束 +#undef BINARY_OP +``` + +> I keep saying “the hash table”, but we don’t actually have one yet. We need a place to store these globals. Since we want them to persist as long as clox is running, we store them right in the VM. + +我一直在说“哈希表”,但实际上我们还没有哈希表。我们需要一个地方来存储这些全局变量。因为我们希望它们在clox运行期间一直存在,所以我们将它们之间存储在虚拟机中。 + +*vm.h,在结构体VM中添加代码:* + +```c + Value* stackTop; + // 新增部分开始 + Table globals; + // 新增部分结束 + Table strings; +``` + +> As we did with the string table, we need to initialize the hash table to a valid state when the VM boots up. + +正如我们对字符串表所做的那样,我们需要在虚拟机启动时将哈希表初始化为有效状态。 + +*vm.c,在initVM()方法中添加代码:* + +```c + vm.objects = NULL; + // 新增部分开始 + initTable(&vm.globals); + // 新增部分结束 + initTable(&vm.strings); +``` + +> And we tear it down when we exit. + +当我们退出时,就将其删掉[^10]。 + +*vm.c,在freeVM()方法中添加代码:* + +```c +void freeVM() { + // 新增部分开始 + freeTable(&vm.globals); + // 新增部分结束 + freeTable(&vm.strings); +``` + +> As usual, we want to be able to disassemble the new instruction too. + +跟往常一样,我们也希望能够对新指令进行反汇编。 + +*debug.c,在disassembleInstruction()方法中添加代码:* + +```c + return simpleInstruction("OP_POP", offset); + // 新增部分开始 + case OP_DEFINE_GLOBAL: + return constantInstruction("OP_DEFINE_GLOBAL", chunk, + offset); + // 新增部分结束 + case OP_EQUAL: +``` + +> And with that, we can define global variables. Not that users can *tell* that they’ve done so, because they can’t actually *use* them. So let’s fix that next. +> + +有了这个,我们就可以定义全局变量了。但用户并不能说他们可以定义全局变量,因为他们实际上还不能使用这些变量。所以,接下来我们解决这个问题。 + +> ## 21 . 3 Reading Variables + +## 21.3 读取变量 + +> As in every programming language ever, we access a variable’s value using its name. We hook up identifier tokens to the expression parser here: + +像所有编程语言中一样,我们使用变量的名称来访问它的值。我们在这里将标识符和表达式解析器进行挂钩: + +*compiler.c,替换1行:* + +```c + [TOKEN_LESS_EQUAL] = {NULL, binary, PREC_COMPARISON}, + // 替换部分开始 + [TOKEN_IDENTIFIER] = {variable, NULL, PREC_NONE}, + // 替换部分结束 + [TOKEN_STRING] = {string, NULL, PREC_NONE}, +``` + +> That calls this new parser function: + +这里调用了这个新解析器函数: + +*compiler.c,在string()方法后添加代码:* + +```c +static void variable() { + namedVariable(parser.previous); +} +``` + +> Like with declarations, there are a couple of tiny helper functions that seem pointless now but will become more useful in later chapters. I promise. + +和声明一样,这里有几个小的辅助函数,现在看起来毫无意义,但在后面的章节中会变得更加有用。我保证。 + +*compiler.c,在string()方法后添加代码:* + +```c +static void namedVariable(Token name) { + uint8_t arg = identifierConstant(&name); + emitBytes(OP_GET_GLOBAL, arg); +} +``` + +> This calls the same `identifierConstant()` function from before to take the given identifier token and add its lexeme to the chunk’s constant table as a string. All that remains is to emit an instruction that loads the global variable with that name. Here’s the instruction: + +这里会调用与之前相同的`identifierConstant()`函数,以获取给定的标识符标识,并将其词素作为字符串添加到字节码块的常量表中。剩下的工作就是生成一条指令,加载具有该名称的全局变量。下面是这个指令: + +*chunk.h,在枚举OpCode中添加代码:* + +```c + OP_POP, + // 新增部分开始 + OP_GET_GLOBAL, + // 新增部分结束 + OP_DEFINE_GLOBAL, +``` + +> Over in the interpreter, the implementation mirrors `OP_DEFINE_GLOBAL`. + +在解释器中,它的实现是`OP_DEFINE_GLOBAL`的镜像操作。 + +*vm.c,在run()方法中添加代码:* + +```c + case OP_POP: pop(); break; + // 新增部分开始 + case OP_GET_GLOBAL: { + ObjString* name = READ_STRING(); + Value value; + if (!tableGet(&vm.globals, name, &value)) { + runtimeError("Undefined variable '%s'.", name->chars); + return INTERPRET_RUNTIME_ERROR; + } + push(value); + break; + } + // 新增部分结束 + case OP_DEFINE_GLOBAL: { +``` + +> We pull the constant table index from the instruction’s operand and get the variable name. Then we use that as a key to look up the variable’s value in the globals hash table. + +我们从指令操作数中提取常量表索引并获得变量名称。然后我们使用它作为键,在全局变量哈希表中查找变量的值。 + +> If the key isn’t present in the hash table, it means that global variable has never been defined. That’s a runtime error in Lox, so we report it and exit the interpreter loop if that happens. Otherwise, we take the value and push it onto the stack. + +如果该键不在哈希表中,就意味着这个全局变量从未被定义过。这在Lox中是运行时错误,所以如果发生这种情况,我们要报告错误并退出解释器循环。否则,我们获取该值并将其压入栈中。 + +*debug.c,在disassembleInstruction()方法中添加代码:* + +```c + return simpleInstruction("OP_POP", offset); + // 新增部分开始 + case OP_GET_GLOBAL: + return constantInstruction("OP_GET_GLOBAL", chunk, offset); + // 新增部分结束 + case OP_DEFINE_GLOBAL: +``` + +> A little bit of disassembling, and we’re done. Our interpreter is now able to run code like this: + +稍微反汇编一下,就完成了。我们的解释器现在可以运行这样的代码了: + +```ruby +var beverage = "cafe au lait"; +var breakfast = "beignets with " + beverage; +print breakfast; +``` + +> There’s only one operation left. + +只剩一个操作了。 + +> ## 21 . 4 Assignment + +## 21.4 赋值 + +> Throughout this book, I’ve tried to keep you on a fairly safe and easy path. I don’t avoid hard *problems*, but I try to not make the *solutions* more complex than they need to be. Alas, other design choices in our bytecode compiler make assignment annoying to implement. + +在这本书中,我一直试图让你走在一条相对安全和简单的道路上。我并不回避困难的*问题*,但是我尽量不让解决方案过于复杂。可惜的是,我们的字节码编译器中的其它设计选择使得赋值的实现变得很麻烦[^11]。 + +> Our bytecode VM uses a single-pass compiler. It parses and generates bytecode on the fly without any intermediate AST. As soon as it recognizes a piece of syntax, it emits code for it. Assignment doesn’t naturally fit that. Consider: + +我们的字节码虚拟机使用的是单遍编译器。它在不需要任何中间AST的情况下,动态地解析并生成字节码。一旦它识别出某个语法,它就会生成对应的字节码。赋值操作天然不符合这一点。请考虑一下: + +```c +menu.brunch(sunday).beverage = "mimosa"; +``` + +> In this code, the parser doesn’t realize `menu.brunch(sunday).beverage` is the target of an assignment and not a normal expression until it reaches `=`, many tokens after the first `menu`. By then, the compiler has already emitted bytecode for the whole thing. + +在这段代码中,直到解析器遇见`=`(第一个`menu`之后很多个标识),它才能意识到`menu.brunch(sunday).beverage`是赋值操作的目标,而不是常规的表达式。到那时,编译器已经为整个代码生成字节码了。 + +> The problem is not as dire as it might seem, though. Look at how the parser sees that example: + +不过,这个问题并不像看上去那么可怕。看看解析器是如何处理这个例子的: + +![The 'menu.brunch(sunday).beverage = "mimosa"' statement, showing that 'menu.brunch(sunday)' is an expression.](21.全局变量/setter.png) + +> Even though the `.beverage` part must not be compiled as a get expression, everything to the left of the `.` is an expression, with the normal expression semantics. The `menu.brunch(sunday)` part can be compiled and executed as usual. + +尽管`.beverage`部分无法被编译为一个get表达式,`.`左侧的其它部分是一个表达式,有着正常的表达式语义。`menu.brunch(sunday)`部分可以像往常一样编译和执行。 + +> Fortunately for us, the only semantic differences on the left side of an assignment appear at the very right-most end of the tokens, immediately preceding the `=`. Even though the receiver of a setter may be an arbitrarily long expression, the part whose behavior differs from a get expression is only the trailing identifier, which is right before the `=`. We don’t need much lookahead to realize `beverage` should be compiled as a set expression and not a getter. + +幸运的是,赋值语句左侧部分唯一的语义差异在于其最右侧的标识,紧挨着`=`之前。尽管setter的接收方可能是一个任意长的表达式,但与get表达式不同的部分在于尾部的标识符,它就在`=`之前。我们不需要太多的前瞻就可以意识到`beverage`应该被编译为set表达式而不是getter。 + +> Variables are even easier since they are just a single bare identifier before an `=`. The idea then is that right *before* compiling an expression that can also be used as an assignment target, we look for a subsequent `=` token. If we see one, we compile it as an assignment or setter instead of a variable access or getter. + +变量就更简单了,因为它们在`=`之前就是一个简单的标识符。那么我们的想法是,在编译一个也可以作为赋值目标的表达式*之前*,我们会寻找随后的`=`标识。如果我们看到了,那表明我们将其一个赋值表达式或setter来编译,而不是变量访问或getter。 + +> We don’t have setters to worry about yet, so all we need to handle are variables. + +我们还不需要考虑setter,所以我们需要处理的就是变量。 + +*compiler.c,在namedVariable()方法中替换1行:* + +```c + uint8_t arg = identifierConstant(&name); + // 替换部分开始 + if (match(TOKEN_EQUAL)) { + expression(); + emitBytes(OP_SET_GLOBAL, arg); + } else { + emitBytes(OP_GET_GLOBAL, arg); + } + // 替换部分结束 +} +``` + +> In the parse function for identifier expressions, we look for an equals sign after the identifier. If we find one, instead of emitting code for a variable access, we compile the assigned value and then emit an assignment instruction. + +在标识符表达式的解析函数中,我们会查找标识符后面的等号。如果找到了,我们就不会生成变量访问的代码,我们会编译所赋的值,然后生成一个赋值指令。 + +> That’s the last instruction we need to add in this chapter. + +这就是我们在本章中需要添加的最后一条指令。 + +*chunk.h,在枚举OpCode中添加代码:* + +```c + OP_DEFINE_GLOBAL, + // 新增部分开始 + OP_SET_GLOBAL, + // 新增部分结束 + OP_EQUAL, +``` + +> As you’d expect, its runtime behavior is similar to defining a new variable. + +如你所想,它的运行时行为类似于定义一个新变量。 + +*vm.c,在run()方法中添加代码[^12]:* + +```c + } + // 新增部分开始 + case OP_SET_GLOBAL: { + ObjString* name = READ_STRING(); + if (tableSet(&vm.globals, name, peek(0))) { + tableDelete(&vm.globals, name); + runtimeError("Undefined variable '%s'.", name->chars); + return INTERPRET_RUNTIME_ERROR; + } + break; + } + // 新增部分结束 + case OP_EQUAL: { +``` + +> The main difference is what happens when the key doesn’t already exist in the globals hash table. If the variable hasn’t been defined yet, it’s a runtime error to try to assign to it. Lox [doesn’t do implicit variable declaration](http://www.craftinginterpreters.com/statements-and-state.html#design-note). + +主要的区别在于,当键在全局变量哈希表中不存在时会发生什么。如果这个变量还没有定义,对其进行赋值就是一个运行时错误。Lox不做隐式的变量声明。 + +> The other difference is that setting a variable doesn’t pop the value off the stack. Remember, assignment is an expression, so it needs to leave that value there in case the assignment is nested inside some larger expression. + +另一个区别是,设置变量并不会从栈中弹出值。记住,赋值是一个表达式,所以它需要把这个值保留在那里,以防赋值嵌套在某个更大的表达式中。 + +> Add a dash of disassembly: + +加一点反汇编代码: + +*debug.c,在disassembleInstruction()方法中添加代码:* + +```c + return constantInstruction("OP_DEFINE_GLOBAL", chunk, + offset); + // 新增部分开始 + case OP_SET_GLOBAL: + return constantInstruction("OP_SET_GLOBAL", chunk, offset); + // 新增部分结束 + case OP_EQUAL: +``` + +> So we’re done, right? Well . . . not quite. We’ve made a mistake! Take a gander at: + +我们已经完成了,是吗?嗯……不完全是。我们犯了一个错误!看一下这个: + +```c +a * b = c + d; +``` + +> According to Lox’s grammar, `=` has the lowest precedence, so this should be parsed roughly like: + +根据Lox语法,`=`的优先级最低,所以这大致应该解析为: + +![The expected parse, like '(a * b) = (c + d)'.](21.全局变量/ast-good.png) + +> Obviously, `a * b` isn’t a valid assignment target, so this should be a syntax error. But here’s what our parser does: + +显然,`a*b`不是一个有效的赋值目标[^13],所以这应该是一个语法错误。但我们的解析器是这样的: + +> 1. First, `parsePrecedence()` parses `a` using the `variable()` prefix parser. +> 2. After that, it enters the infix parsing loop. +> 3. It reaches the `*` and calls `binary()`. +> 4. That recursively calls `parsePrecedence()` to parse the right-hand operand. +> 5. That calls `variable()` again for parsing `b`. +> 6. Inside that call to `variable()`, it looks for a trailing `=`. It sees one and thus parses the rest of the line as an assignment. + +1. 首先,`parsePrecedence()`使用`variable()`前缀解析器解析`a`。 +2. 之后,会进入中缀解析循环。 +3. 达到`*`,并调用`binary()`。 +4. 递归地调用`parsePrecedence()`解析右操作数。 +5. 再次调用`variable()`解析`b`。 +6. 在对`variable()`的调用中,会查找尾部的`=`。它看到了,因此会将本行的其余部分解析为一个赋值表达式。 + +> In other words, the parser sees the above code like: + +换句话说,解析器将上面的代码看作: + +![The actual parse, like 'a * (b = c + d)'.](21.全局变量/ast-bad.png) + +> We’ve messed up the precedence handling because `variable()` doesn’t take into account the precedence of the surrounding expression that contains the variable. If the variable happens to be the right-hand side of an infix operator, or the operand of a unary operator, then that containing expression is too high precedence to permit the `=`. + +我们搞砸了优先级处理,因为`variable()`没有考虑包含变量的外围表达式的优先级。如果变量恰好是中缀操作符的右操作数,或者是一元操作符的操作数,那么这个包含表达式的优先级太高,不允许使用`=`。 + +> To fix this, `variable()` should look for and consume the `=` only if it’s in the context of a low-precedence expression. The code that knows the current precedence is, logically enough, `parsePrecedence()`. The `variable()` function doesn’t need to know the actual level. It just cares that the precedence is low enough to allow assignment, so we pass that fact in as a Boolean. + +为了解决这个问题,`variable()`应该只在低优先级表达式的上下文中寻找并使用`=`。从逻辑上讲,知道当前优先级的代码是`parsePrecedence()`。`variable()`函数不需要知道实际的级别。它只关心优先级是否低到允许赋值表达式,所以我们把这个情况以布尔值传入。 + +*compiler.c,在parsePrecedence()方法中替换1行:* + +```c + error("Expect expression."); + return; + } + // 替换部分开始 + bool canAssign = precedence <= PREC_ASSIGNMENT; + prefixRule(canAssign); + // 替换部分结束 + while (precedence <= getRule(parser.current.type)->precedence) { +``` + +> Since assignment is the lowest-precedence expression, the only time we allow an assignment is when parsing an assignment expression or top-level expression like in an expression statement. That flag makes its way to the parser function here: + +因为赋值是最低优先级的表达式,只有在解析赋值表达式或如表达式语句等顶层表达式时,才允许出现赋值。这个标志会被传入这个解析器函数: + +*compiler.c,在variable()函数中替换3行:* + +```c +static void variable(bool canAssign) { + namedVariable(parser.previous, canAssign); +} +``` + +> Which passes it through a new parameter: + +通过一个新参数透传该值: + +*compiler.c,在namedVariable()方法中替换1行:* + +```c +// 替换部分开始 +static void namedVariable(Token name, bool canAssign) { + // 替换部分结束 + uint8_t arg = identifierConstant(&name); +``` + +> And then finally uses it here: + +最后在这里使用它: + +``` + uint8_t arg = identifierConstant(&name); +``` + +*compiler.c,在namedVariable()方法中替换1行:* + +```c + uint8_t arg = identifierConstant(&name); + // 替换部分开始 + if (canAssign && match(TOKEN_EQUAL)) { + // 替换部分结束 + expression(); +``` + +> That’s a lot of plumbing to get literally one bit of data to the right place in the compiler, but arrived it has. If the variable is nested inside some expression with higher precedence, `canAssign` will be `false` and this will ignore the `=` even if there is one there. Then `namedVariable()` returns, and execution eventually makes its way back to `parsePrecedence()`. + +为了把字面上的1比特数据送到编译器的正确位置需要做很多工作,但它已经到达了。如果变量嵌套在某个优先级更高的表达式中,`canAssign`将为`false`,即使有`=`也会被忽略。然后`namedVariable()`返回,执行最终返回到了`parsePrecedence()`。 + +> Then what? What does the compiler do with our broken example from before? Right now, `variable()` won’t consume the `=`, so that will be the current token. The compiler returns back to `parsePrecedence()` from the `variable()` prefix parser and then tries to enter the infix parsing loop. There is no parsing function associated with `=`, so it skips that loop. + +然后呢?编译器会对我们前面的负面例子做什么?现在,`variable()`不会消耗`=`,所以它将是当前的标识。编译器从`variable()`前缀解析器返回到`parsePrecedence()`,然后尝试进入中缀解析循环。没有与`=`相关的解析函数,因此也会跳过这个循环。 + +> Then `parsePrecedence()` silently returns back to the caller. That also isn’t right. If the `=` doesn’t get consumed as part of the expression, nothing else is going to consume it. It’s an error and we should report it. + +然后`parsePrecedence()`默默地返回到调用方。这也是不对的。如果`=`没有作为表达式的一部分被消耗,那么其它任何东西都不会消耗它。这是一个错误,我们应该报告它。 + +*compiler.c,在parsePrecedence()方法中添加代码:* + +```c + infixRule(); + } + // 新增部分开始 + if (canAssign && match(TOKEN_EQUAL)) { + error("Invalid assignment target."); + } + // 新增部分结束 +} +``` + +> With that, the previous bad program correctly gets an error at compile time. OK, *now* are we done? Still not quite. See, we’re passing an argument to one of the parse functions. But those functions are stored in a table of function pointers, so all of the parse functions need to have the same type. Even though most parse functions don’t support being used as an assignment target—setters are the only other one—our friendly C compiler requires them *all* to accept the parameter. + +这样,前面的错误程序在编译时就会正确地得到一个错误。好了,现在我们完成了吗?也不尽然。看,我们正向一个解析函数传递参数。但是这些函数是存储在一个函数指令表格中的,所以所有的解析函数需要具有相同的类型。尽管大多数解析函数都不支持被用作赋值目标——setter是唯一的一个[^14]——但我们这个友好的C编译器要求它们*都*接受相同的参数。 + +> So we’re going to finish off this chapter with some grunt work. First, let’s go ahead and pass the flag to the infix parse functions. + +所以我们要做一些苦差事来结束这一章。首先,让我们继续前进,将标志传给中缀解析函数。 + +*compiler.c,在parsePrecedence()方法中替换1行:* + +```c + ParseFn infixRule = getRule(parser.previous.type)->infix; + // 替换部分开始 + infixRule(canAssign); + // 替换部分结束 + } +``` + +> We’ll need that for setters eventually. Then we’ll fix the typedef for the function type. + +我们最终会在setter中需要它。然后,我们要修复函数类型的类型定义。 + +*compiler.c,在枚举Precedence后替换1行:* + +```c +} Precedence; +// 替换部分开始 +typedef void (*ParseFn)(bool canAssign); +// 替换部分结束 +typedef struct { +``` + +> And some completely tedious code to accept this parameter in all of our existing parse functions. Here: + +还有一些非常乏味的代码,为了在所有的现有解析函数中接受这个参数。这里: + +*compiler.c,在binary()方法中替换1行:* + +```c +// 替换部分开始 +static void binary(bool canAssign) { +// 替换部分结束 + TokenType operatorType = parser.previous.type; +``` + +这里: + +*compiler.c,在literal()方法中替换1行:* + +```c +// 替换部分开始 +static void literal(bool canAssign) { +// 替换部分结束 + switch (parser.previous.type) { +``` + +这里: + +*compiler.c,在grouping()方法中替换1行:* + +```c +// 替换部分开始 +static void grouping(bool canAssign) { +// 替换部分结束 + expression(); +``` + +这里: + +*compiler.c,在number()方法中替换1行:* + +```c +// 替换部分开始 +static void number(bool canAssign) { +// 替换部分结束 + double value = strtod(parser.previous.start, NULL); +``` + +还有这里: + +*compiler.c,在string()方法中替换1行:* + +```c +// 替换部分开始 +static void string(bool canAssign) { +// 替换部分结束 + emitConstant(OBJ_VAL(copyString(parser.previous.start + 1, +``` + +最后: + +*compiler.c,在unary()方法中替换1行:* + +```c +// 替换部分开始 +static void unary(bool canAssign) { +// 替换部分结束 + TokenType operatorType = parser.previous.type; +``` + +> Phew! We’re back to a C program we can compile. Fire it up and now you can run this: + +吁!我们又回到了可以编译的C程序。启动它,新增你可以运行这个: + +```javascript +var breakfast = "beignets"; +var beverage = "cafe au lait"; +breakfast = "beignets with " + beverage; + +print breakfast; +``` + +> It’s starting to look like real code for an actual language! + +它开始看起来像是实际语言的真正代码了! + + + +[^1]: 这是复杂的语言实现中常见的元策略。通常情况下,同一种语言特性会有多种实现技术,每种技术都针对不同的使用模式进行了优化。举例来说,与属性集可以自由修改的其它对象相比,Java Script虚拟机通常对那些使用起来像类实例对象有着更快的表示形式。C和C++编译器通常由多种方法能够根据case分支数量和case值的密集程度来编译`switch`语句。 +[^2]: 代码块的作用有点像表达式中的括号。块可以让你把“低级别的”声明语句放在只允许“高级别的”非声明语句的地方。 +[^3]: 这听起来微不足道,但是非玩具型语言的手写解析器非常大。当你有数千行代码时,如果一个实用函数可以将两行代码简化为一行代码,并使结果更易于阅读,那它就很容易被接受。 +[^4]: `OP_ADD`执行过后堆栈会少一个元素,所以它的效应是`-1`:![The stack effect of an OP_ADD instruction.](21.全局变量/stack-effect.png) +[^5]: 不过,我们只是近了一步。等我们添加函数时,还会重新审视`OP_RETURN`。现在,它退出整个解释器的循环即可。 +[^6]: 据我统计,在本章末尾的`compiler.c`版本中,149条语句中有80条是表达式语句。 +[^7]: 基本上,编译器会对变量声明进行脱糖处理,如`var a;`变成`var a = nil;`,它为前者生成的代码和为后者生成的代码是相同的。 +[^8]: 我知道这里有一些函数现在看起来没什么意义。但是,随着我们增加更多与名称相关的语言特性,我们会从中获得更多的好处。函数和类声明都声明了新的变量,而变量表达式和赋值表达式会访问它们。 +[^9]: 请注意,直到将值添加到哈希表之后,我们才会弹出它。这确保了如果在将值添加到哈希表的过程中触发了垃圾回收,虚拟机仍然可以找到这个值。这显然是很可能的,因为哈希表在调整大小时需要动态分配。 +[^10]: 这个进程在退出时会释放所有的东西,但要求操作系统来收拾我们的烂摊子,总感觉很不体面。 +[^11]: 如果你还记得,在jlox中赋值是很容易的。 +[^12]: 对`tableSet()`的调用会将值存储在全局变量表中,即使该变量之前没有定义。这个问题在REPL会话中是用户可见的,因为即使报告了运行时错误,它仍然在运行。因此,我们也要注意从表中删除僵尸值。 +[^13]: 如果`a*b`是一个有效的赋值目标,这岂不是很疯狂?你可以想象一些类似代数的语言,试图以某种合理的方式划分所赋的值,并将其分配给`a`和`b`……这可能是一个很糟糕的主意。 +[^14]: 如果Lox有数组和下标操作符,如`array[index]`,那么中缀操作符`[`也能允许赋值,支持:`array[index] = value`。 + + + +--- + +## 习题 + +1. > The compiler adds a global variable’s name to the constant table as a string every time an identifier is encountered. It creates a new constant each time, even if that variable name is already in a previous slot in the constant table. That’s wasteful in cases where the same variable is referenced multiple times by the same function. That, in turn, increases the odds of filling up the constant table and running out of slots since we allow only 256 constants in a single chunk. + > + > Optimize this. How does your optimization affect the performance of the compiler compared to the runtime? Is this the right trade-off? + + 每次遇到标识符时,编译器都会将全局变量的名称作为字符串添加到常量表中。它每次都会创建一个新的常量,即使这个变量的名字已经在常量表中的前一个槽中存在。在同一个函数多次引用同一个变量的情况下,这是一种浪费。这反过来又增加了填满常量表的可能性,因为我们在一个字节码块中只允许有256个常量。 + + 对此进行优化。与运行时相比,你的优化对编译器的性能有何影响?这是正确的取舍吗? + +2. > Looking up a global variable by name in a hash table each time it is used is pretty slow, even with a good hash table. Can you come up with a more efficient way to store and access global variables without changing the semantics? + + 每次使用全局变量时,根据名称在哈希表中查找变量是很慢的,即使有一个很好的哈希表。你能否想出一种更有效的方法来存储和访问全局变量而不改变语义? + +3. > When running in the REPL, a user might write a function that references an unknown global variable. Then, in the next line, they declare the variable. Lox should handle this gracefully by not reporting an “unknown variable” compile error when the function is first defined. + > + > But when a user runs a Lox *script*, the compiler has access to the full text of the entire program before any code is run. Consider this program: + + 当在REPL中运行时,用户可能会编写一个引用未知全局变量的函数。然后,在下一行中,他们声明了这个变量。Lox应该优雅地处理这个问题,在第一次定义函数时不报告“未知变量”的编译错误。 + + 但是,当用户运行Lox脚本时,编译器可以在任何代码运行之前访问整个程序的全部文本。考虑一下这个程序: + + ```javascript + fun useVar() { + print oops; + } + + var ooops = "too many o's!"; + ``` + + > Here, we can tell statically that `oops` will not be defined because there is *no* declaration of that global anywhere in the program. Note that `useVar()` is never called either, so even though the variable isn’t defined, no runtime error will occur because it’s never used either. + > + > We could report mistakes like this as compile errors, at least when running from a script. Do you think we should? Justify your answer. What do other scripting languages you know do? + + 这里,我们可以静态地告知用户`oops`不会被定义,因为在程序中没有任何地方对该全局变量进行了声明。请注意,`useVar()`也从未被调用,所以即使变量没有被定义,也不会发生运行时错误,因为它从未被使用。 + + 我们可以将这样的错误报告为编译错误,至少在运行脚本时是这样。你认为我们应该这样做吗?请说明你的答案。你知道其它脚本语言是怎么做的吗? diff --git a/posts/hello_world/index.md b/posts/hello_world/index.md new file mode 100644 index 0000000..86942fa --- /dev/null +++ b/posts/hello_world/index.md @@ -0,0 +1,8 @@ +--- +title: "A Chain Reaction" +date: '2023-12-11' +spoiler: "The limits of my language mean the limits of my world." +--- + +# Hello +World World diff --git a/posts/mlog_2022-10-26.md b/posts/mlog_2022-10-26.md deleted file mode 100644 index dce6923..0000000 --- a/posts/mlog_2022-10-26.md +++ /dev/null @@ -1,9 +0,0 @@ -```metadata -{ - "cover": "../app/assets/sources/mlog_cover.png", - "tag": "碎碎念", - "title": "飞贼" -} -``` - -早上匆匆茫茫去买肠粉,走的时候跑的飞快忘记给钱了😅,估计老板都懵了,拿了肠粉就跑... diff --git a/router/src/lib.rs b/router/src/lib.rs deleted file mode 100644 index 231d436..0000000 --- a/router/src/lib.rs +++ /dev/null @@ -1,23 +0,0 @@ -use yew_router::prelude::*; - -#[derive(Clone, Routable, PartialEq, Debug)] -pub enum RootRoutes { - #[at("/home")] - Home, - #[at("/posts/:filename")] - Post { filename: String }, - #[at("/")] - Root, - #[at("/projects")] - Projects, - #[at("/links")] - Links, - #[at("/about")] - About, - // Compatible with https://github.com/jetli/awesome-yew - #[at("/technology")] - Technology, - #[not_found] - #[at("/404")] - NotFound, -} diff --git a/services/.gitignore b/services/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/services/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/services/Cargo.toml b/services/Cargo.toml deleted file mode 100644 index 690983f..0000000 --- a/services/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "services" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -pulldown-cmark = "0.8.0" -regex = "1.5.5" -serde = "1.0.137" -web-sys = { version = "0.3", features = ["HtmlMetaElement", "Document", "Element", "DocumentFragment", "HtmlTemplateElement", "MediaQueryList"] } -serde_json = "1.0.64" -once_cell = "1.10.0" -yew = "0.19.3" -material-yew = { git = "https://github.com/hamza1311/material-yew", features = ["full"] } -lazy_static = "1.4.0" -chrono = "0.4" -urlencoding = "2.1.0" -wasm-logger = "0.2.0" -log = "0.4.17" - -[dependencies.syntect] -version = "4.5" -default-features = false -features = [ - "html", - "dump-load", - "regex-fancy" -] - -[build-dependencies] -syntect = { version = "4.5", default-features = false, features = ["default-fancy"] } diff --git a/services/src/lib.rs b/services/src/lib.rs deleted file mode 100644 index 343abcc..0000000 --- a/services/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod links_service; -pub mod markdown_service; -pub mod post_service; -pub mod posts; -pub mod projects_service; -pub mod theme_service; -extern crate lazy_static; diff --git a/services/src/links_service/links.json b/services/src/links_service/links.json deleted file mode 100644 index 0e73cf7..0000000 --- a/services/src/links_service/links.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "links": [ - { - "name": "Clay 的技术博客", - "addr": "https://www.techgrow.cn", - "desc": "用进废退 | 艺不压身", - "logo": "https://www.techgrow.cn/img/head.jpg" - }, - { - "name": "Christine的博客", - "desc": "虽然我不够优秀,但我从未放弃过努力。", - "logo": "https://christine-only.github.io/blog/logo.png", - "addr": "https://christine-only.github.io/blog/" - }, - { - "name": "Forever丿顾北博客", - "addr": "https://forevergubei.gitee.io/myblod/", - "desc": "一个追寻大佬脚步的小白", - "logo": "https://forevergubei.gitee.io/myblod/logo.png" - }, - { - "name": "MrDJun's Blog", - "addr": "https://mrdjun.gitee.io/", - "desc": "Just an ordinary JAVA programmer", - "logo": "https://mrdjun.gitee.io/images/avatar.png" - }, - { - "name": "Fatpandac’s Blog", - "addr": "https://www.fatpandac.com/", - "desc": "不知道写啥的个人博客", - "logo": "https://avatars.githubusercontent.com/u/30423976?v=4" - }, - { - "name": "低调小熊猫", - "addr": "https://ilovey.live", - "desc": "读万卷书,行万里路,赚很多钱", - "logo": "https://uss.ilovey.live/img/avatar.jpg" - }, - { - "name": "风吹鼍鼓旌旗动", - "addr": "http://idinr.com/", - "desc": "有技术,有科学", - "logo": "https://s2.loli.net/2022/06/15/d6MYUaVWf74sKIi.jpg" - }, - { - "name": "Levi's Blog", - "addr": "https://www.jianshu.com/u/1fc674525eeb", - "desc": "学的越多,懂的越少", - "logo": "https://upload.jianshu.io/users/upload_avatars/10826765/5e06dfca-ff26-488c-a60c-79ca6504917a?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240" - }, - { - "name": "高耳机", - "addr": "http://www.ragnaroks.site/", - "desc": "青春有爱 年少有梦", - "logo": "https://avatars.githubusercontent.com/u/7598199?v=4" - }, - { - "name": "Pseudoyu", - "addr": "https://www.pseudoyu.com/", - "desc": "Blockchain | Programming | Photography | Misty", - "logo": "https://www.pseudoyu.com/images/author.webp" - }, - { - "name": "circlehotarux's blog", - "addr": "https://www.circlehotarux.me/", - "desc": "心诚则灵", - "logo": "https://avatars.githubusercontent.com/u/48060080?v=4" - }, - { - "name": "小博's blog", - "addr": "https://zhongbohuang.github.io/hzb-blog.github.io/", - "desc": "记录知识,记录生活", - "logo": "https://zhongbohuang.github.io/hzb-blog.github.io/img/avatar.png" - }, - { - "name": "绝对值の垃圾站", - "addr": "https://absolutevalue.cc", - "desc": "一个垃圾站,写点垃圾话", - "logo": "https://bu.dusays.com/2022/10/11/63456cf24d191.png" - }, - { - "name": "SeerSu", - "addr": "https://seersu.me", - "desc": "留给自己一片大陆,把无垠的大海留给飞鸟和大鱼", - "logo": "https://seersu.me/favicon/avatar.jpg" - }, - { - "name": "Scorpio's Blog", - "addr": "http://0925.wang", - "desc": "改掉拖延症~", - "logo": "https://q.qlogo.cn/headimg_dl?bs=qq&dst_uin=1849748550@qq.com&src_uin=qq.feixue.me&fid=blog&spec=100&id=1000" - }, - { - "name": "一物一世界的 blog", - "addr": "https://www.flyfrag.cn/", - "desc": "太阳强烈,水波温柔", - "logo": "https://avatars.githubusercontent.com/u/53332638?v=4" - }, - { - "name": "Airing's blog", - "addr": "https://blog.ursb.me/", - "desc": "我是一只小小小小熊", - "logo": "https://airing.ursb.me/image/airing-face.png" - } - ] -} diff --git a/services/src/links_service/links_service.rs b/services/src/links_service/links_service.rs deleted file mode 100644 index 23fea94..0000000 --- a/services/src/links_service/links_service.rs +++ /dev/null @@ -1,36 +0,0 @@ -use once_cell::sync::Lazy; -use serde::Deserialize; -use serde_json; -#[derive(Deserialize, Clone, PartialEq)] -pub struct RawLinkData { - pub links: Vec, -} - -#[derive(Deserialize, Clone, PartialEq)] -pub struct LinkData { - pub name: String, - pub desc: String, - pub addr: String, - pub logo: Option, -} - -pub struct LinksService { - links_data: Vec, -} - -impl LinksService { - pub fn new() -> LinksService { - let links_data = include_str!("./links.json"); - let links_data: RawLinkData = serde_json::from_str(links_data).unwrap(); - - LinksService { - links_data: links_data.links, - } - } - - pub fn get_links_data(&self) -> Vec { - self.links_data.clone() - } -} - -pub static LINKS_SERVICE: Lazy = Lazy::new(|| LinksService::new()); diff --git a/services/src/links_service/mod.rs b/services/src/links_service/mod.rs deleted file mode 100644 index c153bab..0000000 --- a/services/src/links_service/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod links_service; diff --git a/services/src/markdown_service/elements.rs b/services/src/markdown_service/elements.rs deleted file mode 100644 index 2b44b2d..0000000 --- a/services/src/markdown_service/elements.rs +++ /dev/null @@ -1,73 +0,0 @@ -use serde::{Deserialize, Serialize}; -use urlencoding::encode; - -#[derive(Serialize, Deserialize, Debug)] -pub struct GitHubRenderBlock { - pub url: String, - pub repo: String, - pub description: String, -} - -pub fn render_heading(content: String, level: u32) -> String { - format!( - "{}", - content, level, encode(content.as_str()).to_string(), content, level - ) -} - -pub fn render_code_block(code_block: String) -> String { - format!( - "
-
-
-
-
-
- {} -
", - code_block - ) -} - -pub fn render_image(url: String, alt: String) -> String { - format!( - "
- {} - {} -
", - url, alt, url, alt - ) -} - -pub fn render_github_render_block(github_render_block: GitHubRenderBlock) -> String { - format!( - "", - github_render_block.url, github_render_block.repo, github_render_block.description - ) -} - -pub fn render_spotlight(text: &str) -> String { - format!( - "
-
{}
-
", - text - ) -} - -pub fn render_code(code: String) -> String { - format!("{}", code) -} diff --git a/services/src/markdown_service/markdown_service.rs b/services/src/markdown_service/markdown_service.rs deleted file mode 100644 index 4746077..0000000 --- a/services/src/markdown_service/markdown_service.rs +++ /dev/null @@ -1,280 +0,0 @@ -use crate::markdown_service::elements::render_code; -use crate::markdown_service::elements::render_github_render_block; -use crate::markdown_service::elements::render_heading; -use crate::markdown_service::elements::render_spotlight; -use crate::markdown_service::elements::GitHubRenderBlock; -use crate::markdown_service::elements::{render_code_block, render_image}; -use crate::post_service::post_card_size::PostCardSize; -use pulldown_cmark::CodeBlockKind; -use pulldown_cmark::CodeBlockKind::{Fenced, Indented}; -use pulldown_cmark::{html, Event, Options, Parser, Tag}; -use serde::Deserialize; -use serde_json; -use std::path::Path; -use syntect::highlighting::ThemeSet; -use syntect::html::highlighted_html_for_string; -use syntect::parsing::SyntaxReference; -use syntect::parsing::SyntaxSet; -use web_sys::window; -use web_sys::Element; - -#[derive(Clone)] -pub struct MarkdownService { - value: String, -} - -#[derive(Deserialize, Clone, Debug, PartialEq)] -pub struct RawPostMetadata { - pub cover: String, - pub tag: String, - pub title: String, - pub size: Option, - pub pined: Option, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct PostMetadata { - pub cover: String, - pub tag: String, - pub title: String, - pub size: PostCardSize, - pub pined: bool, -} - -#[derive(Clone)] -enum TraverseKind { - Spotlight, - CodeBlock(SyntaxReference), - GitHubRenderBlock, - Metadata, - Image(String), - Heading(u32), - Nope, -} - -impl MarkdownService { - pub fn new(input: String) -> MarkdownService { - return MarkdownService { value: input }; - } - - fn parse_code_block_language_kind(kind: &CodeBlockKind) -> String { - match kind { - Indented => "rust".to_string(), - Fenced(language) => language.to_string(), - } - } - - fn parse_source_path(path: &str) -> String { - let path = Path::new(path); - let filename = path.file_name().expect("Cannot parse filename of image, please make sure the image has a valid extension and file stem.").to_str().unwrap(); - - format!("/sources/{}", filename) - } - - /** - * The post metadata is looks like: - * - * ```metadata - * { - * "cover": "https://zzhack.fun", - * "tag": ["zzhack", "zzhack.fun"] - * } - * ``` - */ - pub fn extract_metadata(&self) -> Option { - let mut metadata: Vec = vec![]; - let mut traverse_kind = TraverseKind::Nope; - let parser = Parser::new_ext(&self.value, Options::empty()); - - parser.for_each(|event| match event { - Event::Start(Tag::CodeBlock(kind)) => { - let language = MarkdownService::parse_code_block_language_kind(&kind); - - if language == "metadata" { - traverse_kind = TraverseKind::Metadata; - } - } - Event::End(Tag::CodeBlock(_)) => { - if let TraverseKind::Metadata = traverse_kind { - traverse_kind = TraverseKind::Nope; - } - } - Event::Text(text) => { - if let TraverseKind::Metadata = traverse_kind { - metadata.push(text.to_string()) - } - } - _ => {} - }); - - if metadata.len() == 0 { - return None; - } - - let parsed_metadata = metadata.join(""); - let mut metadata: RawPostMetadata = serde_json::from_str(parsed_metadata.as_str()).unwrap(); - - metadata.cover = MarkdownService::parse_source_path(&metadata.cover); - let metadata = PostMetadata { - cover: metadata.cover, - tag: metadata.tag, - title: metadata.title, - size: match metadata.size { - Some(size) => PostCardSize::from(size), - None => PostCardSize::Small, - }, - pined: match metadata.pined { - Some(pined) => pined, - None => false, - }, - }; - - return Some(metadata); - } - - pub fn parse(&self, theme: &'static str) -> String { - let mut traverse_kind = TraverseKind::Nope; - - let mut codes: Vec = Vec::new(); - let ss = SyntaxSet::load_defaults_newlines(); - let ts = ThemeSet::load_defaults(); - let theme = &ts.themes[theme]; - let parser = Parser::new_ext(&self.value, Options::empty()) - .map(|event| match event { - Event::Start(Tag::Image(..)) => { - traverse_kind = TraverseKind::Image("".into()); - event - } - Event::Start(Tag::Heading(level)) => { - traverse_kind = TraverseKind::Heading(level); - event - } - Event::End(Tag::Heading(..)) => { - traverse_kind = TraverseKind::Nope; - event - } - Event::End(Tag::Image(kind, url, title)) => { - if let TraverseKind::Image(alt) = traverse_kind.clone() { - traverse_kind = TraverseKind::Nope; - let stringify_url = url.to_string(); - - return Event::Html( - render_image( - MarkdownService::parse_source_path(stringify_url.as_str()), - alt, - ) - .into(), - ); - } - - traverse_kind = TraverseKind::Nope; - - Event::End(Tag::Image(kind, url, title)) - } - Event::Code(code) => Event::Html(render_code(code.to_string()).into()), - Event::Start(Tag::CodeBlock(kind)) => { - let language = MarkdownService::parse_code_block_language_kind(&kind); - - if language == "github" { - traverse_kind = TraverseKind::GitHubRenderBlock; - return Event::Start(Tag::CodeBlock(kind)); - }; - - if language == "metadata" { - traverse_kind = TraverseKind::Metadata; - return Event::Start(Tag::CodeBlock(kind)); - } - - if language == "spotlight" { - traverse_kind = TraverseKind::Spotlight; - return Event::Start(Tag::CodeBlock(kind)); - } - - let syntax = match ss.find_syntax_by_name(language.as_ref()) { - Some(syntax) => syntax, - None => ss.find_syntax_by_extension("rs").unwrap(), - }; - traverse_kind = TraverseKind::CodeBlock(syntax.clone()); - - Event::Start(Tag::CodeBlock(kind)) - } - Event::End(Tag::CodeBlock(code)) => { - let parsed_code = codes.join(""); - let event = match traverse_kind.clone() { - TraverseKind::CodeBlock(syntax) => { - let html = highlighted_html_for_string( - parsed_code.as_str(), - &ss, - &syntax, - theme, - ); - Event::Html(render_code_block(html).into()) - } - TraverseKind::Metadata => Event::Text("".into()), - TraverseKind::GitHubRenderBlock => { - let github_render_block: GitHubRenderBlock = - serde_json::from_str(parsed_code.as_str()).unwrap(); - Event::Html(render_github_render_block(github_render_block).into()) - } - TraverseKind::Spotlight => { - Event::Html(render_spotlight(parsed_code.as_str()).into()) - } - _ => Event::End(Tag::CodeBlock(code)), - }; - - // reset codes - codes = Vec::new(); - traverse_kind = TraverseKind::Nope; - - event - } - Event::Text(text) => { - let empty_str_event = Event::Text("".into()); - let parsed_text = text.to_string(); - - match traverse_kind { - TraverseKind::Image(_) => { - traverse_kind = TraverseKind::Image(parsed_text); - - empty_str_event - } - TraverseKind::CodeBlock(_) - | TraverseKind::GitHubRenderBlock - | TraverseKind::Spotlight => { - codes.push(parsed_text); - - empty_str_event - } - TraverseKind::Heading(level) => { - Event::Html(render_heading(parsed_text, level).into()) - } - TraverseKind::Metadata => Event::Text("".into()), - _ => Event::Text(text), - } - } - _ => event, - }) - .filter(|event| match event { - Event::Start(Tag::CodeBlock(_)) | Event::End(Tag::CodeBlock(_)) => false, - Event::Start(Tag::Image(..)) | Event::End(Tag::Image(..)) => false, - Event::Start(Tag::Heading(..)) | Event::End(Tag::Heading(..)) => false, - _ => true, - }); - - let mut html_output: String = String::with_capacity(self.value.len() * 3 / 2); - - html::push_html(&mut html_output, parser); - - html_output - } - - pub fn parse_to_element(&self, theme: &'static str) -> Element { - let window = window().unwrap(); - let div = window.document().unwrap().create_element("div").unwrap(); - let parsed_html = self.parse(theme); - - div.set_inner_html(parsed_html.as_str()); - - div - } -} diff --git a/services/src/markdown_service/mod.rs b/services/src/markdown_service/mod.rs deleted file mode 100644 index 4c58ee3..0000000 --- a/services/src/markdown_service/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod elements; -pub mod markdown_service; diff --git a/services/src/post_service/mod.rs b/services/src/post_service/mod.rs deleted file mode 100644 index 7e7f418..0000000 --- a/services/src/post_service/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod post_card_size; -pub mod post_service; diff --git a/services/src/post_service/post_card_size.rs b/services/src/post_service/post_card_size.rs deleted file mode 100644 index 7dad864..0000000 --- a/services/src/post_service/post_card_size.rs +++ /dev/null @@ -1,15 +0,0 @@ -#[derive(PartialEq, Clone, Debug)] -pub enum PostCardSize { - Small, - Large, -} - -impl From for PostCardSize { - fn from(size: String) -> Self { - match size.as_str() { - "small" => PostCardSize::Small, - "large" => PostCardSize::Large, - _ => PostCardSize::Small, - } - } -} diff --git a/services/src/post_service/post_service.rs b/services/src/post_service/post_service.rs deleted file mode 100644 index fc61b83..0000000 --- a/services/src/post_service/post_service.rs +++ /dev/null @@ -1,139 +0,0 @@ -use crate::markdown_service::markdown_service::{MarkdownService, PostMetadata}; -use crate::posts::POSTS; -use chrono::NaiveDateTime; -use once_cell::sync::Lazy; -use regex::Regex; -use std::cmp::Ordering; - -#[derive(Clone, Debug, PartialEq)] -pub struct Post { - pub metadata: PostMetadata, - pub raw_content: &'static str, - pub desc: String, - pub modified_time: String, - pub filename: &'static str, -} - -pub struct PostService { - posts: Vec, -} - -#[derive(Clone)] -pub enum FilterTag { - All, - Tag(String), -} - -const MAX_DESC_LENGTH: usize = 600; - -pub fn find_char_boundary(s: &str, index: usize) -> usize { - if s.len() <= index { - return index; - } - - let mut new_index = index; - while !s.is_char_boundary(new_index) { - new_index += 1; - } - - new_index -} - -impl PostService { - pub fn new() -> PostService { - let posts = PostService::read_posts_into_memo(); - - PostService { posts } - } - - pub fn get_posts(&self) -> Vec { - self.posts.clone() - } - - pub fn trim_useless_symbol(content: &'static str) -> String { - Regex::new(r#"([\n]|```[^`]+```|`[^`]+`)"#) - .unwrap() - .replace_all(content, "") - .into_owned() - } - - pub fn find_post_by_filename(&self, filename: &str) -> Option { - self.posts - .clone() - .into_iter() - .find(|post| post.filename == filename) - } - - pub fn get_tags(&self) -> Vec { - let mut tags = vec![]; - - self.posts.iter().for_each(|post| { - let is_exist = tags.contains(&post.metadata.tag); - - if !is_exist { - tags.push(post.metadata.tag.clone()); - } - }); - - tags - } - - pub fn filter_post_by_tag(&self, tag: FilterTag) -> Vec { - let posts = self.posts.clone(); - - match tag { - FilterTag::All => posts, - FilterTag::Tag(tag) => posts - .into_iter() - .filter(|post| post.metadata.tag == tag) - .collect::>(), - } - } - - fn read_posts_into_memo() -> Vec { - let mut posts = POSTS - .clone() - .into_iter() - .map(|post| { - let markdown_service = MarkdownService::new(post.content.to_string()); - let metadata = markdown_service.extract_metadata().expect( - "Please make sure the post has metadata which declare using block syntax.", - ); - let parsed_content = PostService::trim_useless_symbol(post.content); - let parsed_content_length = parsed_content.len(); - let slice_desc_length = if parsed_content_length > MAX_DESC_LENGTH { - MAX_DESC_LENGTH - } else { - parsed_content_length - }; - let desc = parsed_content[..find_char_boundary(&parsed_content, slice_desc_length)] - .to_string(); - let modified_secs = (post.modified_time / 1000) as i64; - let modified_time = NaiveDateTime::from_timestamp(modified_secs, 0); - let modified_time = modified_time.format("%a, %b %e %Y").to_string(); - - Post { - metadata, - raw_content: post.content, - desc, - modified_time, - filename: post.filename, - } - }) - .collect::>(); - - posts.sort_by(|a, b| { - if a.metadata.pined { - Ordering::Less - } else if b.metadata.pined { - Ordering::Greater - } else { - a.modified_time.cmp(&b.modified_time) - } - }); - - posts - } -} - -pub static POST_SERVICE: Lazy = Lazy::new(|| PostService::new()); diff --git a/services/src/posts.rs b/services/src/posts.rs deleted file mode 100644 index dfcd684..0000000 --- a/services/src/posts.rs +++ /dev/null @@ -1,27 +0,0 @@ -use std::*; - -#[derive(Clone)] -pub struct PostFile { - pub content: &'static str, - pub modified_time: u128, - pub filename: &'static str -} - -pub static POSTS: [PostFile; 3] = [ - PostFile { - content: include_str!("../../posts/mlog_2022-10-26.md"), - modified_time: 1666755490770, - filename: "mlog_2022-10-26" -}, -PostFile { - content: include_str!("../../posts/build_blog.md"), - modified_time: 1654609915763, - filename: "build_blog" -}, -PostFile { - content: include_str!("../../posts/add_links.md"), - modified_time: 1654855511389, - filename: "add_links" -}, - -]; diff --git a/services/src/projects_service/mod.rs b/services/src/projects_service/mod.rs deleted file mode 100644 index 60a9696..0000000 --- a/services/src/projects_service/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod projects_service; diff --git a/services/src/projects_service/projects.json b/services/src/projects_service/projects.json deleted file mode 100644 index ae63cc0..0000000 --- a/services/src/projects_service/projects.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "projects": [ - { - "name": "zzhack", - "addr": "https://github.com/zzhack-stack/zzhack", - "desc": "⚙️ 🦀️ My personal blog site" - }, - { - "name": "Archie", - "addr": "https://github.com/wizardoc/archie", - "desc": "GraphQL API server for @wizardoc/wizard, powered by @gin, Git-based document management, timely notification for document changes." - }, - { - "name": "Chelli", - "addr": "https://github.com/mistricky/chelli", - "desc": "🖖🏻Chelli is a function set that implements command-line parsing using Zsh Shell. And the name of Chelli means that combination Shell and CLI." - }, - { - "name": "Arrow Cache", - "addr": "https://github.com/wizardoc/arrow-cache", - "desc": "🏹️Cache mechanism base on Web Worker, help us build high performance webApp." - }, - { - "name": "postcss-relaxed-unit", - "addr": "https://github.com/mistricky/postcss-relaxed-unit", - "desc": "🍮Postcss-relaxed-unit is a postcss plugin for unit tranformation and make write css easier with custom unit." - }, - { - "name": "Fence", - "addr": "https://github.com/wizardoc/fence", - "desc": "🐟Fence is an operator-based request library that provide a serises of APIs to help you more easily manipulate the request flow" - }, - { - "name": "Wizard", - "addr": "https://github.com/wizardoc/wizard", - "desc": "🍳Wizardoc is a WEBAPP for managing documents and knowledge." - }, - { - "name": "Flat", - "addr": "https://github.com/mistlang/Flat", - "desc": "🍪Lightweight MVVM framework base on TypeScript." - } - ] -} diff --git a/services/src/projects_service/projects_service.rs b/services/src/projects_service/projects_service.rs deleted file mode 100644 index 820d56a..0000000 --- a/services/src/projects_service/projects_service.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::post_service::post_service::{Post, POST_SERVICE}; -use once_cell::sync::Lazy; -use serde::Deserialize; -use serde_json; - -#[derive(Deserialize, Clone)] -pub struct RawProject { - pub name: String, - pub desc: String, - pub addr: String, - pub post: Option, -} - -#[derive(Clone, PartialEq)] -pub struct Project { - pub name: String, - pub desc: String, - pub addr: String, - pub post: Option, -} - -#[derive(Deserialize, Clone)] -pub struct ProjectsData { - pub projects: Vec, -} - -pub struct ProjectsService { - projects: Vec, -} - -impl ProjectsService { - pub fn new() -> ProjectsService { - let projects_data = include_str!("./projects.json"); - let projects_data: ProjectsData = serde_json::from_str(projects_data).unwrap(); - let projects = projects_data - .projects - .iter() - .map(|raw_project| { - let post = match raw_project.post.clone() { - Some(filename) => POST_SERVICE.find_post_by_filename(&filename).clone(), - None => None, - }; - - Project { - addr: raw_project.addr.clone(), - name: raw_project.name.clone(), - desc: raw_project.desc.clone(), - post, - } - }) - .collect(); - - ProjectsService { projects } - } - - pub fn get_projects(&self) -> Vec { - self.projects.clone() - } - - pub fn get_projects_by_odd_even(&self) -> (Vec, Vec) { - let mut even = vec![]; - let mut odd = vec![]; - - for (i, project) in self.projects.iter().enumerate() { - if (i + 1) % 2 == 0 { - even.push(project.clone()); - } else { - odd.push(project.clone()); - } - } - - (odd, even) - } -} - -pub static PROJECTS_SERVICE: Lazy = Lazy::new(|| ProjectsService::new()); diff --git a/services/src/theme_service/mod.rs b/services/src/theme_service/mod.rs deleted file mode 100644 index 20657a1..0000000 --- a/services/src/theme_service/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod theme; -pub mod theme_service; - -pub use theme::*; -pub use theme_service::*; diff --git a/services/src/theme_service/theme.rs b/services/src/theme_service/theme.rs deleted file mode 100644 index 44e6a69..0000000 --- a/services/src/theme_service/theme.rs +++ /dev/null @@ -1,33 +0,0 @@ -#[derive(Clone, PartialEq, Debug)] -pub enum Theme { - Dark, - Light, - Auto, -} - -impl Theme { - pub fn into_str(&self) -> &'static str { - match self { - Theme::Dark => "dark", - Theme::Light => "light", - Theme::Auto => "auto", - } - } - - pub fn from(theme: &str) -> Theme { - match theme { - "dark" => Theme::Dark, - "light" => Theme::Light, - "auto" => Theme::Auto, - _ => Theme::Dark, - } - } - - pub fn eq(&self, other: &Theme) -> bool { - self.clone().into_str() == other.clone().into_str() - } - - pub fn nq(&self, other: &Theme) -> bool { - !self.eq(other) - } -} diff --git a/services/src/theme_service/theme_service.rs b/services/src/theme_service/theme_service.rs deleted file mode 100644 index f0897f4..0000000 --- a/services/src/theme_service/theme_service.rs +++ /dev/null @@ -1,94 +0,0 @@ -use super::theme::Theme; -use once_cell::sync::Lazy; - -pub struct ThemePool { - pub theme: Theme, -} - -pub struct ThemeService { - pool: &'static mut Lazy, -} - -const THEME_KEY: &'static str = "THEME"; - -impl ThemeService { - pub fn from_storage() -> ThemeService { - let instance = unsafe { - ThemeService { - pool: &mut THEME_POOL, - } - }; - - instance.mount_on_dom(); - instance - } - - pub fn mount_on_dom(&self) { - let window = web_sys::window().unwrap(); - let document = window.document().unwrap(); - let body = document.body().unwrap(); - - body.set_class_name(self.pool.theme.clone().into_str()); - } - pub fn get_theme_follow_os_from_storage() -> Theme { - let is_dark_theme = web_sys::window() - .unwrap() - .match_media("(prefers-color-scheme: dark)") - .unwrap() - .unwrap() - .matches(); - - if is_dark_theme { - Theme::Dark - } else { - Theme::Light - } - } - - pub fn convert_auto_to_actually_theme(theme: Theme) -> Theme { - match theme { - Theme::Auto => ThemeService::get_theme_follow_os_from_storage(), - _ => theme, - } - } - - pub fn get_theme_from_storage() -> Theme { - let local_storage = web_sys::window().unwrap().local_storage().unwrap().unwrap(); - - match local_storage.get_item(THEME_KEY).unwrap() { - Some(theme_literal) => Theme::from(&theme_literal), - None => { - local_storage - .set_item(THEME_KEY, Theme::Dark.into_str()) - .unwrap(); - - Theme::Dark - } - } - } - - pub fn get_theme(&self) -> &Theme { - &self.pool.theme - } - - fn update_theme(&mut self) { - let theme = ThemeService::get_theme_from_storage(); - self.pool.theme = ThemeService::convert_auto_to_actually_theme(theme); - self.mount_on_dom(); - } - - pub fn set_theme(&mut self, theme: &Theme) { - let local_storage = web_sys::window().unwrap().local_storage().unwrap().unwrap(); - let stringify_theme = theme.clone().into_str(); - - local_storage.set_item(THEME_KEY, stringify_theme).unwrap(); - self.update_theme(); - } -} - -pub static mut THEME_POOL: Lazy = Lazy::new(|| { - let theme = - ThemeService::convert_auto_to_actually_theme(ThemeService::get_theme_from_storage()); - - ThemePool { theme } -}); diff --git a/router/Cargo.toml b/shared/Cargo.toml similarity index 80% rename from router/Cargo.toml rename to shared/Cargo.toml index 51bf0f0..8a1f6b8 100644 --- a/router/Cargo.toml +++ b/shared/Cargo.toml @@ -1,9 +1,9 @@ [package] -name = "router" +name = "shared" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -yew-router = "0.16.0" +serde = "1.0.204" diff --git a/shared/src/lib.rs b/shared/src/lib.rs new file mode 100644 index 0000000..f409c95 --- /dev/null +++ b/shared/src/lib.rs @@ -0,0 +1,4 @@ +pub mod links; +pub mod post; +pub mod site_config; +pub mod tag; diff --git a/shared/src/links.rs b/shared/src/links.rs new file mode 100644 index 0000000..96f9e23 --- /dev/null +++ b/shared/src/links.rs @@ -0,0 +1,14 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize)] +pub struct LinksConfig { + pub links: Vec, +} + +#[derive(Deserialize, Serialize)] +pub struct Link { + pub name: String, + pub description: String, + pub url: String, + pub avatar: String, +} diff --git a/shared/src/post.rs b/shared/src/post.rs new file mode 100644 index 0000000..98f7776 --- /dev/null +++ b/shared/src/post.rs @@ -0,0 +1,43 @@ +use serde::{Deserialize, Serialize}; + +use crate::tag::Tag; + +#[derive(Serialize, Deserialize, Clone)] +pub struct PostWithTags { + pub post: P, + pub tags: Vec, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct PaginationPostsRes { + pub page_limit: u64, + pub page: u64, + pub total: u64, + pub has_next: bool, + pub posts: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct Post { + pub id: i32, + pub path: String, + pub spoiler: String, + pub title: String, + pub created_at: String, + pub updated_at: String, + pub tags: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct PostDetail { + pub id: i32, + pub content: String, + pub created_at: String, + pub updated_at: String, + pub title: String, + pub tags: Vec, +} + +pub trait IntoPost

: Sized { + fn into_post>(self, tags: Vec) -> P; +} diff --git a/shared/src/site_config.rs b/shared/src/site_config.rs new file mode 100644 index 0000000..574bf8f --- /dev/null +++ b/shared/src/site_config.rs @@ -0,0 +1,27 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)] +pub struct Config { + pub root: RootConfig, + pub server: ServerConfig, + pub nav: Vec, +} + +#[derive(PartialEq, Serialize, Eq, Debug, Deserialize, Clone)] +pub struct NavConfig { + pub text: String, + pub url: String, +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)] +pub struct RootConfig { + pub language: String, + pub posts_folder_name: String, + pub dynamic_pages_folder_name: String, +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, Clone)] +pub struct ServerConfig { + pub dev_port: usize, + pub prod_port: usize, +} diff --git a/shared/src/tag.rs b/shared/src/tag.rs new file mode 100644 index 0000000..1bb4901 --- /dev/null +++ b/shared/src/tag.rs @@ -0,0 +1,8 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +pub struct Tag { + pub id: i32, + pub text: String, + pub color: Option, +} diff --git a/site.config.toml b/site.config.toml new file mode 100644 index 0000000..4b423b5 --- /dev/null +++ b/site.config.toml @@ -0,0 +1,44 @@ +[root] +posts_folder_name = "posts" + +# The default language of the site +# en | zh +language = "en" + +# dynamic_pages_folder_name is really useful in most cases, +# you can put markdown files under the root/dynamic_pages_folder_name folder, +# if zzhack found markdown file in root/dynamic_pages_folder_name, +# these files will be rendered as html pages, that allows users can visit these pages +# through path of these markdown files, for instance: +# +# dynamic_pages/ +# |- about/ +# |- me.md +# +# then you can visit the about page through /pages/about/me +dynamic_pages_folder_name = "dynamic_pages" + +[[nav]] +text = "Home" +url = "/" + +[[nav]] +text = "Posts" +url = "/posts" + +[[nav]] +text = "About me" +url = "/pages/about/me" + +[server] +dev_port = 8000 +prod_port = 8080 + +# If you want to use different client to create/update post, +# you need to set password and secret_key to create access_token +# Otherwise you don't need to modify this section +[api] +password = "" + +# The secret_key used for encoding and decoding the JWT tokens +secret_key = "" diff --git a/site_config/Cargo.toml b/site_config/Cargo.toml new file mode 100644 index 0000000..c48e98d --- /dev/null +++ b/site_config/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "site_config" +version = "0.0.1" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = "1.0.204" +serde_derive = "1.0.204" +toml = "0.8.14" +shared = { path = "../shared" } +cached = "0.52.0" +wasm-bindgen = "0.2.92" diff --git a/site_config/src/lib.rs b/site_config/src/lib.rs new file mode 100644 index 0000000..ca28476 --- /dev/null +++ b/site_config/src/lib.rs @@ -0,0 +1,9 @@ +use cached::proc_macro::cached; +use shared::site_config::Config; + +static SITE_CONFIG_STRING: &'static str = include_str!("../../site.config.toml"); + +#[cached] +pub fn get_site_config() -> Config { + toml::from_str::(&SITE_CONFIG_STRING).expect("Wrong format of site.config.toml") +} diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..113ada5 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,40 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + darkMode: "selector", + content: ["app/**/*.{rs,html}", "entry/**/*.{rs,html}"], + theme: { + extend: { + borderRadius: { + "4xl": "30px", + "5xl": "38px", + "6xl": "42px", + }, + }, + colors: { + orange: { + DEFAULT: "#FFBE76", + }, + gray: { + 500: "rgba(156,156,156,0.5)", + 200: "rgba(217,217,217,0.2)", + 900: "rgba(138,143,152,0.9)", + 450: "rgba(175,177,180,0.45)", + 600: "rgba(28,28,32,0.6)", + 100: "rgba(128,132,140,0.1)", + }, + black: { + dark: "#F7F8F8", + DEFAULT: "#1e1e1e", + 400: "rgba(0,0,0,0.4)", + 900: "#0A0A0A", + }, + white: { + dark: "#2B2B2F", + DEFAULT: "#ffffff", + 200: "#E9E9E9", + 600: "rgba(255,255,255,0.6)", + }, + }, + }, + plugins: [require("@tailwindcss/typography")], +}; diff --git a/templates/md_parser.template b/templates/md_parser.template deleted file mode 100644 index 6723ae0..0000000 --- a/templates/md_parser.template +++ /dev/null @@ -1,12 +0,0 @@ -use std::*; - -#[derive(Clone)] -pub struct PostFile { - pub content: &'static str, - pub modified_time: u128, - pub filename: &'static str -} - -pub static POSTS: [PostFile; {{TRAVERSE_COUNT}}] = [ - {{TEMPLATE}} -]; diff --git a/templates/md_parser_iteration.template b/templates/md_parser_iteration.template deleted file mode 100644 index 11309b0..0000000 --- a/templates/md_parser_iteration.template +++ /dev/null @@ -1,5 +0,0 @@ -PostFile { - content: include_str!("../../posts/{{STEM}}.md"), - modified_time: {{MODIFY_TIME}}, - filename: "{{STEM}}" -}, diff --git a/ui/.gitignore b/ui/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/ui/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/ui/Cargo.toml b/ui/Cargo.toml deleted file mode 100644 index 8b8a878..0000000 --- a/ui/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "ui" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -yew = "0.19.3" -stylist = {version = "0.10", features = ["yew_integration"]} -utils = {path = "../utils"} -global = {path = "../global"} -services = {path = "../services"} -web-sys = { version = "0.3", features = ["HtmlMetaElement", "Document", "Element", "DocumentFragment", "HtmlTemplateElement", "MediaQueryList"] } -wasm-logger = "0.2.0" -log = "0.4.17" -material-yew = { git = "https://github.com/hamza1311/material-yew", features = ["full"] } -urlencoding = "2.1.0" -router = {path = "../router"} -yew-router = "0.16.0" diff --git a/ui/src/common/contact.rs b/ui/src/common/contact.rs deleted file mode 100644 index 153678d..0000000 --- a/ui/src/common/contact.rs +++ /dev/null @@ -1,87 +0,0 @@ -use super::image::Icon; -use crate::link::Link; -use utils::use_style; -use yew::prelude::*; - -#[derive(Properties, Clone, PartialEq)] -pub struct ContactsProps { - pub source: Vec, -} - -#[derive(Clone, PartialEq)] -pub enum ContactType { - Twitter, - GitHub, - Email, - WeChat, - Discord, -} - -impl From<&ContactType> for &'static str { - fn from(contact: &ContactType) -> &'static str { - match contact { - ContactType::Discord => "discord.svg", - ContactType::Twitter => "twitter.svg", - ContactType::WeChat => "wechat.svg", - ContactType::Email => "gmail.svg", - ContactType::GitHub => "github.svg", - } - } -} - -impl ContactType { - pub fn into_lnk(&self) -> &'static str { - match self { - ContactType::Discord => "#", - ContactType::Twitter => "https://twitter.com/_mistricky", - ContactType::WeChat => "#", - ContactType::Email => "mailto:mist.zzh@gmail.com", - ContactType::GitHub => "https://github.com/mistricky", - } - } -} - -impl ContactType { - fn has_theme(&self) -> bool { - match self { - ContactType::GitHub => true, - _ => false, - } - } - - fn into_size(&self) -> i32 { - match self { - ContactType::GitHub => 30, - _ => 32, - } - } -} - -#[function_component(Contacts)] -pub fn contacts(props: &ContactsProps) -> Html { - let style = use_style!( - r" - display: flex; - align-items: center; - " - ); - - let render_contacts = props - .source - .iter() - .map(|contact| { - let source: &'static str = contact.into(); - html! { - - - - } - }) - .collect::(); - - html! { -

- {render_contacts} -
- } -} diff --git a/ui/src/common/container.rs b/ui/src/common/container.rs deleted file mode 100644 index 978ca18..0000000 --- a/ui/src/common/container.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::use_style; -use yew::prelude::*; - -#[derive(Properties, Clone, Debug, PartialEq)] -pub struct ContainerProps { - pub children: Children, -} - -#[function_component(Container)] -pub fn container(props: &ContainerProps) -> Html { - let style = use_style!( - r" - width: 100%; - display: flex; - justify-content: center; - - .container-box { - width: 816px; - box-sizing: border-box; - } - - @media (max-width: 600px) { - .container-box { - min-width: 100%; - width: 100%; - padding: 0 22px; - } - } - " - ); - - html! { -
-
- {props.children.clone()} -
-
- } -} diff --git a/ui/src/common/footer.rs b/ui/src/common/footer.rs deleted file mode 100644 index 9b92552..0000000 --- a/ui/src/common/footer.rs +++ /dev/null @@ -1,58 +0,0 @@ -use crate::contact::{ContactType, Contacts}; -use crate::container::Container; -use utils::use_style; -use yew::prelude::*; - -#[function_component(Footer)] -pub fn footer() -> Html { - let style = use_style!( - r" - width: 100%; - background: var(--base-color); - padding-bottom: 18px; - - .contacts { - margin-top: 31px; - display: flex; - align-items: center; - justify-content: space-between; - } - - .copyright { - display: flex; - justify-content: center; - } - - .text { - font-size: 14px; - } - - @media (max-width: 600px) { - .contacts { - flex-direction: column; - height: auto; - padding-bottom: 30px; - } - } - " - ); - - html! { -
- -
-
-
-
{"Powered by Rust & Yew"}
-
{"Illustration by Icons 8 from Ouch!"}
-
-
- -
-
- -
-
-
- } -} diff --git a/ui/src/common/gradient_title.rs b/ui/src/common/gradient_title.rs deleted file mode 100644 index 179d5b8..0000000 --- a/ui/src/common/gradient_title.rs +++ /dev/null @@ -1,44 +0,0 @@ -use stylist::style; -use yew::prelude::*; - -#[derive(Properties, Clone, PartialEq)] -pub struct GradientTitleProps { - pub children: Children, -} - -#[function_component(GradientTitle)] -pub fn gradient_title(props: &GradientTitleProps) -> Html { - let style = style!( - r" - display: flex; - margin-bottom: 21px; - - .gradient-title__content { - font-size: 29px; - position: relative; - } - - .gradient-title__content::before { - content: ''; - width: 120%; - display: block; - height: 21px; - border-radius: 100px; - background: linear-gradient(90deg, #FF4AA8 0%, #F95D66 22%, #FE9C76 100%); - position: absolute; - z-index: -1; - bottom: 0px; - left: -5%; - } - " - ) - .unwrap(); - - html! { -
-
- { props.children.clone() } -
-
- } -} diff --git a/ui/src/common/header/drawer.rs b/ui/src/common/header/drawer.rs deleted file mode 100644 index a4d71f7..0000000 --- a/ui/src/common/header/drawer.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::header::drawer_item::DrawerItem; -use stylist::{css, yew::styled_component}; -use utils::html::render_with_insert_node; -use yew::prelude::*; - -#[derive(Properties, Clone, PartialEq)] -pub struct DrawerProps { - pub is_open: UseStateHandle, - pub children: ChildrenWithProps, -} - -#[styled_component(Drawer)] -pub fn drawer(props: &DrawerProps) -> Html { - let style = css!( - r" - width: 100%; - transition: all 0.2s; - position: absolute; - left: 0; - background: var(--underlay-color); - z-index: 5; - transform: translateY(${translate}); - - .drawer-items { - position: relative; - z-index: 5; - background: var(--underlay-color); - padding: 0 20px; - } - - .line { - width: 100%; - height: 1px; - background: var(--shallow-gray); - } - ", - translate = if *props.is_open { "56px" } else { "-100%" }, - ); - let mask_style = css!( - r" - top: 0; - left: 0; - position: fixed; - width: 100%; - height: 100vh; - background: var(--mask-color); - z-index: 4; - display: ${display}; - ", - display = if *props.is_open { "block" } else { "none" } - ); - let render_nodes = props - .children - .iter() - .map(|item| html! {{item}}) - .collect::>(); - let handle_mask_click = { - let is_open = props.is_open.clone(); - - Callback::from(move |_| is_open.set(!*is_open)) - }; - - html! { - <> -
-
-
- {render_with_insert_node(&render_nodes, &html! { -
- })} -
-
- - } -} diff --git a/ui/src/common/header/drawer_item.rs b/ui/src/common/header/drawer_item.rs deleted file mode 100644 index b317c03..0000000 --- a/ui/src/common/header/drawer_item.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::link::Link; -use router::RootRoutes; -use utils::use_style; -use yew::prelude::*; - -#[derive(Properties, Clone, PartialEq)] -pub struct DrawerProps { - pub lnk: RootRoutes, - pub children: Children, -} - -#[function_component(DrawerItem)] -pub fn drawer_item(props: &DrawerProps) -> Html { - let style = use_style!( - r" - padding: 12px 0; - " - ); - - html! { -
- {props.children.clone()} -
- } -} diff --git a/ui/src/common/header/header.rs b/ui/src/common/header/header.rs deleted file mode 100644 index 33f2965..0000000 --- a/ui/src/common/header/header.rs +++ /dev/null @@ -1,146 +0,0 @@ -use crate::contact::ContactType; -use crate::container::Container; -use crate::header::drawer::Drawer; -use crate::header::drawer_item::DrawerItem; -use crate::image::{Icon, ThemeImage}; -use crate::link::Link; -use crate::modal::{modal::Modal, modal_content::ModalContent}; -use crate::theme_selector::ThemeSelector; -use router::RootRoutes; -use utils::resource::with_assets; -use utils::theme::only_render_on_mobile; -use utils::use_style; -use yew::prelude::*; - -#[function_component(Header)] -pub fn header() -> Html { - let style = use_style!( - r" - width: 100%; - - .wrapper { - height: 56px; - justify-content: space-between; - } - - .wrapper, .tabs, .left, .right, .setting-icon { - display: flex; - align-items: center; - } - - .left img { - height: 23px; - display: flex; - } - - .tabs { - margin-left: 88px; - } - - .tabs a { - font-size: 14px; - margin: 0 15px; - } - - .setting-icon { - margin-right: 19px; - } - - .header { - height:56px; - width: 100%; - background: var(--base-color); - position: relative; - z-index: 6; - } - - @media (max-width: 600px) { - .tabs { - display: none; - } - } - " - ); - let control_theme_style = use_style!( - r" - display: flex; - flex-direction: column; - align-items: center; - - .control-theme__text { - width: 100%; - text-align: left; - font-size: 14px; - color: var(--sub-text-color); - } - - .control-theme__img { - width: 126px; - } - - @media (max-width: 600px) { - .control-theme__text { - font-size: 12px; - } - } - " - ); - let is_open_drawer_handle = use_state_eq(|| false); - let is_open_theme_modal = use_state_eq(|| false); - let handle_drawer_click = { - let is_open_drawer_handle = is_open_drawer_handle.clone(); - - Callback::from(move |_| is_open_drawer_handle.set(!*is_open_drawer_handle)) - }; - let handle_setting_click = { - let is_open_theme_modal = is_open_theme_modal.clone(); - - Callback::from(move |_| is_open_theme_modal.set(!*is_open_theme_modal)) - }; - - html! { -
- - {"Posts"} - {"Projects"} - {"About"} - {"Links"} - -
- -
-
- - - -
- {"Posts"} - {"Projects"} - {"About"} - {"Links"} -
-
-
- - - - - {only_render_on_mobile(html! { - - })} -
-
-
-
- - - -
-

{"你可以在任何地方通过开关随时修改你的主题"}

- -
-
-
-
- } -} diff --git a/ui/src/common/header/mod.rs b/ui/src/common/header/mod.rs deleted file mode 100644 index f7e3457..0000000 --- a/ui/src/common/header/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod drawer; -pub mod drawer_item; -pub mod header; -pub use header::Header; diff --git a/ui/src/common/image.rs b/ui/src/common/image.rs deleted file mode 100644 index 616ba57..0000000 --- a/ui/src/common/image.rs +++ /dev/null @@ -1,108 +0,0 @@ -use global::theme_context::ThemeContext; -use material_yew::MatIconButton; -use stylist::{style, yew::styled_component}; -use utils::resource::{with_assets, with_assets_by_theme}; -use utils::theme::with_reactive_source; -use yew::prelude::*; - -#[derive(Properties, Clone, PartialEq)] -pub struct ImageProps { - pub source: &'static str, -} - -#[derive(Properties, Clone, PartialEq)] -pub struct ThemeImageProps { - #[prop_or(false)] - pub is_reactive: bool, - pub source: &'static str, - #[prop_or(String::from(""))] - pub style: String, -} - -#[derive(Properties, Clone, PartialEq)] -pub struct IconProps { - pub source: &'static str, - #[prop_or(true)] - pub has_theme: bool, - pub size: i32, - #[prop_or(String::from(""))] - pub style: String, - #[prop_or_default] - pub onclick: Option>, -} - -#[derive(Properties, Clone, PartialEq)] -pub struct BaseImageProps { - pub source: &'static str, - #[prop_or(false)] - pub has_theme: bool, - #[prop_or(false)] - pub is_reactive: bool, - #[prop_or(String::from(""))] - pub style: String, -} - -#[function_component(BaseImage)] -pub fn base_image(props: &BaseImageProps) -> Html { - let theme_ctx = use_context::().unwrap(); - let source = if props.is_reactive { - with_reactive_source(props.source.to_string()) - } else { - props.source.to_string() - }; - let source = if props.has_theme { - with_assets_by_theme(&source, &theme_ctx.theme) - } else { - with_assets(&source) - }; - - html! { - - } -} - -#[function_component(Image)] -pub fn image(props: &ImageProps) -> Html { - html! { - - } -} - -#[function_component(ThemeImage)] -pub fn theme_image(props: &ThemeImageProps) -> Html { - html! { - - } -} - -#[styled_component(Icon)] -pub fn icon(props: &IconProps) -> Html { - let style = style!( - r" - width: ${size}px; - height: ${size}px; - ", - size = props.size, - ) - .unwrap(); - let style = style.get_class_name(); - let wrapper_style = style!( - r" - --mdc-icon-size: ${size}px; - ", - size = props.size, - ) - .unwrap(); - let onclick_callback = match props.onclick.clone() { - Some(callback) => callback, - None => Callback::noop(), - }; - - html! { -
- - - -
- } -} diff --git a/ui/src/common/layout.rs b/ui/src/common/layout.rs deleted file mode 100644 index a73f4ba..0000000 --- a/ui/src/common/layout.rs +++ /dev/null @@ -1,54 +0,0 @@ -use super::{footer::Footer, header::Header}; -use crate::common::switch::ThemeSwitchBar; -use crate::container::Container; -use utils::use_style; -use yew::prelude::*; - -#[derive(Properties, PartialEq)] -pub struct BaseLayoutProps { - pub children: Children, -} - -#[function_component(BaseLayout)] -pub fn base_layout(props: &BaseLayoutProps) -> Html { - let style = use_style!( - r" - width: 100%; - height: 100%; - position: relative; - - .theme-switch-bar { - position: absolute; - right: -118px; - top: 63px; - } - - .page-outlet { - min-height: calc(100vh - 100px); - } - - @media (max-width: 600px) { - .theme-switch-bar { - position: static; - } - } - " - ); - - html! { - <> -
- -
-
- -
-
- { props.children.clone() } -
-
-
-