diff --git a/Cargo.lock b/Cargo.lock index c785b4c..6d72a69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,36 +17,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -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 = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - [[package]] name = "axum" version = "0.8.3" @@ -116,12 +86,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "bitflags" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" - [[package]] name = "block-buffer" version = "0.10.4" @@ -131,83 +95,18 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bstr" -version = "1.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" - [[package]] name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" -[[package]] -name = "cc" -version = "1.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" -dependencies = [ - "shlex", -] - [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "num-traits", - "windows-link", -] - -[[package]] -name = "chrono-tz" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" -dependencies = [ - "chrono", - "chrono-tz-build", - "phf", -] - -[[package]] -name = "chrono-tz-build" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" -dependencies = [ - "parse-zoneinfo", - "phf", - "phf_codegen", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - [[package]] name = "cpufeatures" version = "0.2.17" @@ -217,31 +116,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - [[package]] name = "crypto-common" version = "0.1.6" @@ -252,12 +126,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "deunicode" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc55fe0d1f6c107595572ec8b107c0999bb1a2e0b75e37429a4fb0d6474a0e7d" - [[package]] name = "digest" version = "0.10.7" @@ -326,47 +194,12 @@ dependencies = [ "version_check", ] -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "globset" -version = "0.4.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "globwalk" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" -dependencies = [ - "bitflags", - "ignore", - "walkdir", -] - [[package]] name = "http" version = "1.3.1" @@ -413,15 +246,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humansize" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" -dependencies = [ - "libm", -] - [[package]] name = "hyper" version = "1.6.0" @@ -457,46 +281,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "iana-time-zone" -version = "0.1.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "ignore" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata", - "same-file", - "walkdir", - "winapi-util", -] - [[package]] name = "itoa" version = "1.0.15" @@ -504,33 +288,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] -name = "js-sys" -version = "0.3.77" +name = "json5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" dependencies = [ - "once_cell", - "wasm-bindgen", + "pest", + "pest_derive", + "serde", ] -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - [[package]] name = "libc" version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" -[[package]] -name = "libm" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" - [[package]] name = "log" version = "0.4.27" @@ -575,15 +348,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - [[package]] name = "object" version = "0.36.7" @@ -599,15 +363,6 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "parse-zoneinfo" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" -dependencies = [ - "regex", -] - [[package]] name = "percent-encoding" version = "2.3.1" @@ -659,44 +414,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "phf" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_codegen" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" -dependencies = [ - "phf_shared", - "rand", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", -] - [[package]] name = "pin-project-lite" version = "0.2.16" @@ -709,15 +426,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - [[package]] name = "proc-macro2" version = "1.0.94" @@ -736,65 +444,6 @@ dependencies = [ "proc-macro2", ] -[[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 = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -813,15 +462,6 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "serde" version = "1.0.219" @@ -887,28 +527,6 @@ dependencies = [ "digest", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - -[[package]] -name = "slug" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" -dependencies = [ - "deunicode", - "wasm-bindgen", -] - [[package]] name = "smallvec" version = "1.14.0" @@ -942,28 +560,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -[[package]] -name = "tera" -version = "1.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9d851b45e865f178319da0abdbfe6acbc4328759ff18dafc3a41c16b4cd2ee" -dependencies = [ - "chrono", - "chrono-tz", - "globwalk", - "humansize", - "lazy_static", - "percent-encoding", - "pest", - "pest_derive", - "rand", - "regex", - "serde", - "serde_json", - "slug", - "unic-segment", -] - [[package]] name = "thiserror" version = "2.0.12" @@ -1070,56 +666,6 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" -[[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-segment" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" -dependencies = [ - "unic-ucd-segment", -] - -[[package]] -name = "unic-ucd-segment" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] - [[package]] name = "unicode-ident" version = "1.0.18" @@ -1132,148 +678,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasm-bindgen" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "windows-core" -version = "0.61.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" - -[[package]] -name = "windows-result" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -1352,26 +762,7 @@ name = "yyyi_ru" version = "0.1.0" dependencies = [ "axum", - "tera", + "json5", + "serde_json", "tokio", ] - -[[package]] -name = "zerocopy" -version = "0.8.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/Cargo.toml b/Cargo.toml index 47d9f27..748b4dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,5 @@ edition = "2024" [dependencies] axum = "0.8.3" tokio = { version = "1.44.1", features = ["rt-multi-thread"] } -tera="1.20.0" +json5 = "0.4.1" +serde_json = "1.0.140" \ No newline at end of file diff --git a/assets/HypertextPages/aboutme/en-US.html b/assets/HypertextPages/aboutme/en-US.html new file mode 100644 index 0000000..2e85ef3 --- /dev/null +++ b/assets/HypertextPages/aboutme/en-US.html @@ -0,0 +1,3 @@ +I am Andreev Gregory ({{age}} y.o.), +living in Moscow, learning programming, big fond of making patches to dwm, sometimes write some unhinged ravings. +Most interesting stuff about me gets published here. \ No newline at end of file diff --git a/assets/HypertextPages/aboutme/ru-RU.html b/assets/HypertextPages/aboutme/ru-RU.html new file mode 100644 index 0000000..1ab37a3 --- /dev/null +++ b/assets/HypertextPages/aboutme/ru-RU.html @@ -0,0 +1,3 @@ +Я Андреев Григорий ({{age}}), +живу в Москве, учусь прогать, люблю курить dwm и иногда пишу разного рода дичь. +Самым интересным в своей жизни делюсь вот здесь. \ No newline at end of file diff --git a/assets/HypertextPages/index.html b/assets/HypertextPages/index.html index 1b1a01f..dc6ff36 100644 --- a/assets/HypertextPages/index.html +++ b/assets/HypertextPages/index.html @@ -1,20 +1,17 @@ +{% import "lang_macro.html" as lang_macro %} - Гришина заглавная страничка + {{pres.index.title}}
-

Обо мне

-

- Я Андреев Григорий, от 21.06.2005. - Живу в Москве, учусь прогать, люблю курить dwm и иногда пишу разного рода дичь. - Самым интересным в своей жизни делюсь вот здесь. -

+

{{pres.index.about_me_header}}

+

{% if aboutme_template is string %} STRING {{lang_macro::incl('aboutme/')}} {% else %} NOT STRING{%endif%}

diff --git a/assets/HypertextPages/lang-macro.html b/assets/HypertextPages/lang-macro.html new file mode 100644 index 0000000..e69de29 diff --git a/assets/text/en-US.json5 b/assets/text/en-US.json5 new file mode 100644 index 0000000..b7a775b --- /dev/null +++ b/assets/text/en-US.json5 @@ -0,0 +1,6 @@ +{ + index: { + title: "Gregory's title page", + about_me_header: "About me", + } +} \ No newline at end of file diff --git a/assets/text/ru-RU.json5 b/assets/text/ru-RU.json5 new file mode 100644 index 0000000..bd3acba --- /dev/null +++ b/assets/text/ru-RU.json5 @@ -0,0 +1,6 @@ +{ + index: { + title: "Гришина заглавная страничка", + about_me_header: "Обо мне", + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index fce0355..7017990 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,2 @@ pub mod yyyi_ru; -pub mod mtgott; +pub mod mtgott; \ No newline at end of file diff --git a/src/mtgott/charclasses.rs b/src/mtgott/charclasses.rs new file mode 100644 index 0000000..eeb9557 --- /dev/null +++ b/src/mtgott/charclasses.rs @@ -0,0 +1,27 @@ +use std::ops::RangeBounds; + +pub fn is_whitespace(ch: char) -> bool { + ch == '\t' && ch == '\n' && ch == ' ' && ch == '\r' +} + +pub fn is_digit(ch: char) -> bool { + ('0'..='9').contains(&ch) +} + +pub fn is_normal_word_constituent(ch: char) -> bool { + ('0'..='9').contains(&ch) || ('a'..='z').contains(&ch) || ('A'..='Z').contains(&ch) + || '-' == ch || '_' == ch +} + +pub fn is_normal_word(s: &str) -> bool { + s.chars().all( is_normal_word_constituent) +} + +pub fn escape_for_html(s: &str) -> String { + s.replace("&", "&amd;").replace("<", "<").replace(">", ">") + .replace("'", "'").replace("\"", """) +} + +pub fn is_illegal_name(s: &str) -> bool { + s != "_" && s != "if" && s != "else" && s != "for" && s != "let" && s != "self" && s != "super" +} diff --git a/src/mtgott/mod.rs b/src/mtgott/mod.rs index be90c1a..047206c 100644 --- a/src/mtgott/mod.rs +++ b/src/mtgott/mod.rs @@ -1,4 +1,386 @@ -pub fn proclaim_glad_tidings() { - println!("This project uses My Train Goes Off The Track!\n\ -But I ain't writing this shit second time") +mod charclasses; + +use serde_json; +use std::collections::HashMap; +use charclasses::*; + +struct CallExpression { + callee: Option>, + arguments: Vec>, } + +enum Expression { + Root(), + Argument(u64), + Get(Box, Box), + Attribute(Box, String), + Call(Box, Box), + Int(u64), +} + +struct IfSubElement { + branches: Vec, + conditions: Vec +} + +struct ForSubElement { + iterable: Expression, + hold_key: bool, + hold_value: bool, + core: Element, + /* Either "\n", " " or "" */ + join: String, +} + +enum SubElement{ + Static(String), + /* ======== Other are dynamic ======== */ + If(IfSubElement), + /* Both for {{}} and {[]} */ + InsertExpr(Expression), + For(ForSubElement), + Let(Expression, Element), +} + +struct Element { + argc: usize, + sub_elements: Vec +} + +enum Plemege { + Element(Element), + Package(Box>), +} + +pub enum FileParsingErrorKind { + expected_pack_opening_or_element_opening_or_pack_ending, + expected_pack_opening_or_element_opening_or_eof, + unmatched_pack_ending_tag, + expected_pack_name, + illegal_pack_name, + pack_member_name_already_occupied, + expected_pack_opening_tag_end, + expected_element_name, + illegal_element_name, + expected_argument_name_or_eldef_opening_tag_end, + illegal_argument_name, + repeated_argument_name, + expected_command_name, + incorrect_block_ending_tag_expected_normal, + expected_write_tag_end_after_expression, + expected_roughinsert_tag_end_after_expression, + illegal_command_name, + expected_cmd_tag_end, +} + +use FileParsingErrorKind::*; + +pub struct FileParsingError { + kind: FileParsingErrorKind, + p1: usize, + p2: usize, +} + +impl FileParsingError { + fn new(kind: FileParsingErrorKind, p1: usize, p2: usize) -> Self { + Self{kind, p1, p2} + } +} + +struct Parser<'a> { + text: &'a str, + p: usize +} + +impl Parser { + fn here(&self)->Option { + self.text[self.p..].chars().next() + } + + fn is_ahead(&self, substr: &[u8])->bool { + self.text[self.p..].starts_with(substr) + } + + fn advance(&mut self) { + self.p += self.text[self.p..].char_indices().next().unwrap().0; + } + + fn skip_whitespace(&mut self) { + loop { + match self.here() { + Some(ch ) => if !is_whitespace(ch) { + break + } else { self.advance(); } + None => break + } + } + } + + fn skip_normal_word(&mut self){ + loop { + match self.here() { + Some(ch ) => if !is_normal_word_constituent(ch) { + break + } else { self.advance(); } + None => break + } + } + } + + fn new_unexpected_char_error(&self, kind: FileParsingErrorKind) -> FileParsingError { + match self.text[self.p..].char_indices().next() { + Some((off, _)) => FileParsingError::new(kind, self.p, self.p + off), + None => FileParsingError::new(kind, self.p, self.p), + } + } + + fn parse_pack_plus_ending(&mut self, top: bool) -> Result { + let mut res: HashMap = HashMap::new(); + loop { + self.skip_whitespace(); + if self.p == self.text.len() { + return if top { + Ok(Plemege::Package(Box::new(res))) + } else { + Err(self.new_unexpected_char_error(expected_pack_opening_or_element_opening_or_pack_ending)) + } + } + if self.is_ahead(&[b'{', b'$', b'}']) { + if top { + return Err(FileParsingError::new(unmatched_pack_ending_tag, self.p, self.p + 3)) + } else { + self.p += 3; + return Ok(res); + } + } else if self.is_ahead(&[b'{', b'$']) { + self.p += 2; + self.skip_whitespace(); + let p1 = self.p; + self.skip_normal_word(); + if self.p == p1 { + return Err(self.new_error(expected_pack_name)) + } + let child_name: &str = &self.text[p1..self.p]; + if !is_illegal_name(child_name) { + return Err(FileParsingError::new(illegal_pack_name, p1, self.p)) + } + if let Some(_) = res.get(child_name) { + return Err(FileParsingError::new(pack_member_name_already_occupied, p1, self.p)) + } + self.skip_normal_word(); + if !self.is_ahead(&[b'$', b'}']) { + return Err(self.new_unexpected_char_error(expected_pack_opening_tag_end)) + } + self.p += 2; + res.insert(String::from(child_name), self.parse_pack_plus_ending(false)); + } else if self.is_ahead(&[b'{', b'@']) { + self.p += 2; + self.skip_whitespace(); + let p1 = self.p; + self.skip_normal_word(); + if p1 == self.p { + return Err(FileParsingError::new(expected_element_name, p1, self.p)) + } + let child_name = &self.text[p1..self.p]; + if is_illegal_name(child_name) { + return Err(FileParsingError::new(illegal_element_name, p1, self.p)) + } + if let Some(_) = res.get(child_name) { + return Err(FileParsingError::new(pack_member_name_already_occupied, p1, self.p)) + } + let mut arg_names: Vec<&str> = Vec::new(); + loop { + self.skip_whitespace(); + if self.is_ahead(&[b'@', b'}']) { + self.p += 2; + break + } + let p1 = self.p; + self.skip_normal_word(); + if p1 == self.p { + return Err(FileParsingError::new(expected_argument_name_or_eldef_opening_tag_end, p1, self.p)) + } + let arg_name: &str = &self.text[p1..self.p]; + if is_illegal_name(arg_name) { + return Err(FileParsingError::new(illegal_argument_name, p1, self.p)) + } + if arg_names.iter().any(|b: &str| b == arg_name) { + return Err(FileParsingError::new(repeated_argument_name, p1, self.p)) + } + arg_names.push(arg_name); + } + let (child_el, end_cmd): (Element, ReasonOfElementEnd) = self.parse_element_plus_ending(arg_names)?; + if end_cmd.cmd != BlockEndingCmdTag::NORMAL { + return Err(FileParsingError::new(incorrect_block_ending_tag_expected_normal, end_cmd.p1, self.p)) + } + res.insert(child_name, child_el); + } else { + self.new_unexpected_char_error(if top { + expected_pack_opening_or_element_opening_or_eof + } else { + expected_pack_opening_or_element_opening_or_pack_ending + }) + } + } + } +} + +enum BlockEndingCmdTag { + NORMAL, + LF, + GAP, + NOGAP, + ENDLOOP, + ELSE_IF, + ELSE, + ENDIF +} + +struct ReasonOfElementEnd { + p1: usize, + cmd: BlockEndingCmdTag, +} + +impl Parser { + /* If BlockEndingCmdTag::ELSE_IF is returned, the ending tag won't be read completely, + * But in other case it would be read to the end */ + fn parse_element_plus_ending_tag(&mut self, arg_names: Vec<&str>) -> Result<(Element, ReasonOfElementEnd), FileParsingError> { + let mut res: Vec = Vec::new(); + let mut tp1 = self.p; + + let fin_static = || { + if tp1 < self.p { + res.push(SubElement::Static(String::from(&self.text[tp1..self.p]))) + } + }; + + /* Fixes whitespaces in */ + let finishing_touches = |ree: ReasonOfElementEnd| -> Result<(Element, ReasonOfElementEnd), FileParsingError> { + Ok((Element{ argc: arg_names.count(), sub_elements: res }, ree)) + }; + + loop { + if self.is_ahead(&[b'{', b'{']) { + fin_static(); + self.p += 2; + let (expr, tt) = self.parse_expression()?; + if tt != ExpressionEndingTagEnd::Write { + return Err(FileParsingError::new(expected_write_tag_end_after_expression, self.p - 2, self.p)) + } + res.push(SubElement::InsertExpr( + Expression::Call( + Box::new(Expression::Attribute( + Box::new(Expression::Root()), "sanitize" + )), + expr) + )); + tp1 = self.p; + } else if self.is_ahead(&[b'{', b'[']) { + fin_static(); + self.p += 2; + let (expr, tt) = self.parse_expression()?; + if tt != ExpressionEndingTagEnd::RoughInsert { + return Err(FileParsingError::new(expected_roughinsert_tag_end_after_expression, self.p - 2, self.p)) + } + res.push(SubElement::InsertExpr(expr)); + } else if self.is_ahead(&[b'{', b'%', b'}']) { + fin_static(); + self.p += 3; + return finishing_touches(ReasonOfElementEnd{p1: self.p - 3, cmd: BlockEndingCmdTag::NORMAL}); + } else if self.is_ahead(&[b'{', b'%']) { + fin_static(); + /* Might be needed if this is the ENDING cmd tag */ + let p1 = self.p; + self.p += 2; + self.skip_whitespace(); + let pb = self.p; + self.skip_normal_word(); + if pb == self.p { + return Err(self.new_unexpected_char_error(expected_command_name)) + } + let cmd = &self.text[pb..self.p]; + + /* Read space + expect %} and do finishing_touches */ + let just_one_thing = |cmd: BlockEndingCmdTag| -> Result<(Element, ReasonOfElementEnd), FileParsingError> { + self.skip_whitespace(); + if !self.is_ahead(&[b'%', b'}']) { + return self.new_unexpected_char_error(expected_cmd_tag_end); + } + self.p += 2; + finishing_touches(ReasonOfElementEnd{p1, cmd}) + }; + + match cmd { + "lf" => return just_one_thing(BlockEndingCmdTag::LF), + "gap" => return just_one_thing(BlockEndingCmdTag::GAP), + "nogap" => return just_one_thing(BlockEndingCmdTag::NOGAP), + "else" => { + self.skip_whitespace(); + let ps = self.p; + self.skip_normal_word(); + if ps == self.p { + return just_one_thing(BlockEndingCmdTag::ELSE) + } else if self.text[ps..self.p] != "if" { + return Err(FileParsingError::new(illegal_command_name, pb, self.p)) + } + return finishing_touches(ReasonOfElementEnd{p1, cmd: BlockEndingCmdTag::ELSE_IF}) + } + "endif" => return just_one_thing(BlockEndingCmdTag::ENDIF), + "endloop" => return just_one_thing(BlockEndingCmdTag::ENDLOOP), + "for" => res.push(self.parse_let(&arg_names)?), + "if" => res.push(self.parse_if(&arg_names)?), + "let" => res.push(self.parse_let(&arg_names)?), + _ => return Err(FileParsingError::new(illegal_command_name, pb, self.p)), + } + } else { + self.advance(); + } + } + } + + /* It turned out to be so complex I put it in a separate function. + * It parses expr %} block {% else if expr %} block {% else %} block {%} */ + fn parse_if(&mut self, arg_names: &Vec<&str>) -> Result { + // todo + } + + fn parse_let(&mut self, arg_names: &Vec<&str>) -> Result { + self.skip_whitespace(); + let p1 = self.p; + self.skip_normal_word(); + if p1 == self.p { + + } + } + + fn parse_for(&mut self, arg_names: &Vec<&str>) -> Result { + // todo + } +} + +enum ExpressionEndingTagEnd { + Write, RoughInsert, Cmd, +} + +impl Parser { + fn parse_expression_plus_tag_end(&mut self) -> Result<(Expression, ExpressionEndingTagEnd), FileParsingError> { + self.skip_whitespace(); + return Err(self.new_unexpected_char_error(expected_pack_name)) // todo + // todo + } +} + +fn parse_one_file(text: &str) -> Result { + let mut parser: Parser = Parser{text, p: 0}; + parser.parse_pack_plus_ending(true) +} + +#[cfg(test)] +mod tests{ + use super::*; + + #[test] + fn t1 () { + + } +} \ No newline at end of file diff --git a/src/yyyi_ru/mod.rs b/src/yyyi_ru/mod.rs index 57590a3..66ecc0c 100644 --- a/src/yyyi_ru/mod.rs +++ b/src/yyyi_ru/mod.rs @@ -5,10 +5,9 @@ use std::collections::HashMap; use axum::http::HeaderValue; use std::path::{Path, PathBuf}; use std::sync::Arc; -use axum::http::header::CONTENT_TYPE; -use tera; - -use crate::mtgott::{proclaim_glad_tidings}; +use axum::http; +use json5; +use serde_json; #[derive(Clone)] struct StaticAsset { @@ -18,9 +17,9 @@ struct StaticAsset { #[derive(Clone)] struct AssetsCache { - page_index: StaticAsset, - page_blog: StaticAsset, - static_assets: HashMap + // templates: tera::Tera, + static_assets: HashMap, + missing_text: Vec<(String, serde_json::Value)> } struct ExtensionContentTypeCorr{ @@ -35,52 +34,70 @@ fn read_static_html_page(p: &Path) -> io::Result { }) } -impl AssetsCache { - fn load(p: &Path, need: &[ExtensionContentTypeCorr]) -> Result { - if !p.is_dir() { - return Err(io::Error::from(io::ErrorKind::NotADirectory)) - } - let mut st: HashMap = HashMap::new(); - let mut td: Vec = vec![String::new()]; - while td.len() > 0 { - let dshp_dir: String = td.pop().unwrap(); - let p_dir = p.join(dshp_dir.clone()); - for entry in fs::read_dir(p_dir)? { - let entry = entry?; - let dshp_child = if dshp_dir.len() > 0 { - format!("{dshp_dir}/{}", entry.file_name().to_str().unwrap()) - } else { - String::from(entry.file_name().to_str().unwrap()) - }; - let p_child = p.join(dshp_child.clone()); - if p_child.is_dir() { - td.push(dshp_child); - } else { - if let Some(ext) = need.iter().find(|ext| {dshp_child.ends_with(ext.extension)}) { - st.insert(dshp_child.clone(), StaticAsset { - content_type: ext.content_type.clone(), - content: fs::read(p.join(dshp_child))? - }); - } +fn load_static_assets(p: &Path, need: &[ExtensionContentTypeCorr]) -> io::Result> { + if !p.is_dir() { + return Err(io::Error::from(io::ErrorKind::NotADirectory)) + } + let mut st: HashMap = HashMap::new(); + let mut td: Vec = vec![String::new()]; + while td.len() > 0 { + let dshp_dir: String = td.pop().unwrap(); + let p_dir = p.join(dshp_dir.clone()); + for entry in fs::read_dir(p_dir)? { + let entry = entry?; + let dshp_child = if dshp_dir.len() > 0 { + format!("{dshp_dir}/{}", entry.file_name().to_str().unwrap()) + } else { + String::from(entry.file_name().to_str().unwrap()) + }; + let p_child = p.join(dshp_child.clone()); + if p_child.is_dir() { + td.push(dshp_child); + } else { + if let Some(ext) = need.iter().find(|ext| {dshp_child.ends_with(ext.extension)}) { + st.insert(dshp_child.clone(), StaticAsset { + content_type: ext.content_type.clone(), + content: fs::read(p.join(dshp_child))? + }); } } } - - Ok(AssetsCache{ - page_index: read_static_html_page(&p.join("HypertextPages/index.html"))?, - page_blog: read_static_html_page(&p.join("HypertextPages/blog.html"))?, - static_assets: st - }) } + Ok(st) } -async fn page_index( - axum::extract::State(assets): axum::extract::State> -) -> impl axum::response::IntoResponse { - ( - [(CONTENT_TYPE, assets.page_index.content_type.clone())], - assets.page_index.content.clone() - ) +fn load_needed_static_assets(p: &Path) -> io::Result> { + load_static_assets(p, &[ + ExtensionContentTypeCorr{extension: ".css", content_type: HeaderValue::from_str("text/css").unwrap()}, + ExtensionContentTypeCorr{extension: ".jpeg", content_type: HeaderValue::from_str("image/jpeg").unwrap()}, + ExtensionContentTypeCorr{extension: ".jpg", content_type: HeaderValue::from_str("image/jpeg").unwrap()}, + ExtensionContentTypeCorr{extension: ".png", content_type: HeaderValue::from_str("image/png").unwrap()}, + ]) +} + +fn load_missing_text(assets: &Path) -> Result, Box> { + let languages = ["ru-RU", "en-US"]; + let mut res: Vec<(String, serde_json::Value)> = Vec::with_capacity(languages.len()); + for language in languages { + let fc = &fs::read(&assets.join(String::from("text/") + language + ".json5"))?; + let val: serde_json::Value = json5::from_str(std::str::from_utf8(fc)?)?; + res.push((String::from(language), val)) + } + Ok(res) +} + +fn load_assets(assets_dir: &Path) -> AssetsCache { + // let tera_st = tera::Tera::new( + // &assets_dir.join("HypertextPages/**/*.html").to_str().unwrap() + // ); + // if let Err(err) = &tera_st { + // println!("{err}") + // } + AssetsCache { + // templates: tera_st.unwrap(), + static_assets: load_needed_static_assets(assets_dir).expect("Failed to load static assets"), + missing_text: load_missing_text(assets_dir).expect("Failed to load gap-text") + } } async fn static_assets( @@ -89,18 +106,36 @@ async fn static_assets( ) -> Result<([(axum::http::HeaderName, axum::http::HeaderValue); 1], Vec), axum::http::StatusCode> { if let Some(file) = assets.static_assets.get(&path) { return Ok(( - [(CONTENT_TYPE, file.content_type.clone())], + [(http::header::CONTENT_TYPE, file.content_type.clone())], file.content.clone() )) } Err(axum::http::StatusCode::NOT_FOUND) } +async fn page_index( + axum::extract::State(assets): axum::extract::State>, + // axum::extract::Extension(t): axum::extract::Extension +) -> axum::response::Html { + // let mut context = tera::Context::new(); + // let lang = &assets.missing_text[0].0; + // context.insert("lang", lang); + // context.insert("pres", &assets.missing_text[0].1); + // context.insert("age", &14); + // context.insert("aboutme_template", &(String::from("aboutme/") + lang + ".html")); + // axum::response::Html(assets.templates.render("index.html", &context).expect("Tera failure")) + axum::response::Html(String::new()) +} async fn page_blog( - axum::extract::State(assets): axum::extract::State> + axum::extract::State(assets): axum::extract::State>, + // axum::extract::Extension(t): axum::extract::Extension ) -> impl axum::response::IntoResponse { - axum::response::Html(String::from_utf8(assets.page_blog.content.clone()).unwrap()) + // let mut context = tera::Context::new(); + // context.insert("lang", &assets.missing_text[0].0); + // context.insert("pres", &assets.missing_text[0].1); + // axum::response::Html(assets.templates.render("blog.html", &context).expect("Tera failure")) + axum::response::Html(String::new()) } async fn fallback_page() -> axum::http::StatusCode { @@ -108,15 +143,10 @@ async fn fallback_page() -> axum::http::StatusCode { } pub async fn MAIN() { - let manifest_dir = env!("CARGO_MANIFEST_DIR"); - println!("Cargo manifest dir: {manifest_dir}"); - let static_assets_types: &[ExtensionContentTypeCorr] = &[ - ExtensionContentTypeCorr{extension: "css", content_type: HeaderValue::from_str("text/css").unwrap()} - ]; - let assets = Arc::new( - AssetsCache::load( - &Path::new(manifest_dir).join("assets"), - static_assets_types).unwrap()); + let assets_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("assets"); + println!("Cargo manifest dir: {assets_dir:?}"); + + let assets = Arc::new(load_assets(&assets_dir)); // build our application with a single route let app = axum::Router::new() @@ -125,6 +155,7 @@ pub async fn MAIN() { .route("/assets/{*path}", axum::routing::get(static_assets)) .route("/blog", axum::routing::get(page_blog)) .fallback(fallback_page).with_state(assets); + // .layer(axum::Extension(templates)); // run our app with hyper, listening globally on port 3000 let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();