diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d84f0bdd..661be3d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,8 @@ jobs: shell: bash run: | set -e - pip install utiles --find-links dist --force-reinstall + pip install click + pip install utiles --no-index --no-deps --find-links dist --force-reinstall pip install -r requirements/dev.txt pytest # - name: pytest @@ -86,10 +87,10 @@ jobs: shell: bash run: | set -e - pip install utiles --find-links dist --force-reinstall + pip install click + pip install utiles --no-index --no-deps --find-links dist --force-reinstall pip install -r requirements/dev.txt pytest - macos: runs-on: macos-latest strategy: @@ -116,7 +117,8 @@ jobs: shell: bash run: | set -e - pip install utiles --find-links dist --force-reinstall + pip install click + pip install utiles --no-index --no-deps --find-links dist --force-reinstall pip install -r requirements/dev.txt pytest diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 4cba6b1c..5d063b99 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -27,7 +27,7 @@ jobs: uses: PyO3/maturin-action@v1 with: target: ${{ matrix.target }} - args: --release --out dist --find-interpreter + args: --release --out dist --find-interpreter --verbose sccache: "true" manylinux: auto @@ -36,23 +36,26 @@ jobs: shell: bash run: | set -e - pip install utiles --find-links dist --force-reinstall + pip install click + pip install utiles --no-index --no-deps --find-links dist --force-reinstall pip install -r requirements/dev.txt pytest -# - name: pytest -# if: ${{ !startsWith(matrix.target, 'x86') && matrix.target != 'ppc64' }} -# uses: uraimo/run-on-arch-action@v2.5.0 -# with: -# arch: ${{ matrix.target }} -# distro: ubuntu22.04 -# githubToken: ${{ github.token }} -# install: | -# apt-get update -# apt-get install -y --no-install-recommends python3 python3-pip -# pip3 install -U pip pytest -# pip3 install -r requirements/dev.txt -# run: | -# set -e -# pip3 install utiles --find-links dist --force-reinstall -# pip3 install -r requirements/dev.txt + # - name: pytest + # if: ${{ !startsWith(matrix.target, 'x86') && matrix.target != 'ppc64' }} + # uses: uraimo/run-on-arch-action@v2.5.0 + # with: + # arch: ${{ matrix.target }} + # distro: ubuntu22.04 + # githubToken: ${{ github.token }} + # install: | + # apt-get update + # apt-get install -y --no-install-recommends python3 python3-pip + # pip3 install -U pip pytest + # pip3 install -r requirements/dev.txt + # run: | + # set -e + # pip install click + # pip3 install utiles --no-index --no-deps --find-links dist --force-reinstall + # pip3 install -r requirements/dev.txt + # pytest diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dc48f0c..105905f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,16 @@ -# 0.1.0 +# 0.2.0 (2023-11-10) + + - Converted cli to rust as an excerise in learning clap + - Moved old click cli to `utiles._legacy.cli` + - Added tilejson/tj command to rust cli to write out tilejson files for mbtiles + - Added meta command to rust cli to write out json of metadata table for mbtiles + +# 0.1.0 (2023-10-27) - Drop python 3.7 (was good knowing you) - Update pyo3 to 0.20.0 - Added rasterio/rio entry points ('utiles' and 'ut' alias bc why type `rio utiles` over `rio ut`) - # 0.0.2 - Added `__len__` to TilesGenerator for pbars diff --git a/Cargo.lock b/Cargo.lock index c8fe759d..d06f001a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,102 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "approx" version = "0.5.1" @@ -17,18 +113,141 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap-verbosity-flag" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5fdbb015d790cfb378aca82caf9cc52a38be96a7eecdb92f31b4366a8afc019" +dependencies = [ + "clap", + "log", +] + +[[package]] +name = "clap_builder" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fast_hilbert" version = "2.0.0" @@ -49,12 +268,56 @@ dependencies = [ "serde", ] +[[package]] +name = "geojson" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d728c1df1fbf328d74151efe6cb0586f79ee813346ea981add69bd22c9241b" +dependencies = [ + "geo-types", + "log", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown", +] + [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + [[package]] name = "indoc" version = "2.0.4" @@ -67,6 +330,12 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.149" @@ -79,6 +348,17 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "libsqlite3-sys" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "lock_api" version = "0.4.11" @@ -89,6 +369,27 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[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 = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + [[package]] name = "memoffset" version = "0.9.0" @@ -98,6 +399,36 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-traits" version = "0.2.17" @@ -108,12 +439,37 @@ dependencies = [ "libm", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking_lot" version = "0.12.1" @@ -137,6 +493,18 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + [[package]] name = "proc-macro2" version = "1.0.69" @@ -192,7 +560,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -204,7 +572,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -215,9 +583,12 @@ dependencies = [ "geo-types", "pyo3", "pyo3-build-config", + "rusqlite", "serde", "serde_json", "utiles", + "utiles-cli", + "utilesqlite", ] [[package]] @@ -235,9 +606,73 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rusqlite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" +dependencies = [ + "bitflags 2.4.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "ryu" version = "1.0.15" @@ -267,7 +702,7 @@ checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -281,12 +716,78 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_tuple" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f025b91216f15a2a32aa39669329a475733590a015835d1783549a56d09427" +dependencies = [ + "serde", + "serde_tuple_macros", +] + +[[package]] +name = "serde_tuple_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4076151d1a2b688e25aaf236997933c66e18b870d0369f8b248b8ab2be630d7e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "smallvec" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.38" @@ -304,6 +805,151 @@ version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tilejson" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a16212e5ea60f2b406835981338a1df9b206fab312e3470502d60f69e590a9c9" +dependencies = [ + "serde", + "serde_json", + "serde_tuple", +] + +[[package]] +name = "tokio" +version = "1.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "tokio-rusqlite" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aa66395f5ff117faee90c9458232c936405f9227ad902038000b74b3bc1feac" +dependencies = [ + "crossbeam-channel", + "rusqlite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -316,14 +962,111 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "utiles" version = "0.0.1" dependencies = [ + "anyhow", "fast_hilbert", "geo-types", + "geojson", + "log", + "serde", + "serde_json", + "thiserror", + "tilejson", + "tracing", +] + +[[package]] +name = "utiles-cli" +version = "0.0.1" +dependencies = [ + "clap", + "clap-verbosity-flag", + "rusqlite", "serde", "serde_json", + "tokio", + "tracing", + "tracing-subscriber", + "utiles", + "utilesqlite", +] + +[[package]] +name = "utilesqlite" +version = "0.1.0" +dependencies = [ + "rusqlite", + "serde", + "serde_json", + "tilejson", + "tokio", + "tokio-rusqlite", + "tracing", + "utiles", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", ] [[package]] @@ -382,3 +1125,23 @@ name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "zerocopy" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd66a62464e3ffd4e37bd09950c2b9dd6c4f8767380fabba0d523f9a775bc85a" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "255c4596d41e6916ced49cfafea18727b24d67878fa180ddfd69b9df34fd1726" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] diff --git a/Cargo.toml b/Cargo.toml index bf451e88..f9b44747 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,12 @@ crate-type = ["cdylib"] fast_hilbert = "2.0.0" geo-types = "0.7.9" pyo3 = "0.20.0" +rusqlite = "0.29.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.96" utiles = { path = "crates/utiles" } +utilesqlite = { path = "crates/utilesqlite" } +utiles-cli = { path = "crates/utiles-cli" } [profile.dev] opt-level = 0 diff --git a/crates/utiles-cli/Cargo.lock b/crates/utiles-cli/Cargo.lock new file mode 100644 index 00000000..0b35d7dd --- /dev/null +++ b/crates/utiles-cli/Cargo.lock @@ -0,0 +1,1042 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap-verbosity-flag" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5fdbb015d790cfb378aca82caf9cc52a38be96a7eecdb92f31b4366a8afc019" +dependencies = [ + "clap", + "log", +] + +[[package]] +name = "clap_builder" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fast_hilbert" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ec2bbe15af87954c739e236021f4411766c0f2b9c4a5f0b9317bcf6048ebf8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "geo-types" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9705398c5c7b26132e74513f4ee7c1d7dafd786004991b375c172be2be0eecaa" +dependencies = [ + "approx", + "num-traits", + "serde", +] + +[[package]] +name = "geojson" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d728c1df1fbf328d74151efe6cb0586f79ee813346ea981add69bd22c9241b" +dependencies = [ + "geo-types", + "log", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[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.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[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 = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +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", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[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 = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", +] + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rusqlite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" +dependencies = [ + "bitflags 2.4.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.190" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.190" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_tuple" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f025b91216f15a2a32aa39669329a475733590a015835d1783549a56d09427" +dependencies = [ + "serde", + "serde_tuple_macros", +] + +[[package]] +name = "serde_tuple_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4076151d1a2b688e25aaf236997933c66e18b870d0369f8b248b8ab2be630d7e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tilejson" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a16212e5ea60f2b406835981338a1df9b206fab312e3470502d60f69e590a9c9" +dependencies = [ + "serde", + "serde_json", + "serde_tuple", +] + +[[package]] +name = "tokio" +version = "1.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "tokio-rusqlite" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aa66395f5ff117faee90c9458232c936405f9227ad902038000b74b3bc1feac" +dependencies = [ + "crossbeam-channel", + "rusqlite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "utiles" +version = "0.0.1" +dependencies = [ + "anyhow", + "fast_hilbert", + "geo-types", + "geojson", + "serde", + "serde_json", + "thiserror", + "tilejson", + "tracing", +] + +[[package]] +name = "utiles-cli" +version = "0.0.1" +dependencies = [ + "clap", + "clap-verbosity-flag", + "rusqlite", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", + "utiles", + "utilesqlite", +] + +[[package]] +name = "utilesqlite" +version = "0.1.0" +dependencies = [ + "rusqlite", + "serde", + "serde_json", + "tilejson", + "tokio", + "tokio-rusqlite", + "tracing", + "utiles", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[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_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[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_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[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_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "zerocopy" +version = "0.7.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686b7e407015242119c33dab17b8f61ba6843534de936d94368856528eae4dcc" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020f3dfe25dfc38dfea49ce62d5d45ecdd7f0d8a724fa63eb36b6eba4ec76806" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] diff --git a/crates/utiles-cli/Cargo.toml b/crates/utiles-cli/Cargo.toml new file mode 100644 index 00000000..696427ad --- /dev/null +++ b/crates/utiles-cli/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "utiles-cli" +version = "0.0.1" +edition = "2021" + +[lib] +name = "utiles_cli" +path = "src/lib.rs" + +[[bin]] +name = "ut" +path = "src/bin.rs" + +[dependencies] +clap = { version = "4.4.7", features = ["derive"] } +clap-verbosity-flag = "2.1.0" +rusqlite = { version = "0.29.0", features = ["bundled", "vtab", "blob"] } +tokio = { version = "1.33.0", features = ["full"] } +tracing = { version = "0.1.40", features = [] } +tracing-subscriber = { version = "0.3.17", features = ["serde", "serde_json", "env-filter"] } +utiles = { path = "../utiles" } +utilesqlite = { path = "../utilesqlite" } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.96" \ No newline at end of file diff --git a/crates/utiles-cli/src/bin.rs b/crates/utiles-cli/src/bin.rs new file mode 100644 index 00000000..9c60ab64 --- /dev/null +++ b/crates/utiles-cli/src/bin.rs @@ -0,0 +1,8 @@ +mod cli; +mod shapes; +mod stdinterator; + +#[tokio::main] +async fn main() { + cli::cli_main(None, None) +} diff --git a/crates/utiles-cli/src/cli.rs b/crates/utiles-cli/src/cli.rs new file mode 100644 index 00000000..3e377a27 --- /dev/null +++ b/crates/utiles-cli/src/cli.rs @@ -0,0 +1,339 @@ +use std::io::{self, Write}; +use std::path::Path; + +use clap::{Parser, Subcommand}; +use tracing::{debug, error}; +use tracing_subscriber::EnvFilter; +use utiles::mbtiles::metadata_row::MbtilesMetadataRow; +use utiles::parsing::parse_bbox; +use utiles::tilejson::tilejson_stringify; +use utiles::tiles; +use utiles::zoom::ZoomOrZooms; +use utiles::{bounding_tile, Tile}; +use utilesqlite::mbtiles::Mbtiles; + +use crate::shapes::{shapes_main, ShapesArgs}; +use crate::stdinterator::StdInterator; + +/// A fictional versioning CLI +#[derive(Debug, Parser)] // requires `derive` feature +#[command(name = "ut")] +#[command(about = "utiles cli (rust)", long_about = None)] +pub struct Cli { + #[command(subcommand)] + command: Commands, + + // debug flag + #[arg( + long, + short, + global = true, + default_value = "false", + help = "debug mode" + )] + debug: bool, + // #[command(flatten , help="verbosity level (-v, -vv, -vvv, -vvvv)" )] + // verbose: Verbosity, +} + +#[derive(Debug, Parser)] // requires `derive` feature +pub struct InputAndSequenceArgs { + /// The remote to clone + #[arg(required = false)] + input: Option, + + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] + seq: bool, +} +// #[group(ArgGroup::new("projected").args(&["geographic", "mercator"]).required(false))] + +#[derive(Debug, Subcommand)] +pub enum Commands { + #[command(name = "lint", about = "Lint mbtiles file(s)", long_about = None)] + Lint { + #[arg(required = true)] + filepath: String, + + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] + fix: bool, + }, + #[command(name = "tilejson", visible_alias = "tj", about = "Echo tileson for mbtiles file(s)", long_about = None)] + Tilejson { + #[arg(required = true, help = "mbtiles filepath")] + filepath: String, + + #[arg(required = false, short, long, help = "compact json", action = clap::ArgAction::SetTrue)] + min: bool, + }, + + #[command(name = "meta", about = "Echo metadata (table) as json", long_about = None)] + Meta { + #[arg(required = true, help = "mbtiles filepath")] + filepath: String, + + #[arg(required = false, short, long, help = "compact json", action = clap::ArgAction::SetTrue)] + min: bool, + // #[arg(required = false, short, long, help= "compact json", action = clap::ArgAction::SetTrue)] + // raw: bool, + }, + + // ======================================================================== + // TILE CLI UTILS - MERCANTILE LIKE CLI + // ======================================================================== + #[command(name = "tiles", about = "Echo tiles of bbox", long_about = None)] + Tiles { + #[arg(required = true)] + zoom: u8, + + #[arg(required = false)] + input: Option, + + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] + seq: bool, + }, + + #[command(name = "quadkey", visible_alias = "qk", about = "Convert to/from quadkey(s)", long_about = None)] + Quadkey { + #[arg(required = false)] + input: Option, + }, + + #[command(name = "bounding-tile", about = "Echo the bounding tile of a lonlat/bbox/GeoJSON", long_about = None)] + BoundingTile { + #[arg(required = false)] + input: Option, + + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] + seq: bool, + }, + #[command(name = "neighbors", about = "Echo neighbors of tile(s)", long_about = None)] + Neighbors { + #[arg(required = false)] + input: Option, + + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] + seq: bool, + }, + + #[command(name = "parent", about = "Echo parent of tile(s)", long_about = None)] + Parent { + #[arg(required = false)] + input: Option, + + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] + seq: bool, + + #[arg(required = false, long, default_value = "1")] + depth: u8, + }, + #[command(name = "children", about = "Echo children of tile(s)", long_about = None)] + Children { + #[arg(required = false)] + input: Option, + + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] + seq: bool, + + #[arg(required = false, long, default_value = "1")] + depth: u8, + }, + + #[command(name = "shapes", about = "Echo shapes of tile(s) as GeoJSON", long_about = None)] + Shapes(ShapesArgs), +} + +fn stdin_filtered( + input: Option, +) -> Box>> { + let input_lines = StdInterator::new(input).unwrap(); + let filtered_lines = input_lines + .filter(|l| !l.is_err()) + .filter(|l| !l.as_ref().unwrap().is_empty()) + .filter(|l| l.as_ref().unwrap() != "\x1e"); + + Box::new(filtered_lines) as _ +} + +pub fn cli_main(argv: Option>, loop_fn: Option<&dyn Fn()>) { + // print args + let argv = match argv { + Some(argv) => argv, + None => std::env::args().collect::>(), + }; + let args = Cli::parse_from(&argv); + let filter = if args.debug { + EnvFilter::new("DEBUG") + } else { + EnvFilter::from_default_env() + }; + // Install the global collector configured based on the filter. + tracing_subscriber::fmt() + .with_env_filter(filter) + .with_writer(io::stderr) + .init(); + + debug!("args: {:?}", std::env::args().collect::>()); + debug!("argv: {:?}", argv); + + debug!("args: {:?}", args); + + match args.command { + Commands::Lint { filepath, fix } => { + println!("lint (fix -- {fix}): {filepath}"); + // throw not implemented error + panic!("not implemented (yet)") + } + Commands::Meta { filepath, min } => { + debug!("meta: {filepath}"); + // check that filepath exists and is file + let filepath = Path::new(&filepath); + if !filepath.exists() { + panic!("File does not exist: {}", filepath.display()); + } + if !filepath.is_file() { + panic!("Not a file: {filepath}", filepath = filepath.display()); + } + let mbtiles: Mbtiles = Mbtiles::from(filepath); + // let mbtiles = Mbtiles::from_filepath(&filepath).unwrap(); + let metadata_rows = mbtiles.metadata().unwrap(); + if min { + let s = + serde_json::to_string::>(&metadata_rows) + .unwrap(); + println!("{s}"); + } else { + let s = serde_json::to_string_pretty::>( + &metadata_rows, + ) + .unwrap(); + println!("{s}"); + } + } + + Commands::Tilejson { filepath, min } => { + debug!("tilejson: {filepath}"); + // check that filepath exists and is file + let filepath = Path::new(&filepath); + if !filepath.exists() { + panic!("File does not exist: {}", filepath.display()); + } + if !filepath.is_file() { + panic!("Not a file: {filepath}", filepath = filepath.display()); + } + let mbtiles: Mbtiles = Mbtiles::from(filepath); + // let mbtiles = Mbtiles::from_filepath(&filepath).unwrap(); + let tj = mbtiles.tilejson().unwrap(); + let s = tilejson_stringify(&tj, Option::from(!min)); + println!("{s}"); + } + + // mercantile cli like + Commands::Quadkey { input } => { + let lines = stdin_filtered(input); + for line in lines { + // if the line bgins w '[' treat as tile + // otherwise treat as quadkey + let lstr = line.unwrap(); + if lstr.starts_with('[') { + // treat as tile + let tile = Tile::from_json_arr(&lstr); + println!("{}", tile.quadkey()); + } else { + // treat as quadkey + let qk = lstr; + let tile = Tile::from_quadkey(&qk); + if tile.is_err() { + error!("Invalid quadkey: {qk}"); + println!("Invalid quadkey: {qk}"); + } else { + println!("{}", tile.unwrap().json_arr()); + } + } + } + } + Commands::BoundingTile { input, seq } => { + let lines = stdin_filtered(input); + let bboxes = lines.map(|l| { + let s = l.unwrap(); + debug!("l: {:?}", s); + parse_bbox(&s).unwrap() + }); + for bbox in bboxes { + let tile = bounding_tile(bbox, None); + // let tile = Tile::from_bbox(&bbox, zoom); + let rs = if seq { "\x1e\n" } else { "" }; + println!("{}{}", rs, tile.json_arr()); + } + } + Commands::Tiles { zoom, input, seq } => { + let lines = stdin_filtered(input); + let mut stdout = io::stdout(); + let tiles = lines + .map(|l| { + let s = l.unwrap(); + debug!("l: {:?}", s); + parse_bbox(&s).unwrap() + }) + .flat_map(|b| { + tiles((b.west, b.south, b.east, b.north), ZoomOrZooms::Zoom(zoom)) + }) + .enumerate(); + // let bboxes = lines + for (i, tile) in tiles { + let rs = if seq { "\x1e\n" } else { "" }; + // println!("{}{}", rs, tile.json_arr()); + writeln!(stdout, "{}{}", rs, tile.json_arr()).unwrap(); + // call loop_fn if it's defined every 1000 iterations for signal break + if i % 1024 == 0 { + stdout.flush().unwrap(); + if let Some(f) = loop_fn { + f(); + } + } + } + stdout.flush().unwrap(); + } + + Commands::Neighbors { input, seq } => { + let lines = stdin_filtered(input); + let tiles = lines.map(|l| Tile::from_json(&l.unwrap())); + for tile in tiles { + let neighbors = tile.neighbors(); + for neighbor in neighbors { + let rs = if seq { "\x1e\n" } else { "" }; + println!("{}{}", rs, neighbor.json_arr()); + } + } + } + + Commands::Children { input, seq, depth } => { + let lines = stdin_filtered(input); + let tiles = lines.map(|l| Tile::from_json(&l.unwrap())); + for tile in tiles { + let children = tile.children(Option::from(tile.z + depth)); + for child in children { + let rs = if seq { "\x1e\n" } else { "" }; + println!("{}{}", rs, child.json_arr()); + } + } + } + + Commands::Parent { input, seq, depth } => { + let lines = stdin_filtered(input); + let tiles = lines.map(|l| Tile::from_json(&l.unwrap())); + for tile in tiles { + let nup = tile.z as i32 - depth as i32; + if nup < 0 { + // error + panic!("depth must be less than or equal to tile zoom"); + } + let parent = tile.parent(Option::from(depth - 1)); + let rs = if seq { "\x1e\n" } else { "" }; + println!("{}{}", rs, parent.json_arr()); + } + } + Commands::Shapes(args) => { + shapes_main(args); + } + } +} diff --git a/crates/utiles-cli/src/lib.rs b/crates/utiles-cli/src/lib.rs new file mode 100644 index 00000000..97033478 --- /dev/null +++ b/crates/utiles-cli/src/lib.rs @@ -0,0 +1,3 @@ +pub mod cli; +pub mod shapes; +pub mod stdinterator; diff --git a/crates/utiles-cli/src/shapes.rs b/crates/utiles-cli/src/shapes.rs new file mode 100644 index 00000000..9c4c5d10 --- /dev/null +++ b/crates/utiles-cli/src/shapes.rs @@ -0,0 +1,228 @@ +use crate::stdinterator::StdInterator; +use clap::{Args, Parser}; +use serde_json::{Map, Value}; +use tracing::debug; +use utiles::projection::Projection; +use utiles::tile::FeatureOptions; +use utiles::Tile; + +// #[group(required = false, id="projected")] +#[derive(Args, Debug)] +#[group(required = false, multiple = false, id = "project")] +pub struct ShapesProject { + /// Output in geographic coordinates (the default). + #[arg(long , default_value = "false", conflicts_with = "mercator", action = clap::ArgAction::SetTrue)] + geographic: bool, + + /// Output in Web Mercator coordinates. + #[arg(long , default_value = "false", conflicts_with = "geographic", action = clap::ArgAction::SetTrue)] + mercator: bool, +} + +impl Default for ShapesProject { + fn default() -> Self { + ShapesProject { + geographic: true, + mercator: false, + } + } +} + +#[derive(Args, Debug)] +#[group(required = false, multiple = false, id = "output-mode")] +pub struct ShapesOutputMode { + #[arg(long , default_value = "false", conflicts_with = "bbox", action = clap::ArgAction::SetTrue)] + feature: bool, + + /// Output in Web Mercator coordinates. + #[arg(long , default_value = "false", conflicts_with = "feature", action = clap::ArgAction::SetTrue)] + bbox: bool, +} + +impl Default for ShapesOutputMode { + fn default() -> Self { + ShapesOutputMode { + feature: true, + bbox: false, + } + } +} + +#[derive(Debug, Parser)] // requires `derive` feature +#[command(name = "shapes", about = "echo shapes of tile(s) as GeoJSON", long_about = None)] +pub struct ShapesArgs { + #[arg(required = false)] + input: Option, + + #[arg(required = false, long, action = clap::ArgAction::SetTrue)] + seq: bool, + + /// Decimal precision of coordinates. + #[arg(long, value_parser)] + precision: Option, + + #[command(flatten)] + project: Option, + + #[command(flatten)] + output_mode: Option, + + /// Output as a GeoJSON feature collections. + #[arg(long, action)] + collect: bool, + + /// Write shape extents as ws-separated strings (default is False). + #[arg(long, default_value = "false", action = clap::ArgAction::SetTrue)] + extents: bool, + + /// Shift shape x and y values by a constant number. + #[arg(long, value_parser)] + buffer: Option, +} + +impl Default for ShapesArgs { + fn default() -> Self { + ShapesArgs { + input: None, + seq: false, + precision: None, + project: Some(ShapesProject::default()), + output_mode: Some(ShapesOutputMode::default()), + collect: false, + extents: false, + buffer: None, + } + } +} + +struct TileWithProperties { + tile: Tile, + id: Option, + properties: Option>, +} + +pub fn shapes_main(args: ShapesArgs) { + debug!("{:?}", args); + let input_lines = StdInterator::new(args.input).unwrap(); + let lines = input_lines + .filter(|l| !l.is_err()) + .filter(|l| !l.as_ref().unwrap().is_empty()) + .filter(|l| l.as_ref().unwrap() != "\x1e"); + let parsed_lines = lines.map(|l| { + let ln = l.unwrap(); + let val: Value = serde_json::from_str::(&ln).unwrap(); + let properties: Option> = match val["properties"].is_object() + { + true => { + let properties = val["properties"].as_object().unwrap().clone(); + Option::from(properties) + } + false => None, + }; + let id: Option = match val["id"].is_string() { + true => { + let id = val["id"].as_str().unwrap().to_string(); + Option::from(id) + } + false => None, + }; + let t = Tile::from(&val); + TileWithProperties { + tile: t, + id, + properties, + } + + // Tile::from_json_loose(&ln) + }); + let feature_options: FeatureOptions = FeatureOptions { + fid: None, + projection: match args.project { + Some(project) => match project { + ShapesProject { + geographic: true, + mercator: false, + } => Projection::Geographic, + ShapesProject { + geographic: false, + mercator: true, + } => Projection::Mercator, + _ => Projection::Geographic, + }, + None => Projection::Geographic, + }, + props: None, + buffer: args.buffer, + precision: args.precision, + }; + + if args.collect { + println!("{{"); + println!("\"type\": \"FeatureCollection\","); + println!("\"features\": ["); + } + let mut lons: Vec = Vec::new(); + let mut lats: Vec = Vec::new(); + let output_bbox = match args.output_mode { + Some(output_mode) => match output_mode { + ShapesOutputMode { + feature: true, + bbox: false, + } => false, + ShapesOutputMode { + feature: false, + bbox: true, + } => true, + _ => false, + }, + None => false, + }; + + let mut first = true; + + for tile_n_properties in parsed_lines { + let tile = tile_n_properties.tile; + let properties = tile_n_properties.properties; + let mut f = tile.feature(&feature_options).unwrap(); + + if let Some(properties) = properties { + f.properties.extend(properties); + } + if let Some(id) = tile_n_properties.id { + f.id = id; + } + lons.extend(f.bbox_lons()); + lats.extend(f.bbox_lats()); + if args.extents { + println!("{}", f.extents_string()); + } else if args.collect { + if !first { + println!(","); + } + println!("{}", f.to_json()); + first = false; + } else { + if args.seq { + println!("\x1e"); + } + if output_bbox { + println!("{}", f.bbox_json()); + } else { + println!("{}", f.to_json()); + } + } + } + if args.collect { + println!("]"); + println!("}}"); + } + // for tile in tiles { + // let f = tile.feature( + // &feature_options + // ).unwrap(); + // lons.extend(f.bbox_lons()); + // lats.extend(f.bbox_lats()); + // + // println!("{}", f.to_json()); + // } +} diff --git a/crates/utiles-cli/src/stdinterator.rs b/crates/utiles-cli/src/stdinterator.rs new file mode 100644 index 00000000..445df115 --- /dev/null +++ b/crates/utiles-cli/src/stdinterator.rs @@ -0,0 +1,68 @@ +use std::collections::VecDeque; +use std::io; +use std::io::BufRead; +use std::str::FromStr; +use tracing::debug; + +pub enum StdInteratorSource { + Args(VecDeque), + StdinRead(Box), +} + +pub struct StdInterator { + source: StdInteratorSource, +} + +impl StdInterator { + pub fn new(input: Option) -> io::Result { + let source = match input { + Some(file_content) => { + if file_content == "-" { + debug!("reading from stdin - got '-'"); + let reader = Box::new(io::BufReader::new(io::stdin())); + StdInteratorSource::StdinRead(reader) + } else { + debug!("reading from args"); + let args = file_content + .lines() // This assumes that each line is separated by '\n' + .map(std::string::ToString::to_string) + .collect::>(); + debug!("args: {:?}", args); + StdInteratorSource::Args(args) + } + } + None => { + let reader = Box::new(io::BufReader::new(io::stdin())); + debug!("reading from stdin - no args"); + StdInteratorSource::StdinRead(reader) + } + }; + Ok(Self { source }) + } +} + +impl FromStr for StdInterator { + type Err = io::Error; + + fn from_str(s: &str) -> Result { + Self::new(Some(s.to_string())) + } +} + +impl Iterator for StdInterator { + type Item = io::Result; + + fn next(&mut self) -> Option { + match &mut self.source { + StdInteratorSource::Args(content) => content.pop_front().map(Ok), + StdInteratorSource::StdinRead(reader) => { + let mut line = String::new(); + match reader.read_line(&mut line) { + Ok(0) => None, // EOF + Ok(_) => Some(Ok(line.trim_end().to_string())), + Err(e) => Some(Err(e)), + } + } + } + } +} diff --git a/crates/utiles-wasm/Cargo.lock b/crates/utiles-wasm/Cargo.lock new file mode 100644 index 00000000..7c757ca2 --- /dev/null +++ b/crates/utiles-wasm/Cargo.lock @@ -0,0 +1,425 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[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 = "fast_hilbert" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ec2bbe15af87954c739e236021f4411766c0f2b9c4a5f0b9317bcf6048ebf8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "geo-types" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9705398c5c7b26132e74513f4ee7c1d7dafd786004991b375c172be2be0eecaa" +dependencies = [ + "approx", + "num-traits", + "serde", +] + +[[package]] +name = "geojson" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d728c1df1fbf328d74151efe6cb0586f79ee813346ea981add69bd22c9241b" +dependencies = [ + "geo-types", + "log", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "serde" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_tuple" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f025b91216f15a2a32aa39669329a475733590a015835d1783549a56d09427" +dependencies = [ + "serde", + "serde_tuple_macros", +] + +[[package]] +name = "serde_tuple_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4076151d1a2b688e25aaf236997933c66e18b870d0369f8b248b8ab2be630d7e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "tilejson" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a16212e5ea60f2b406835981338a1df9b206fab312e3470502d60f69e590a9c9" +dependencies = [ + "serde", + "serde_json", + "serde_tuple", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utiles" +version = "0.0.1" +dependencies = [ + "anyhow", + "fast_hilbert", + "geo-types", + "geojson", + "serde", + "serde_json", + "thiserror", + "tilejson", + "tracing", +] + +[[package]] +name = "utiles-wasm" +version = "0.1.0" +dependencies = [ + "utiles", + "wasm-bindgen", + "wasm-bindgen-test", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6433b7c56db97397842c46b67e11873eda263170afeb3a2dc74a7cb370fee0d" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "493fcbab756bb764fa37e6bee8cec2dd709eb4273d06d0c282a5e74275ded735" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "web-sys" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +dependencies = [ + "js-sys", + "wasm-bindgen", +] diff --git a/crates/utiles-wasm/Cargo.toml b/crates/utiles-wasm/Cargo.toml new file mode 100644 index 00000000..83a0e2c8 --- /dev/null +++ b/crates/utiles-wasm/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "utiles-wasm" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +wasm-bindgen = "0.2.88" +wasm-bindgen-test = "0.3.38" +utiles = { path = "../utiles" } + +[lib] +crate-type = ["cdylib"] + diff --git a/crates/utiles-wasm/eslint.config.js b/crates/utiles-wasm/eslint.config.js new file mode 100644 index 00000000..8d8be00f --- /dev/null +++ b/crates/utiles-wasm/eslint.config.js @@ -0,0 +1,22 @@ +// eslint.config.js +import jsse from "@jsse/eslint-config"; + +export default [ + ...jsse({ + typescript: { + tsconfig: ["./tsconfig.json", "./tsconfig.eslint.json"], + }, + }), + { + files: ["schemas/**/*.schema.json"], + rules: { + "unicorn/filename-case": "off", + }, + }, + { + files: ["./src/dev/dev.ts", "./src/dev/dev.test.ts", "./src/scratch/**/*"], + rules: { + "@typescript-eslint/no-unused-vars": "off", + }, + }, +]; diff --git a/crates/utiles-wasm/package.json b/crates/utiles-wasm/package.json new file mode 100644 index 00000000..bcb87d11 --- /dev/null +++ b/crates/utiles-wasm/package.json @@ -0,0 +1,25 @@ +{ + "name": "utiles-wasm", + "type": "module", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "wasmpack": "wasm-pack build --target nodejs", + "wasmpack-r": "wasm-pack build --target nodejs --release" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@jsse/eslint-config": "^0.0.10", + "eslint": "^8.52.0", + "pmtiles": "^2.11.0", + "typescript": "^5.2.2", + "vite": "^4.5.0", + "vite-plugin-top-level-await": "^1.3.1", + "vite-plugin-wasm": "^3.2.2", + "vitest": "^0.34.6" + } +} diff --git a/crates/utiles-wasm/pnpm-lock.yaml b/crates/utiles-wasm/pnpm-lock.yaml new file mode 100644 index 00000000..4f668fc8 --- /dev/null +++ b/crates/utiles-wasm/pnpm-lock.yaml @@ -0,0 +1,3713 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: false + excludeLinksFromLockfile: false + +devDependencies: + '@jsse/eslint-config': + specifier: ^0.0.10 + version: 0.0.10(eslint@8.52.0)(typescript@5.2.2)(vitest@0.34.6) + eslint: + specifier: ^8.52.0 + version: 8.52.0 + pmtiles: + specifier: ^2.11.0 + version: 2.11.0 + typescript: + specifier: ^5.2.2 + version: 5.2.2 + vite: + specifier: ^4.5.0 + version: 4.5.0(@types/node@20.8.10) + vite-plugin-top-level-await: + specifier: ^1.3.1 + version: 1.3.1(vite@4.5.0) + vite-plugin-wasm: + specifier: ^3.2.2 + version: 3.2.2(vite@4.5.0) + vitest: + specifier: ^0.34.6 + version: 0.34.6 + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@alloc/quick-lru@5.2.0: + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + requiresBuild: true + dev: true + + /@antfu/eslint-define-config@1.23.0-2: + resolution: {integrity: sha512-LvxY21+ZhpuBf/aHeBUtGQhSEfad4PkNKXKvDOSvukaM3XVTfBhwmHX2EKwAsdq5DlfjbT3qqYyMiueBIO5iDQ==} + engines: {node: '>=18.0.0', npm: '>=9.0.0', pnpm: '>= 8.6.0'} + dev: true + + /@babel/code-frame@7.22.13: + resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.22.20 + chalk: 2.4.2 + dev: true + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/highlight@7.22.20: + resolution: {integrity: sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@es-joy/jsdoccomment@0.40.1: + resolution: {integrity: sha512-YORCdZSusAlBrFpZ77pJjc5r1bQs5caPWtAu+WWmiSo+8XaUzseapVrfAtiRFbQWnrBxxLLEwF6f6ZG/UgCQCg==} + engines: {node: '>=16'} + dependencies: + comment-parser: 1.4.0 + esquery: 1.5.0 + jsdoc-type-pratt-parser: 4.0.0 + dev: true + + /@esbuild/android-arm64@0.18.20: + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.52.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.52.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.2: + resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.23.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.52.0: + resolution: {integrity: sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@humanwhocodes/config-array@0.11.13: + resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 2.0.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@2.0.1: + resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} + dev: true + + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + requiresBuild: true + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.20 + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + requiresBuild: true + dev: true + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + requiresBuild: true + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.20: + resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} + requiresBuild: true + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@jsse/eslint-config@0.0.10(eslint@8.52.0)(typescript@5.2.2)(vitest@0.34.6): + resolution: {integrity: sha512-RW+afjxT68Pp1YHOv2wN9BAnGJ1Xj7Tuxe4sJQEZky73UT5Ghx3DJ3F1Z/0APt4lobMQJcIRENEPWZPKNjecZw==} + hasBin: true + peerDependencies: + eslint: '>=8.0.0' + dependencies: + '@antfu/eslint-define-config': 1.23.0-2 + '@eslint/js': 8.52.0 + '@stylistic/eslint-plugin': 0.1.1(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/eslint-plugin': 6.9.1(@typescript-eslint/parser@6.9.1)(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/parser': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + eslint: 8.52.0 + eslint-config-flat-gitignore: 0.1.1 + eslint-define-config: 1.24.1 + eslint-plugin-antfu: 1.0.1(eslint@8.52.0) + eslint-plugin-eslint-comments: 3.2.0(eslint@8.52.0) + eslint-plugin-i: 2.29.0(@typescript-eslint/parser@6.9.1)(eslint@8.52.0) + eslint-plugin-jsdoc: 46.8.2(eslint@8.52.0) + eslint-plugin-jsonc: 2.10.0(eslint@8.52.0) + eslint-plugin-markdown: 3.0.1(eslint@8.52.0) + eslint-plugin-n: 16.2.0(eslint@8.52.0) + eslint-plugin-no-only-tests: 3.1.0 + eslint-plugin-react: 7.33.2(eslint@8.52.0) + eslint-plugin-react-hooks: 4.6.0(eslint@8.52.0) + eslint-plugin-react-refresh: 0.4.4(eslint@8.52.0) + eslint-plugin-tailwindcss: 3.13.0(tailwindcss@3.3.5) + eslint-plugin-unicorn: 48.0.1(eslint@8.52.0) + eslint-plugin-unused-imports: 3.0.0(@typescript-eslint/eslint-plugin@6.9.1)(eslint@8.52.0) + eslint-plugin-vitest: 0.3.8(@typescript-eslint/eslint-plugin@6.9.1)(eslint@8.52.0)(typescript@5.2.2)(vitest@0.34.6) + eslint-plugin-yml: 1.10.0(eslint@8.52.0) + jsonc-eslint-parser: 2.4.0 + yaml-eslint-parser: 1.2.2 + optionalDependencies: + tailwindcss: 3.3.5 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + - ts-node + - typescript + - vitest + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + requiresBuild: true + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + requiresBuild: true + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + requiresBuild: true + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@rollup/plugin-virtual@3.0.2: + resolution: {integrity: sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dev: true + + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + + /@stylistic/eslint-plugin-js@0.1.1(eslint@8.52.0): + resolution: {integrity: sha512-gZbT/Sqz1viW+87YaGrzosjI54IIAwGuYE+5AgXO1C9zGMLpxDroyU+HpcqVSynN5Nihuaocp89UU49nCJh9KA==} + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) + acorn: 8.11.2 + escape-string-regexp: 4.0.0 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esutils: 2.0.3 + graphemer: 1.4.0 + transitivePeerDependencies: + - eslint + dev: true + + /@stylistic/eslint-plugin-jsx@0.1.1(eslint@8.52.0): + resolution: {integrity: sha512-36+iAWxGIAwGb2k7vS4S14NIt/2NVBCJSn3Q+T91t4MF1fWyaQFOoc5l6c4oW5UTzfr0vgMMcqsr8f8JJjqm0g==} + dependencies: + '@stylistic/eslint-plugin-js': 0.1.1(eslint@8.52.0) + estraverse: 5.3.0 + jsx-ast-utils: 3.3.5 + transitivePeerDependencies: + - eslint + dev: true + + /@stylistic/eslint-plugin-ts@0.1.1(eslint@8.52.0)(typescript@5.2.2): + resolution: {integrity: sha512-mJHYWzljqrDykOop60nyCAXRp4LLstfNQWXxubJFMOz5tlAoqd2Mex60TCa/fctPhaOd36f/lu1CuU9WgFh+DA==} + peerDependencies: + eslint: '*' + dependencies: + '@stylistic/eslint-plugin-js': 0.1.1(eslint@8.52.0) + '@typescript-eslint/scope-manager': 6.9.1 + '@typescript-eslint/type-utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + eslint: 8.52.0 + graphemer: 1.4.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@stylistic/eslint-plugin@0.1.1(eslint@8.52.0)(typescript@5.2.2): + resolution: {integrity: sha512-pOuT7q5XOain+YBExC7vHHSWEmvp2Z9/v1HTuYjy+EzvZkv7w8pXlC44qmO14G8A5uVaUp3CZtNzTvT2h4Ilaw==} + peerDependencies: + eslint: '*' + dependencies: + '@stylistic/eslint-plugin-js': 0.1.1(eslint@8.52.0) + '@stylistic/eslint-plugin-jsx': 0.1.1(eslint@8.52.0) + '@stylistic/eslint-plugin-ts': 0.1.1(eslint@8.52.0)(typescript@5.2.2) + eslint: 8.52.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@swc/core-darwin-arm64@1.3.95: + resolution: {integrity: sha512-VAuBAP3MNetO/yBIBzvorUXq7lUBwhfpJxYViSxyluMwtoQDhE/XWN598TWMwMl1ZuImb56d7eUsuFdjgY7pJw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-darwin-x64@1.3.95: + resolution: {integrity: sha512-20vF2rvUsN98zGLZc+dsEdHvLoCuiYq/1B+TDeE4oolgTFDmI1jKO+m44PzWjYtKGU9QR95sZ6r/uec0QC5O4Q==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm-gnueabihf@1.3.95: + resolution: {integrity: sha512-oEudEM8PST1MRNGs+zu0cx5i9uP8TsLE4/L9HHrS07Ck0RJ3DCj3O2fU832nmLe2QxnAGPwBpSO9FntLfOiWEQ==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-gnu@1.3.95: + resolution: {integrity: sha512-pIhFI+cuC1aYg+0NAPxwT/VRb32f2ia8oGxUjQR6aJg65gLkUYQzdwuUmpMtFR2WVf7WVFYxUnjo4UyMuyh3ng==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-musl@1.3.95: + resolution: {integrity: sha512-ZpbTr+QZDT4OPJfjPAmScqdKKaT+wGurvMU5AhxLaf85DuL8HwUwwlL0n1oLieLc47DwIJEMuKQkYhXMqmJHlg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-gnu@1.3.95: + resolution: {integrity: sha512-n9SuHEFtdfSJ+sHdNXNRuIOVprB8nbsz+08apKfdo4lEKq6IIPBBAk5kVhPhkjmg2dFVHVo4Tr/OHXM1tzWCCw==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-musl@1.3.95: + resolution: {integrity: sha512-L1JrVlsXU3LC0WwmVnMK9HrOT2uhHahAoPNMJnZQpc18a0paO9fqifPG8M/HjNRffMUXR199G/phJsf326UvVg==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-arm64-msvc@1.3.95: + resolution: {integrity: sha512-YaP4x/aZbUyNdqCBpC2zL8b8n58MEpOUpmOIZK6G1SxGi+2ENht7gs7+iXpWPc0sy7X3YPKmSWMAuui0h8lgAA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-ia32-msvc@1.3.95: + resolution: {integrity: sha512-w0u3HI916zT4BC/57gOd+AwAEjXeUlQbGJ9H4p/gzs1zkSHtoDQghVUNy3n/ZKp9KFod/95cA8mbVF9t1+6epQ==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-x64-msvc@1.3.95: + resolution: {integrity: sha512-5RGnMt0S6gg4Gc6QtPUJ3Qs9Un4sKqccEzgH/tj7V/DVTJwKdnBKxFZfgQ34OR2Zpz7zGOn889xwsFVXspVWNA==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core@1.3.95: + resolution: {integrity: sha512-PMrNeuqIusq9DPDooV3FfNEbZuTu5jKAc04N3Hm6Uk2Fl49cqElLFQ4xvl4qDmVDz97n3n/C1RE0/f6WyGPEiA==} + engines: {node: '>=10'} + requiresBuild: true + peerDependencies: + '@swc/helpers': ^0.5.0 + peerDependenciesMeta: + '@swc/helpers': + optional: true + dependencies: + '@swc/counter': 0.1.2 + '@swc/types': 0.1.5 + optionalDependencies: + '@swc/core-darwin-arm64': 1.3.95 + '@swc/core-darwin-x64': 1.3.95 + '@swc/core-linux-arm-gnueabihf': 1.3.95 + '@swc/core-linux-arm64-gnu': 1.3.95 + '@swc/core-linux-arm64-musl': 1.3.95 + '@swc/core-linux-x64-gnu': 1.3.95 + '@swc/core-linux-x64-musl': 1.3.95 + '@swc/core-win32-arm64-msvc': 1.3.95 + '@swc/core-win32-ia32-msvc': 1.3.95 + '@swc/core-win32-x64-msvc': 1.3.95 + dev: true + + /@swc/counter@0.1.2: + resolution: {integrity: sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==} + dev: true + + /@swc/types@0.1.5: + resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==} + dev: true + + /@types/chai-subset@1.3.4: + resolution: {integrity: sha512-CCWNXrJYSUIojZ1149ksLl3AN9cmZ5djf+yUoVVV+NuYrtydItQVlL2ZDqyC6M6O9LWRnVf8yYDxbXHO2TfQZg==} + dependencies: + '@types/chai': 4.3.9 + dev: true + + /@types/chai@4.3.9: + resolution: {integrity: sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==} + dev: true + + /@types/json-schema@7.0.14: + resolution: {integrity: sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==} + dev: true + + /@types/mdast@3.0.14: + resolution: {integrity: sha512-gVZ04PGgw1qLZKsnWnyFv4ORnaJ+DXLdHTVSFbU8yX6xZ34Bjg4Q32yPkmveUP1yItXReKfB0Aknlh/3zxTKAw==} + dependencies: + '@types/unist': 2.0.9 + dev: true + + /@types/node@20.8.10: + resolution: {integrity: sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/normalize-package-data@2.4.3: + resolution: {integrity: sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg==} + dev: true + + /@types/semver@7.5.4: + resolution: {integrity: sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==} + dev: true + + /@types/unist@2.0.9: + resolution: {integrity: sha512-zC0iXxAv1C1ERURduJueYzkzZ2zaGyc+P2c95hgkikHPr3z8EdUZOlgEQ5X0DRmwDZn+hekycQnoeiiRVrmilQ==} + dev: true + + /@typescript-eslint/eslint-plugin@6.9.1(@typescript-eslint/parser@6.9.1)(eslint@8.52.0)(typescript@5.2.2): + resolution: {integrity: sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/scope-manager': 6.9.1 + '@typescript-eslint/type-utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.9.1 + debug: 4.3.4 + eslint: 8.52.0 + graphemer: 1.4.0 + ignore: 5.2.4 + natural-compare: 1.4.0 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@6.9.1(eslint@8.52.0)(typescript@5.2.2): + resolution: {integrity: sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 6.9.1 + '@typescript-eslint/types': 6.9.1 + '@typescript-eslint/typescript-estree': 6.9.1(typescript@5.2.2) + '@typescript-eslint/visitor-keys': 6.9.1 + debug: 4.3.4 + eslint: 8.52.0 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@6.9.1: + resolution: {integrity: sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.9.1 + '@typescript-eslint/visitor-keys': 6.9.1 + dev: true + + /@typescript-eslint/type-utils@6.9.1(eslint@8.52.0)(typescript@5.2.2): + resolution: {integrity: sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 6.9.1(typescript@5.2.2) + '@typescript-eslint/utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + debug: 4.3.4 + eslint: 8.52.0 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@6.9.1: + resolution: {integrity: sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: true + + /@typescript-eslint/typescript-estree@6.9.1(typescript@5.2.2): + resolution: {integrity: sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 6.9.1 + '@typescript-eslint/visitor-keys': 6.9.1 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@6.9.1(eslint@8.52.0)(typescript@5.2.2): + resolution: {integrity: sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) + '@types/json-schema': 7.0.14 + '@types/semver': 7.5.4 + '@typescript-eslint/scope-manager': 6.9.1 + '@typescript-eslint/types': 6.9.1 + '@typescript-eslint/typescript-estree': 6.9.1(typescript@5.2.2) + eslint: 8.52.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@6.9.1: + resolution: {integrity: sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 6.9.1 + eslint-visitor-keys: 3.4.3 + dev: true + + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: true + + /@vitest/expect@0.34.6: + resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} + dependencies: + '@vitest/spy': 0.34.6 + '@vitest/utils': 0.34.6 + chai: 4.3.10 + dev: true + + /@vitest/runner@0.34.6: + resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} + dependencies: + '@vitest/utils': 0.34.6 + p-limit: 4.0.0 + pathe: 1.1.1 + dev: true + + /@vitest/snapshot@0.34.6: + resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} + dependencies: + magic-string: 0.30.5 + pathe: 1.1.1 + pretty-format: 29.7.0 + dev: true + + /@vitest/spy@0.34.6: + resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} + dependencies: + tinyspy: 2.2.0 + dev: true + + /@vitest/utils@0.34.6: + resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} + dependencies: + diff-sequences: 29.6.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + dev: true + + /acorn-jsx@5.3.2(acorn@8.11.2): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.11.2 + dev: true + + /acorn-walk@8.3.0: + resolution: {integrity: sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn@8.11.2: + resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + requiresBuild: true + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + requiresBuild: true + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /are-docs-informative@0.0.2: + resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} + engines: {node: '>=14'} + dev: true + + /arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + requiresBuild: true + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-buffer-byte-length@1.0.0: + resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + dependencies: + call-bind: 1.0.5 + is-array-buffer: 3.0.2 + dev: true + + /array-includes@3.1.7: + resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + is-string: 1.0.7 + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.tosorted@1.1.2: + resolution: {integrity: sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 + get-intrinsic: 1.2.2 + dev: true + + /arraybuffer.prototype.slice@1.0.2: + resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + is-array-buffer: 3.0.2 + is-shared-array-buffer: 1.0.2 + dev: true + + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + + /asynciterator.prototype@1.0.0: + resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==} + dependencies: + has-symbols: 1.0.3 + dev: true + + /available-typed-arrays@1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + requiresBuild: true + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + fill-range: 7.0.1 + dev: true + + /builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + dev: true + + /builtins@5.0.1: + resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==} + dependencies: + semver: 7.5.4 + dev: true + + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true + + /call-bind@1.0.5: + resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} + dependencies: + function-bind: 1.1.2 + get-intrinsic: 1.2.2 + set-function-length: 1.1.1 + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + requiresBuild: true + dev: true + + /chai@4.3.10: + resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.3 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /character-entities-legacy@1.1.4: + resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + dev: true + + /character-entities@1.2.4: + resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + dev: true + + /character-reference-invalid@1.1.4: + resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} + dev: true + + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + dependencies: + get-func-name: 2.0.2 + dev: true + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + requiresBuild: true + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + 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 + dev: true + + /ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + dev: true + + /clean-regexp@1.0.0: + resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} + engines: {node: '>=4'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + requiresBuild: true + dev: true + + /comment-parser@1.4.0: + resolution: {integrity: sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw==} + engines: {node: '>= 12.0.0'} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + requiresBuild: true + dev: true + + /debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /deep-eql@4.1.3: + resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /define-data-property@1.1.1: + resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: true + + /define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + has-property-descriptors: 1.0.1 + object-keys: 1.1.1 + dev: true + + /didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + requiresBuild: true + dev: true + + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + requiresBuild: true + dev: true + + /doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /es-abstract@1.22.3: + resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + arraybuffer.prototype.slice: 1.0.2 + available-typed-arrays: 1.0.5 + call-bind: 1.0.5 + es-set-tostringtag: 2.0.2 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.2 + get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + internal-slot: 1.0.6 + is-array-buffer: 3.0.2 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-typed-array: 1.1.12 + is-weakref: 1.0.2 + object-inspect: 1.13.1 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.5.1 + safe-array-concat: 1.0.1 + safe-regex-test: 1.0.0 + string.prototype.trim: 1.2.8 + string.prototype.trimend: 1.0.7 + string.prototype.trimstart: 1.0.7 + typed-array-buffer: 1.0.0 + typed-array-byte-length: 1.0.0 + typed-array-byte-offset: 1.0.0 + typed-array-length: 1.0.4 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.13 + dev: true + + /es-iterator-helpers@1.0.15: + resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==} + dependencies: + asynciterator.prototype: 1.0.0 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-set-tostringtag: 2.0.2 + function-bind: 1.1.2 + get-intrinsic: 1.2.2 + globalthis: 1.0.3 + has-property-descriptors: 1.0.1 + has-proto: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.6 + iterator.prototype: 1.1.2 + safe-array-concat: 1.0.1 + dev: true + + /es-set-tostringtag@2.0.2: + resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + has-tostringtag: 1.0.0 + hasown: 2.0.0 + dev: true + + /es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + dependencies: + hasown: 2.0.0 + dev: true + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: true + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-compat-utils@0.1.2(eslint@8.52.0): + resolution: {integrity: sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + eslint: 8.52.0 + dev: true + + /eslint-config-flat-gitignore@0.1.1: + resolution: {integrity: sha512-ysq0QpN63+uaxE67U0g0HeCweIpv8Ztp7yvm0nYiM2TBalRIG6KQLO5J6lAz2gkA8KVis/QsJppe+BR5VigtWQ==} + dependencies: + parse-gitignore: 2.0.0 + dev: true + + /eslint-define-config@1.24.1: + resolution: {integrity: sha512-o36vBhPSWyIQlHoMqGhhcGmOOm2A2ccBVIdLTG/AWdm9YmjpsLpf+5ntf9LlHR6dduLREgxtGwvwPwSt7vnXJg==} + engines: {node: '>=18.0.0', npm: '>=9.0.0', pnpm: '>= 8.6.0'} + dev: true + + /eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + dependencies: + debug: 3.2.7 + is-core-module: 2.13.1 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.9.1)(eslint-import-resolver-node@0.3.9)(eslint@8.52.0): + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + debug: 3.2.7 + eslint: 8.52.0 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-antfu@1.0.1(eslint@8.52.0): + resolution: {integrity: sha512-NL89JeF73mMwPMaLALLizOk7m+WboEJn4QB+GKRufG3a7jm4fpnkxZPPd2gu8X81ad9eUo3y+RsZ9TczjPFdQQ==} + peerDependencies: + eslint: '*' + dependencies: + eslint: 8.52.0 + dev: true + + /eslint-plugin-es-x@7.3.0(eslint@8.52.0): + resolution: {integrity: sha512-W9zIs+k00I/I13+Bdkl/zG1MEO07G97XjUSQuH117w620SJ6bHtLUmoMvkGA2oYnI/gNdr+G7BONLyYnFaLLEQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '>=8' + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) + '@eslint-community/regexpp': 4.10.0 + eslint: 8.52.0 + dev: true + + /eslint-plugin-eslint-comments@3.2.0(eslint@8.52.0): + resolution: {integrity: sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==} + engines: {node: '>=6.5.0'} + peerDependencies: + eslint: '>=4.19.1' + dependencies: + escape-string-regexp: 1.0.5 + eslint: 8.52.0 + ignore: 5.2.4 + dev: true + + /eslint-plugin-i@2.29.0(@typescript-eslint/parser@6.9.1)(eslint@8.52.0): + resolution: {integrity: sha512-slGeTS3GQzx9267wLJnNYNO8X9EHGsc75AKIAFvnvMYEcTJKotPKL1Ru5PIGVHIVet+2DsugePWp8Oxpx8G22w==} + engines: {node: '>=12'} + peerDependencies: + eslint: ^7.2.0 || ^8 + dependencies: + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.52.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.9.1)(eslint-import-resolver-node@0.3.9)(eslint@8.52.0) + get-tsconfig: 4.7.2 + is-glob: 4.0.3 + minimatch: 3.1.2 + resolve: 1.22.8 + semver: 7.5.4 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-jsdoc@46.8.2(eslint@8.52.0): + resolution: {integrity: sha512-5TSnD018f3tUJNne4s4gDWQflbsgOycIKEUBoCLn6XtBMgNHxQFmV8vVxUtiPxAQq8lrX85OaSG/2gnctxw9uQ==} + engines: {node: '>=16'} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@es-joy/jsdoccomment': 0.40.1 + are-docs-informative: 0.0.2 + comment-parser: 1.4.0 + debug: 4.3.4 + escape-string-regexp: 4.0.0 + eslint: 8.52.0 + esquery: 1.5.0 + is-builtin-module: 3.2.1 + semver: 7.5.4 + spdx-expression-parse: 3.0.1 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-jsonc@2.10.0(eslint@8.52.0): + resolution: {integrity: sha512-9d//o6Jyh4s1RxC9fNSt1+MMaFN2ruFdXPG9XZcb/mR2KkfjADYiNL/hbU6W0Cyxfg3tS/XSFuhl5LgtMD8hmw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) + eslint: 8.52.0 + eslint-compat-utils: 0.1.2(eslint@8.52.0) + jsonc-eslint-parser: 2.4.0 + natural-compare: 1.4.0 + dev: true + + /eslint-plugin-markdown@3.0.1(eslint@8.52.0): + resolution: {integrity: sha512-8rqoc148DWdGdmYF6WSQFT3uQ6PO7zXYgeBpHAOAakX/zpq+NvFYbDA/H7PYzHajwtmaOzAwfxyl++x0g1/N9A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + eslint: 8.52.0 + mdast-util-from-markdown: 0.8.5 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-n@16.2.0(eslint@8.52.0): + resolution: {integrity: sha512-AQER2jEyQOt1LG6JkGJCCIFotzmlcCZFur2wdKrp1JX2cNotC7Ae0BcD/4lLv3lUAArM9uNS8z/fsvXTd0L71g==} + engines: {node: '>=16.0.0'} + peerDependencies: + eslint: '>=7.0.0' + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) + builtins: 5.0.1 + eslint: 8.52.0 + eslint-plugin-es-x: 7.3.0(eslint@8.52.0) + get-tsconfig: 4.7.2 + ignore: 5.2.4 + is-core-module: 2.13.1 + minimatch: 3.1.2 + resolve: 1.22.8 + semver: 7.5.4 + dev: true + + /eslint-plugin-no-only-tests@3.1.0: + resolution: {integrity: sha512-Lf4YW/bL6Un1R6A76pRZyE1dl1vr31G/ev8UzIc/geCgFWyrKil8hVjYqWVKGB/UIGmb6Slzs9T0wNezdSVegw==} + engines: {node: '>=5.0.0'} + dev: true + + /eslint-plugin-react-hooks@4.6.0(eslint@8.52.0): + resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + dependencies: + eslint: 8.52.0 + dev: true + + /eslint-plugin-react-refresh@0.4.4(eslint@8.52.0): + resolution: {integrity: sha512-eD83+65e8YPVg6603Om2iCIwcQJf/y7++MWm4tACtEswFLYMwxwVWAfwN+e19f5Ad/FOyyNg9Dfi5lXhH3Y3rA==} + peerDependencies: + eslint: '>=7' + dependencies: + eslint: 8.52.0 + dev: true + + /eslint-plugin-react@7.33.2(eslint@8.52.0): + resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.7 + array.prototype.flatmap: 1.3.2 + array.prototype.tosorted: 1.1.2 + doctrine: 2.1.0 + es-iterator-helpers: 1.0.15 + eslint: 8.52.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.7 + object.fromentries: 2.0.7 + object.hasown: 1.1.3 + object.values: 1.1.7 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.10 + dev: true + + /eslint-plugin-tailwindcss@3.13.0(tailwindcss@3.3.5): + resolution: {integrity: sha512-Fcep4KDRLWaK3KmkQbdyKHG0P4GdXFmXdDaweTIPcgOP60OOuWFbh1++dufRT28Q4zpKTKaHwTsXPJ4O/EjU2Q==} + engines: {node: '>=12.13.0'} + peerDependencies: + tailwindcss: ^3.3.2 + dependencies: + fast-glob: 3.3.1 + postcss: 8.4.31 + tailwindcss: 3.3.5 + dev: true + + /eslint-plugin-unicorn@48.0.1(eslint@8.52.0): + resolution: {integrity: sha512-FW+4r20myG/DqFcCSzoumaddKBicIPeFnTrifon2mWIzlfyvzwyqZjqVP7m4Cqr/ZYisS2aiLghkUWaPg6vtCw==} + engines: {node: '>=16'} + peerDependencies: + eslint: '>=8.44.0' + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) + ci-info: 3.9.0 + clean-regexp: 1.0.0 + eslint: 8.52.0 + esquery: 1.5.0 + indent-string: 4.0.0 + is-builtin-module: 3.2.1 + jsesc: 3.0.2 + lodash: 4.17.21 + pluralize: 8.0.0 + read-pkg-up: 7.0.1 + regexp-tree: 0.1.27 + regjsparser: 0.10.0 + semver: 7.5.4 + strip-indent: 3.0.0 + dev: true + + /eslint-plugin-unused-imports@3.0.0(@typescript-eslint/eslint-plugin@6.9.1)(eslint@8.52.0): + resolution: {integrity: sha512-sduiswLJfZHeeBJ+MQaG+xYzSWdRXoSw61DpU13mzWumCkR0ufD0HmO4kdNokjrkluMHpj/7PJeN35pgbhW3kw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^6.0.0 + eslint: ^8.0.0 + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + dependencies: + '@typescript-eslint/eslint-plugin': 6.9.1(@typescript-eslint/parser@6.9.1)(eslint@8.52.0)(typescript@5.2.2) + eslint: 8.52.0 + eslint-rule-composer: 0.3.0 + dev: true + + /eslint-plugin-vitest@0.3.8(@typescript-eslint/eslint-plugin@6.9.1)(eslint@8.52.0)(typescript@5.2.2)(vitest@0.34.6): + resolution: {integrity: sha512-MYQJzg3i+nLkaIQgjnOhtqHYIt0W6nErqAOKI3LTSQ2aOgcNHGYTwOhpnwGC1IXTvGWjKgAwb7rHwLpcHWHSAQ==} + engines: {node: 14.x || >= 16} + peerDependencies: + '@typescript-eslint/eslint-plugin': '*' + eslint: '>=8.0.0' + vitest: '*' + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + dependencies: + '@typescript-eslint/eslint-plugin': 6.9.1(@typescript-eslint/parser@6.9.1)(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/utils': 6.9.1(eslint@8.52.0)(typescript@5.2.2) + eslint: 8.52.0 + vitest: 0.34.6 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /eslint-plugin-yml@1.10.0(eslint@8.52.0): + resolution: {integrity: sha512-53SUwuNDna97lVk38hL/5++WXDuugPM9SUQ1T645R0EHMRCdBIIxGye/oOX2qO3FQ7aImxaUZJU/ju+NMUBrLQ==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + dependencies: + debug: 4.3.4 + eslint: 8.52.0 + eslint-compat-utils: 0.1.2(eslint@8.52.0) + lodash: 4.17.21 + natural-compare: 1.4.0 + yaml-eslint-parser: 1.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-rule-composer@0.3.0: + resolution: {integrity: sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==} + engines: {node: '>=4.0.0'} + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.52.0: + resolution: {integrity: sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) + '@eslint-community/regexpp': 4.10.0 + '@eslint/eslintrc': 2.1.2 + '@eslint/js': 8.52.0 + '@humanwhocodes/config-array': 0.11.13 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.23.0 + graphemer: 1.4.0 + ignore: 5.2.4 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.11.2 + acorn-jsx: 5.3.2(acorn@8.11.2) + eslint-visitor-keys: 3.4.3 + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + 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.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + requiresBuild: true + dependencies: + reusify: 1.0.4 + dev: true + + /fflate@0.8.1: + resolution: {integrity: sha512-/exOvEuc+/iaUm105QIiOt4LpBdMTWsXxqR0HDF35vx3fmaKzw7354gTilCh5rkzEt8WYyG//ku3h3nRmd7CHQ==} + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.1.1 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.1.1: + resolution: {integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==} + engines: {node: '>=12.0.0'} + dependencies: + flatted: 3.2.9 + keyv: 4.5.4 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.9: + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + dev: true + + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + requiresBuild: true + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: true + + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + dev: true + + /get-intrinsic@1.2.2: + resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} + dependencies: + function-bind: 1.1.2 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + dev: true + + /get-symbol-description@1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + dev: true + + /get-tsconfig@4.7.2: + resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + dependencies: + resolve-pkg-maps: 1.0.0 + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + requiresBuild: true + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + requiresBuild: true + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.1.6: + resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} + requiresBuild: true + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals@13.23.0: + resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.1 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.1 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.2 + dev: true + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors@1.0.1: + resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} + dependencies: + get-intrinsic: 1.2.2 + dev: true + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: true + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag@1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /hasown@2.0.0: + resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + dev: true + + /hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + dev: true + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + requiresBuild: true + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + requiresBuild: true + dev: true + + /internal-slot@1.0.6: + resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.2 + hasown: 2.0.0 + side-channel: 1.0.4 + dev: true + + /is-alphabetical@1.0.4: + resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} + dev: true + + /is-alphanumerical@1.0.4: + resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} + dependencies: + is-alphabetical: 1.0.4 + is-decimal: 1.0.4 + dev: true + + /is-array-buffer@3.0.2: + resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + is-typed-array: 1.1.12 + dev: true + + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + has-tostringtag: 1.0.0 + dev: true + + /is-builtin-module@3.2.1: + resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} + engines: {node: '>=6'} + dependencies: + builtin-modules: 3.3.0 + dev: true + + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + + /is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + dependencies: + hasown: 2.0.0 + dev: true + + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-decimal@1.0.4: + resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: true + + /is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + dependencies: + call-bind: 1.0.5 + dev: true + + /is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-hexadecimal@1.0.4: + resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} + dev: true + + /is-map@2.0.2: + resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} + dev: true + + /is-negative-zero@2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + requiresBuild: true + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + has-tostringtag: 1.0.0 + dev: true + + /is-set@2.0.2: + resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} + dev: true + + /is-shared-array-buffer@1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.5 + dev: true + + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-typed-array@1.1.12: + resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.13 + dev: true + + /is-weakmap@2.0.1: + resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} + dev: true + + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.5 + dev: true + + /is-weakset@2.0.2: + resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + dev: true + + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /iterator.prototype@1.1.2: + resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + dependencies: + define-properties: 1.2.1 + get-intrinsic: 1.2.2 + has-symbols: 1.0.3 + reflect.getprototypeof: 1.0.4 + set-function-name: 2.0.1 + dev: true + + /jiti@1.21.0: + resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + hasBin: true + requiresBuild: true + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsdoc-type-pratt-parser@4.0.0: + resolution: {integrity: sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==} + engines: {node: '>=12.0.0'} + dev: true + + /jsesc@0.5.0: + resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} + hasBin: true + dev: true + + /jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /jsonc-eslint-parser@2.4.0: + resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.11.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + semver: 7.5.4 + dev: true + + /jsonc-parser@3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + + /jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + dependencies: + array-includes: 3.1.7 + array.prototype.flat: 1.3.2 + object.assign: 4.1.4 + object.values: 1.1.7 + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + requiresBuild: true + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /local-pkg@0.4.3: + resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + engines: {node: '>=14'} + dev: true + + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: true + + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + dependencies: + get-func-name: 2.0.2 + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /mdast-util-from-markdown@0.8.5: + resolution: {integrity: sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==} + dependencies: + '@types/mdast': 3.0.14 + mdast-util-to-string: 2.0.0 + micromark: 2.11.4 + parse-entities: 2.0.0 + unist-util-stringify-position: 2.0.3 + transitivePeerDependencies: + - supports-color + dev: true + + /mdast-util-to-string@2.0.0: + resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromark@2.11.4: + resolution: {integrity: sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==} + dependencies: + debug: 4.3.4 + parse-entities: 2.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + requiresBuild: true + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /mlly@1.4.2: + resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} + dependencies: + acorn: 8.11.2 + pathe: 1.1.1 + pkg-types: 1.0.3 + ufo: 1.3.1 + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + requiresBuild: true + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: true + + /nanoid@3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.8 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: true + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: true + + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + requiresBuild: true + dev: true + + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + dev: true + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign@4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.entries@1.1.7: + resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /object.fromentries@2.0.7: + resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /object.hasown@1.1.3: + resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==} + dependencies: + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /object.values@1.1.7: + resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + requiresBuild: true + dependencies: + wrappy: 1.0.2 + dev: true + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + yocto-queue: 1.0.0 + dev: true + + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-entities@2.0.0: + resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} + dependencies: + character-entities: 1.2.4 + character-entities-legacy: 1.1.4 + character-reference-invalid: 1.1.4 + is-alphanumerical: 1.0.4 + is-decimal: 1.0.4 + is-hexadecimal: 1.0.4 + dev: true + + /parse-gitignore@2.0.0: + resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==} + engines: {node: '>=14'} + dev: true + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.22.13 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /pathe@1.1.1: + resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} + dev: true + + /pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + requiresBuild: true + dev: true + + /pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: true + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + requiresBuild: true + dev: true + + /pkg-types@1.0.3: + resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + dependencies: + jsonc-parser: 3.2.0 + mlly: 1.4.2 + pathe: 1.1.1 + dev: true + + /pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + dev: true + + /pmtiles@2.11.0: + resolution: {integrity: sha512-dU9SzzaqmCGpdEuTnIba6bDHT6j09ZJFIXxwGpvkiEnce3ZnBB1VKt6+EOmJGueriweaZLAMTUmKVElU2CBe0g==} + dependencies: + fflate: 0.8.1 + dev: true + + /postcss-import@15.1.0(postcss@8.4.31): + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + requiresBuild: true + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.31 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + dev: true + + /postcss-js@4.0.1(postcss@8.4.31): + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + requiresBuild: true + peerDependencies: + postcss: ^8.4.21 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.31 + dev: true + + /postcss-load-config@4.0.1(postcss@8.4.31): + resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} + engines: {node: '>= 14'} + requiresBuild: true + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.1.0 + postcss: 8.4.31 + yaml: 2.3.3 + dev: true + + /postcss-nested@6.0.1(postcss@8.4.31): + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + engines: {node: '>=12.0'} + requiresBuild: true + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.31 + postcss-selector-parser: 6.0.13 + dev: true + + /postcss-selector-parser@6.0.13: + resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} + engines: {node: '>=4'} + requiresBuild: true + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + requiresBuild: true + dev: true + + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + dev: true + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + requiresBuild: true + dev: true + + /react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + dev: true + + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + requiresBuild: true + dependencies: + pify: 2.3.0 + dev: true + + /read-pkg-up@7.0.1: + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + read-pkg: 5.2.0 + type-fest: 0.8.1 + dev: true + + /read-pkg@5.2.0: + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} + dependencies: + '@types/normalize-package-data': 2.4.3 + normalize-package-data: 2.5.0 + parse-json: 5.2.0 + type-fest: 0.6.0 + dev: true + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + requiresBuild: true + dependencies: + picomatch: 2.3.1 + dev: true + + /reflect.getprototypeof@1.0.4: + resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + globalthis: 1.0.3 + which-builtin-type: 1.1.3 + dev: true + + /regexp-tree@0.1.27: + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + hasBin: true + dev: true + + /regexp.prototype.flags@1.5.1: + resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + set-function-name: 2.0.1 + dev: true + + /regjsparser@0.10.0: + resolution: {integrity: sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==} + hasBin: true + dependencies: + jsesc: 0.5.0 + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + dev: true + + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + requiresBuild: true + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.1.6 + dev: true + + /rollup@3.29.4: + resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + requiresBuild: true + dependencies: + queue-microtask: 1.2.3 + dev: true + + /safe-array-concat@1.0.1: + resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: true + + /safe-regex-test@1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + is-regex: 1.1.4 + dev: true + + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + dev: true + + /semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + dev: true + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /set-function-length@1.1.1: + resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + get-intrinsic: 1.2.2 + gopd: 1.0.1 + has-property-descriptors: 1.0.1 + dev: true + + /set-function-name@2.0.1: + resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.1 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.1 + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + object-inspect: 1.13.1 + dev: true + + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.16 + dev: true + + /spdx-exceptions@2.3.0: + resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + dev: true + + /spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + dependencies: + spdx-exceptions: 2.3.0 + spdx-license-ids: 3.0.16 + dev: true + + /spdx-license-ids@3.0.16: + resolution: {integrity: sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==} + dev: true + + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true + + /std-env@3.4.3: + resolution: {integrity: sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==} + dev: true + + /string.prototype.matchall@4.0.10: + resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + has-symbols: 1.0.3 + internal-slot: 1.0.6 + regexp.prototype.flags: 1.5.1 + set-function-name: 2.0.1 + side-channel: 1.0.4 + dev: true + + /string.prototype.trim@1.2.8: + resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /string.prototype.trimend@1.0.7: + resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /string.prototype.trimstart@1.0.7: + resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + dependencies: + acorn: 8.11.2 + dev: true + + /sucrase@3.34.0: + resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==} + engines: {node: '>=8'} + hasBin: true + requiresBuild: true + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + commander: 4.1.1 + glob: 7.1.6 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /tailwindcss@3.3.5: + resolution: {integrity: sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA==} + engines: {node: '>=14.0.0'} + hasBin: true + requiresBuild: true + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.5.3 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.1 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.0 + lilconfig: 2.1.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.31 + postcss-import: 15.1.0(postcss@8.4.31) + postcss-js: 4.0.1(postcss@8.4.31) + postcss-load-config: 4.0.1(postcss@8.4.31) + postcss-nested: 6.0.1(postcss@8.4.31) + postcss-selector-parser: 6.0.13 + resolve: 1.22.8 + sucrase: 3.34.0 + transitivePeerDependencies: + - ts-node + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + requiresBuild: true + dependencies: + thenify: 3.3.1 + dev: true + + /thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + requiresBuild: true + dependencies: + any-promise: 1.3.0 + dev: true + + /tinybench@2.5.1: + resolution: {integrity: sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==} + dev: true + + /tinypool@0.7.0: + resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy@2.2.0: + resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==} + engines: {node: '>=14.0.0'} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + requiresBuild: true + dependencies: + is-number: 7.0.0 + dev: true + + /ts-api-utils@1.0.3(typescript@5.2.2): + resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} + engines: {node: '>=16.13.0'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.2.2 + dev: true + + /ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + requiresBuild: true + dev: true + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type-fest@0.6.0: + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} + dev: true + + /type-fest@0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + dev: true + + /typed-array-buffer@1.0.0: + resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + is-typed-array: 1.1.12 + dev: true + + /typed-array-byte-length@1.0.0: + resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-byte-offset@1.0.0: + resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.5 + for-each: 0.3.3 + has-proto: 1.0.1 + is-typed-array: 1.1.12 + dev: true + + /typed-array-length@1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.5 + for-each: 0.3.3 + is-typed-array: 1.1.12 + dev: true + + /typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /ufo@1.3.1: + resolution: {integrity: sha512-uY/99gMLIOlJPwATcMVYfqDSxUR9//AUcgZMzwfSTJPDKzA1S8mX4VLqa+fiAtveraQUBCz4FFcwVZBGbwBXIw==} + dev: true + + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.5 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + + /unist-util-stringify-position@2.0.3: + resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} + dependencies: + '@types/unist': 2.0.9 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: true + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + requiresBuild: true + dev: true + + /uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + dev: true + + /validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + dev: true + + /vite-node@0.34.6(@types/node@20.8.10): + resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} + engines: {node: '>=v14.18.0'} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + mlly: 1.4.2 + pathe: 1.1.1 + picocolors: 1.0.0 + vite: 4.5.0(@types/node@20.8.10) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vite-plugin-top-level-await@1.3.1(vite@4.5.0): + resolution: {integrity: sha512-55M1h4NAwkrpxPNOJIBzKZFihqLUzIgnElLSmPNPMR2Fn9+JHKaNg3sVX1Fq+VgvuBksQYxiD3OnwQAUu7kaPQ==} + peerDependencies: + vite: '>=2.8' + dependencies: + '@rollup/plugin-virtual': 3.0.2 + '@swc/core': 1.3.95 + uuid: 9.0.1 + vite: 4.5.0(@types/node@20.8.10) + transitivePeerDependencies: + - '@swc/helpers' + - rollup + dev: true + + /vite-plugin-wasm@3.2.2(vite@4.5.0): + resolution: {integrity: sha512-cdbBUNR850AEoMd5nvLmnyeq63CSfoP1ctD/L2vLk/5+wsgAPlAVAzUK5nGKWO/jtehNlrSSHLteN+gFQw7VOA==} + peerDependencies: + vite: ^2 || ^3 || ^4 + dependencies: + vite: 4.5.0(@types/node@20.8.10) + dev: true + + /vite@4.5.0(@types/node@20.8.10): + resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.8.10 + esbuild: 0.18.20 + postcss: 8.4.31 + rollup: 3.29.4 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vitest@0.34.6: + resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} + engines: {node: '>=v14.18.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + playwright: '*' + safaridriver: '*' + webdriverio: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + dependencies: + '@types/chai': 4.3.9 + '@types/chai-subset': 1.3.4 + '@types/node': 20.8.10 + '@vitest/expect': 0.34.6 + '@vitest/runner': 0.34.6 + '@vitest/snapshot': 0.34.6 + '@vitest/spy': 0.34.6 + '@vitest/utils': 0.34.6 + acorn: 8.11.2 + acorn-walk: 8.3.0 + cac: 6.7.14 + chai: 4.3.10 + debug: 4.3.4 + local-pkg: 0.4.3 + magic-string: 0.30.5 + pathe: 1.1.1 + picocolors: 1.0.0 + std-env: 3.4.3 + strip-literal: 1.3.0 + tinybench: 2.5.1 + tinypool: 0.7.0 + vite: 4.5.0(@types/node@20.8.10) + vite-node: 0.34.6(@types/node@20.8.10) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-builtin-type@1.1.3: + resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} + engines: {node: '>= 0.4'} + dependencies: + function.prototype.name: 1.1.6 + has-tostringtag: 1.0.0 + is-async-function: 2.0.0 + is-date-object: 1.0.5 + is-finalizationregistry: 1.0.2 + is-generator-function: 1.0.10 + is-regex: 1.1.4 + is-weakref: 1.0.2 + isarray: 2.0.5 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.1 + which-typed-array: 1.1.13 + dev: true + + /which-collection@1.0.1: + resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} + dependencies: + is-map: 2.0.2 + is-set: 2.0.2 + is-weakmap: 2.0.1 + is-weakset: 2.0.2 + dev: true + + /which-typed-array@1.1.13: + resolution: {integrity: sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.5 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /why-is-node-running@2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + requiresBuild: true + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yaml-eslint-parser@1.2.2: + resolution: {integrity: sha512-pEwzfsKbTrB8G3xc/sN7aw1v6A6c/pKxLAkjclnAyo5g5qOh6eL9WGu0o3cSDQZKrTNk4KL4lQSwZW+nBkANEg==} + engines: {node: ^14.17.0 || >=16.0.0} + dependencies: + eslint-visitor-keys: 3.4.3 + lodash: 4.17.21 + yaml: 2.3.3 + dev: true + + /yaml@2.3.3: + resolution: {integrity: sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==} + engines: {node: '>= 14'} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: true diff --git a/crates/utiles-wasm/src/lib.rs b/crates/utiles-wasm/src/lib.rs new file mode 100644 index 00000000..035e576b --- /dev/null +++ b/crates/utiles-wasm/src/lib.rs @@ -0,0 +1,29 @@ +use utiles::pmtiles::xyz2pmid as _xyz2pmid; +use utiles::xyz2quadkey as uxyz2qk; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[wasm_bindgen] +pub fn pmtileid(x: u32, y: u32, z: u8) -> u64 { + _xyz2pmid(x, y, z) +} + +#[wasm_bindgen] +pub fn xyz2qk(x: u32, y: u32, z: u8) -> String { + uxyz2qk(x, y, z) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/utiles-wasm/test/utiles-wasm.bench.ts b/crates/utiles-wasm/test/utiles-wasm.bench.ts new file mode 100644 index 00000000..05895ab3 --- /dev/null +++ b/crates/utiles-wasm/test/utiles-wasm.bench.ts @@ -0,0 +1,38 @@ +import {test, expect, bench} from 'vitest' +import * as utw from '../pkg/utiles_wasm.js' +import * as pmtiles from 'pmtiles' + +function xyz2quadkey( + x: number, y: number, z: number +) { + let quadkey = '' + for (let i = z; i > 0; i--) { + let digit = 0 + const mask = 1 << (i - 1) + if ((x & mask) !== 0) digit += 1 + if ((y & mask) !== 0) digit += 2 + quadkey += digit + } + return quadkey +} + +bench('js-quadkey', () => { + xyz2quadkey(486, 332, 20) +}) + +bench('wasm-quadkey', () => { + utw.xyz2qk(486, 332, 20) +}) + + +bench('js-pmtile-id', () => { + pmtiles.zxyToTileId( + 20, 486, 332 + ) + // .pmtileId2(486, 332, 20) + }) + + +bench('wasm-pmtile-id', () => { + utw.pmtileid(486, 332, 20) +}) diff --git a/crates/utiles-wasm/test/utiles-wasm.test.ts b/crates/utiles-wasm/test/utiles-wasm.test.ts new file mode 100644 index 00000000..7c24df7b --- /dev/null +++ b/crates/utiles-wasm/test/utiles-wasm.test.ts @@ -0,0 +1,72 @@ +import {test, expect, bench} from 'vitest' +import * as utw from '../pkg/utiles_wasm.js' +import * as pmtiles from 'pmtiles' + +function xyz2quadkey( + x: number, y: number, z: number +) { + let quadkey = '' + for (let i = z; i > 0; i--) { + let digit = 0 + const mask = 1 << (i - 1) + if ((x & mask) !== 0) digit += 1 + if ((y & mask) !== 0) digit += 2 + quadkey += digit + } + return quadkey +} +test( + 'uno', () => { + console.log('uno') + expect(1).toBe(1) + } +) +// 486, 332, 20 +test( + 'thingy', async () => { + console.log('thingy') + console.log(utw) + const added = utw.add(1, 2) + expect(added).toBe(3) + + + // const called = await utw.default() + // console.log(called) + + // expect(utw.add(1, 2)).toBe(3) + } +) + +test( + 'quadkey', async () => { + const jsres = xyz2quadkey(486, 332, 20) + const wasmres = utw.xyz2qk(486, 332, 20) + expect(jsres).toBe(wasmres) + + + // const called = await utw.default() + // console.log(called) + + // expect(utw.add(1, 2)).toBe(3) + } +) + +test( + 'pmtileid', async () => { + const jsres = pmtiles.zxyToTileId( + 15, 486, 332 + ) + const wasmres = utw.pmtileid(486, 332, 15) + + console.log( + { + jsres, + wasmres + } + ) + expect(jsres).toBe( + // debigint + Number(wasmres) + + ) + }) diff --git a/crates/utiles-wasm/tsconfig.json b/crates/utiles-wasm/tsconfig.json new file mode 100644 index 00000000..9b803ed0 --- /dev/null +++ b/crates/utiles-wasm/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "Node16", + "moduleResolution": "Node16", + "resolveJsonModule": true, + "allowJs": true, + "strict": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noEmit": false, + "outDir": "dist", + "esModuleInterop": true, + "skipLibCheck": true + }, + "exclude": ["node_modules", "dist", "scratch"] +} diff --git a/crates/utiles-wasm/vitest.config.ts b/crates/utiles-wasm/vitest.config.ts new file mode 100644 index 00000000..f15f8438 --- /dev/null +++ b/crates/utiles-wasm/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + benchmark: { + exclude: ["node_modules", "dist", ".idea", ".git", ".cache"], + include: ["**/*.{bench,benchmark}.?(c|m)[jt]s?(x)"], + }, + }, +}); diff --git a/crates/utiles/Cargo.lock b/crates/utiles/Cargo.lock index 33aa4ac1..c7f1ec3b 100644 --- a/crates/utiles/Cargo.lock +++ b/crates/utiles/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "approx" version = "0.5.1" @@ -28,115 +34,245 @@ dependencies = [ [[package]] name = "geo-types" -version = "0.7.9" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5f0b3068e1537a4b861ec3734f4aa9c317d537cf0845bf6fb6221973499d26c" +checksum = "9705398c5c7b26132e74513f4ee7c1d7dafd786004991b375c172be2be0eecaa" dependencies = [ "approx", "num-traits", "serde", ] +[[package]] +name = "geojson" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d728c1df1fbf328d74151efe6cb0586f79ee813346ea981add69bd22c9241b" +dependencies = [ + "geo-types", + "log", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "log" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", "libm", ] +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + [[package]] name = "proc-macro2" -version = "1.0.60" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.28" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "serde" -version = "1.0.164" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.164" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] name = "serde_json" -version = "1.0.97" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", "serde", ] +[[package]] +name = "serde_tuple" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f025b91216f15a2a32aa39669329a475733590a015835d1783549a56d09427" +dependencies = [ + "serde", + "serde_tuple_macros", +] + +[[package]] +name = "serde_tuple_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4076151d1a2b688e25aaf236997933c66e18b870d0369f8b248b8ab2be630d7e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" -version = "2.0.18" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "tilejson" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a16212e5ea60f2b406835981338a1df9b206fab312e3470502d60f69e590a9c9" +dependencies = [ + "serde", + "serde_json", + "serde_tuple", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + [[package]] name = "unicode-ident" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "utiles" version = "0.0.1" dependencies = [ + "anyhow", "fast_hilbert", "geo-types", + "geojson", "serde", "serde_json", + "thiserror", + "tilejson", + "tracing", ] diff --git a/crates/utiles/Cargo.toml b/crates/utiles/Cargo.toml index c7e476bf..51092d48 100644 --- a/crates/utiles/Cargo.toml +++ b/crates/utiles/Cargo.toml @@ -14,7 +14,13 @@ repository = "https://github.com/jessekrubin/utiles" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0.75" fast_hilbert = "2.0.0" geo-types = "0.7.9" +geojson = "0.24.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.96" +thiserror = "1.0.50" +tilejson = "0.3.2" +tracing = { version = "0.1.40", features = [] } +log = "0.4.20" diff --git a/crates/utiles/src/bbox.rs b/crates/utiles/src/bbox.rs index 81c2a592..16c3ceed 100644 --- a/crates/utiles/src/bbox.rs +++ b/crates/utiles/src/bbox.rs @@ -1,5 +1,21 @@ use crate::lnglat::LngLat; +use crate::parsing::parse_bbox; use crate::tile::Tile; +use geo_types::Coord; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)] +pub struct BBoxTuple(f64, f64, f64, f64); + +#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)] +pub struct CoordTuple(f64, f64); + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +pub enum BBoxParseAble { + BBoxTuple((f64, f64, f64, f64)), + CoordTuple((f64, f64)), +} + #[derive(Debug, Clone, Copy, PartialEq)] pub struct BBox { pub north: f64, @@ -24,12 +40,7 @@ pub enum BBoxContainable { impl From<(f64, f64, f64, f64)> for BBox { fn from(bbox: (f64, f64, f64, f64)) -> Self { - BBox { - north: bbox.0, - south: bbox.1, - east: bbox.2, - west: bbox.3, - } + BBox::new(bbox.0, bbox.1, bbox.2, bbox.3) } } @@ -52,39 +63,80 @@ impl From<(i32, i32, i32, i32)> for BBox { } impl BBox { + #[must_use] + pub fn new(west: f64, south: f64, east: f64, north: f64) -> Self { + BBox { + west, + south, + east, + north, + } + } + + #[must_use] + pub fn world_planet() -> Self { + BBox { + west: -180.0, + south: -90.0, + east: 180.0, + north: 90.0, + } + } + + #[must_use] + pub fn world_web() -> Self { + BBox { + west: -180.0, + south: -85.051_129, + east: 180.0, + north: 85.051_129, + } + } + + #[must_use] pub fn crosses_antimeridian(&self) -> bool { self.west > self.east } + #[must_use] pub fn tuple(&self) -> (f64, f64, f64, f64) { (self.north, self.south, self.east, self.west) } + #[must_use] pub fn north(&self) -> f64 { self.north } + #[must_use] pub fn south(&self) -> f64 { self.south } + #[must_use] pub fn east(&self) -> f64 { self.east } + #[must_use] pub fn west(&self) -> f64 { self.west } + #[must_use] pub fn top(&self) -> f64 { self.north } + #[must_use] pub fn bottom(&self) -> f64 { self.south } + #[must_use] pub fn right(&self) -> f64 { self.east } + #[must_use] pub fn left(&self) -> f64 { self.west } + #[must_use] pub fn contains_lnglat(&self, lnglat: LngLat) -> bool { let lng = lnglat.lng(); let lat = lnglat.lat(); @@ -105,11 +157,13 @@ impl BBox { false } + #[must_use] pub fn contains_tile(&self, tile: Tile) -> bool { let bbox = tile.bbox(); self.contains_bbox(bbox.into()) } + #[must_use] pub fn contains_bbox(&self, other: BBox) -> bool { self.north >= other.north && self.south <= other.south @@ -117,6 +171,7 @@ impl BBox { && self.west <= other.west } + #[must_use] pub fn contains(&self, other: BBoxContainable) -> bool { match other { BBoxContainable::LngLat(lnglat) => self.contains_lnglat(lnglat), @@ -125,6 +180,7 @@ impl BBox { } } + #[must_use] pub fn is_within(&self, other: &BBox) -> bool { self.north <= other.north && self.south >= other.south @@ -132,6 +188,7 @@ impl BBox { && self.west >= other.west } + #[must_use] pub fn intersects(&self, other: &BBox) -> bool { self.north >= other.south && self.south <= other.north @@ -139,6 +196,7 @@ impl BBox { && self.west <= other.east } + #[must_use] pub fn bboxes(&self) -> Vec { if self.crosses_antimeridian() { let mut bboxes = Vec::new(); @@ -162,25 +220,89 @@ impl BBox { } } + #[must_use] pub fn ul(&self) -> LngLat { LngLat::new(self.west, self.north) } + #[must_use] pub fn ur(&self) -> LngLat { LngLat::new(self.east, self.north) } + #[must_use] pub fn lr(&self) -> LngLat { LngLat::new(self.east, self.south) } + #[must_use] pub fn ll(&self) -> LngLat { LngLat::new(self.west, self.south) } } +impl From for BBoxTuple { + fn from(bbox: BBox) -> Self { + BBoxTuple(bbox.west, bbox.south, bbox.east, bbox.north) + } +} + +impl From for BBox { + fn from(tuple: BBoxTuple) -> Self { + BBox::new(tuple.0, tuple.1, tuple.2, tuple.3) + } +} + +impl From<&String> for BBox { + fn from(s: &String) -> Self { + // remove leading and trailing quotes + let s = s.trim_matches('"'); + // let value: Value = serde_json::from_str(&s).unwrap(); + let tuple: BBoxTuple = serde_json::from_str(s).unwrap(); + self::BBox::from(tuple) + } +} + +impl From<&str> for BBox { + fn from(s: &str) -> Self { + parse_bbox(s).unwrap() + } +} + +impl From for BBox { + fn from(s: String) -> Self { + self::BBox::from(&s) + } +} + impl From for WebMercatorBbox { fn from(tile: Tile) -> Self { crate::xyz2bbox(tile.x, tile.y, tile.z) } } + +impl From> for BBox { + fn from(coords: Vec) -> Self { + let mut min_x = 180.0; + let mut min_y = 90.0; + let mut max_x = -180.0; + let mut max_y = -90.0; + for coord in coords { + let x = coord.x; + let y = coord.y; + if x < min_x { + min_x = x; + } + if y < min_y { + min_y = y; + } + if x > max_x { + max_x = x; + } + if y > max_y { + max_y = y; + } + } + BBox::new(min_x, min_y, max_x, max_y) + } +} diff --git a/crates/utiles/src/geojson/mod.rs b/crates/utiles/src/geojson/mod.rs new file mode 100644 index 00000000..e850bf30 --- /dev/null +++ b/crates/utiles/src/geojson/mod.rs @@ -0,0 +1,77 @@ +use geo_types::coord; +use geo_types::Coord; +use geojson::{Feature, GeoJson, Geometry, Value as GeoJsonValue}; + +pub fn geojson_geometry_points(g: Geometry) -> Box>> { + let value = g.value; + match value { + GeoJsonValue::Point(c) => Box::new(std::iter::once(c)), + GeoJsonValue::MultiPoint(points) => Box::new(points.into_iter()), + GeoJsonValue::LineString(line_string) => Box::new(line_string.into_iter()), + GeoJsonValue::MultiLineString(multi_line_string) => { + Box::new(multi_line_string.into_iter().flatten()) + } + GeoJsonValue::Polygon(polygon) => Box::new(polygon.into_iter().flatten()), + GeoJsonValue::MultiPolygon(multi_polygon) => { + Box::new(multi_polygon.into_iter().flatten().flatten()) + } + GeoJsonValue::GeometryCollection(geometries) => { + Box::new(geometries.into_iter().flat_map(geojson_geometry_points)) + } + } +} + +#[must_use] +pub fn geojson_geometry_coords(g: Geometry) -> Box> { + let coord_vecs = geojson_geometry_points(g); + Box::new(coord_vecs.into_iter().map(|v| { + coord! { x: v[0], y: v[1]} + })) +} + +pub fn geojson_geometry_points_vec(g: Geometry) -> Vec> { + let value = g.value; + + match value { + GeoJsonValue::Point(c) => { + vec![c] + } + GeoJsonValue::MultiPoint(c) => c.into_iter().collect(), + GeoJsonValue::LineString(c) => c.into_iter().collect(), + GeoJsonValue::MultiLineString(c) => c.into_iter().flatten().collect(), + GeoJsonValue::Polygon(c) => c.into_iter().flatten().collect(), + GeoJsonValue::MultiPolygon(c) => c.into_iter().flatten().flatten().collect(), + GeoJsonValue::GeometryCollection(c) => c + .into_iter() + .flat_map(geojson_geometry_points_vec) + .collect(), + } +} + +#[must_use] +pub fn geojson_feature_coords(feature: Feature) -> Box> { + let geometry = feature.geometry.unwrap(); + geojson_geometry_coords(geometry) +} + +pub fn geojson_coords(geojson_str: &str) -> Box> { + let gj = geojson_str.parse::().unwrap(); + match gj { + GeoJson::FeatureCollection(fc) => { + let coords = fc.features.into_iter().flat_map(geojson_feature_coords); + Box::new(coords) + // let mut bbox = BBox::new(180.0, 90.0, -180.0, -90.0); + // for feature in fc.features { + // let feature_bbox = geojson_feature_bounds(feature); + // bbox = bbox.union(feature_bbox); + // } + // bbox + } + GeoJson::Feature(feature) => { + // if it has a bbox + let geometry = feature.geometry.unwrap(); + geojson_geometry_coords(geometry) + } + GeoJson::Geometry(geometry) => geojson_geometry_coords(geometry), + } +} diff --git a/crates/utiles/src/lib.rs b/crates/utiles/src/lib.rs index 1381eb3b..7f3bb022 100644 --- a/crates/utiles/src/lib.rs +++ b/crates/utiles/src/lib.rs @@ -1,3 +1,7 @@ +#![deny(clippy::all)] +#![deny(clippy::perf)] +#![deny(clippy::style)] + use std::collections::{HashMap, HashSet}; use std::num::FpCategory; use std::{error::Error, f64::consts::PI}; @@ -14,12 +18,19 @@ use zoom::ZoomOrZooms; pub mod bbox; pub mod constants; +pub mod geojson; pub mod libtiletype; pub mod lnglat; +pub mod mbtiles; +pub mod parsing; pub mod pmtiles; +pub mod projection; pub mod sibling_relationship; pub mod tile; +mod tile_feature; pub mod tile_range; +mod tile_tuple; +pub mod tilejson; pub mod traits; pub mod zoom; @@ -33,10 +44,7 @@ macro_rules! utile { }; } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[allow(clippy::upper_case_acronyms)] -pub struct XYZ(pub u32, pub u32, pub u8); - +#[must_use] pub fn ul(x: u32, y: u32, z: u8) -> LngLat { let (lon_deg, lat_deg) = ult(x, y, z); LngLat { @@ -44,17 +52,21 @@ pub fn ul(x: u32, y: u32, z: u8) -> LngLat { } } +#[must_use] pub fn ll(x: u32, y: u32, z: u8) -> LngLat { ul(x, y + 1, z) } +#[must_use] pub fn ur(x: u32, y: u32, z: u8) -> LngLat { ul(x + 1, y, z) } +#[must_use] pub fn lr(x: u32, y: u32, z: u8) -> LngLat { ul(x + 1, y + 1, z) } +#[must_use] pub fn ult(x: u32, y: u32, z: u8) -> (f64, f64) { let z2 = f64::from(2_u32.pow(u32::from(z))); let lon_deg = (f64::from(x) / z2) * 360.0 - 180.0; @@ -71,6 +83,7 @@ pub struct XY { } /// Truncate a bounding box to the valid range of longitude and latitude. +#[must_use] pub fn bbox_truncate( west: f64, south: f64, @@ -100,20 +113,24 @@ pub fn bbox_truncate( (west, south, east, north) } +#[must_use] pub fn minmax(zoom: u32) -> (u32, u32) { (0, 2_u32.pow(zoom) - 1) } +#[must_use] pub fn valid(x: u32, y: u32, z: u8) -> bool { let (minx, maxx) = minmax(u32::from(z)); let (miny, maxy) = minmax(u32::from(z)); x >= minx && x <= maxx && y >= miny && y <= maxy } +#[must_use] pub fn flipy(y: u32, z: u8) -> u32 { 2_u32.pow(u32::from(z)) - 1 - y } +#[must_use] pub fn bbox2zoom(bbox: (u32, u32, u32, u32)) -> u8 { let max_zoom = 28; let (west, south, east, north) = bbox; @@ -126,6 +143,7 @@ pub fn bbox2zoom(bbox: (u32, u32, u32, u32)) -> u8 { max_zoom } +#[must_use] pub fn bounds(x: u32, y: u32, z: u8) -> (f64, f64, f64, f64) { let ul_corner = ul(x, y, z); let lr_corner = ul(x + 1, y + 1, z); @@ -137,6 +155,7 @@ pub fn bounds(x: u32, y: u32, z: u8) -> (f64, f64, f64, f64) { ) } +#[must_use] pub fn truncate_lng(lng: f64) -> f64 { if lng > 180.0 { 180.0 @@ -147,6 +166,7 @@ pub fn truncate_lng(lng: f64) -> f64 { } } +#[must_use] pub fn truncate_lat(lat: f64) -> f64 { if lat > 90.0 { 90.0 @@ -157,6 +177,7 @@ pub fn truncate_lat(lat: f64) -> f64 { } } +#[must_use] pub fn truncate_lnglat(lnglat: &LngLat) -> LngLat { LngLat { xy: coord! {x: truncate_lng(lnglat.lng()), y: truncate_lat(lnglat.lat())}, @@ -189,35 +210,36 @@ pub fn _xy( } } -pub fn _xy_og(lng: f64, lat: f64, truncate: Option) -> (f64, f64) { - let trunc = truncate.unwrap_or(false); - - let mut lng = lng; - let mut lat = lat; - if trunc { - lng = truncate_lng(lng); - lat = truncate_lat(lat); - } - let x = (lng / 360.0) + 0.5; - - let sinlat = lat.to_radians().sin(); - - let y_inner = (1.0 + sinlat) / (1.0 - sinlat); - let y = match (1.0 + sinlat) / (1.0 - sinlat) { - y if y.is_infinite() => { - panic!("Invalid latitude (inf): {lat:?}"); - } - y if y.is_nan() => { - panic!("Invalid latitude (nan): {lat:?}"); - } - // y => 0.5 - 0.25 * y.ln() / std::f64::consts::PI, - // y = 0.5 - 0.25 * math.log((1.0 + sinlat) / (1.0 - sinlat)) / math.pi - _y => 0.5 - 0.25 * y_inner.ln() / PI, - }; - (x, y) - // y = 0.5 - 0.25 * math.log((1.0 + sinlat) / (1.0 - sinlat)) / math.pi -} +// pub fn _xy_og(lng: f64, lat: f64, truncate: Option) -> (f64, f64) { +// let trunc = truncate.unwrap_or(false); +// +// let mut lng = lng; +// let mut lat = lat; +// if trunc { +// lng = truncate_lng(lng); +// lat = truncate_lat(lat); +// } +// let x = (lng / 360.0) + 0.5; +// +// let sinlat = lat.to_radians().sin(); +// +// let y_inner = (1.0 + sinlat) / (1.0 - sinlat); +// let y = match (1.0 + sinlat) / (1.0 - sinlat) { +// y if y.is_infinite() => { +// panic!("Invalid latitude (inf): {lat:?}"); +// } +// y if y.is_nan() => { +// panic!("Invalid latitude (nan): {lat:?}"); +// } +// // y => 0.5 - 0.25 * y.ln() / std::f64::consts::PI, +// // y = 0.5 - 0.25 * math.log((1.0 + sinlat) / (1.0 - sinlat)) / math.pi +// _y => 0.5 - 0.25 * y_inner.ln() / PI, +// }; +// (x, y) +// // y = 0.5 - 0.25 * math.log((1.0 + sinlat) / (1.0 - sinlat)) / math.pi +// } +#[must_use] pub fn xy(lng: f64, lat: f64, truncate: Option) -> (f64, f64) { let trunc = truncate.unwrap_or(false); let mut lng = lng; @@ -238,6 +260,7 @@ pub fn xy(lng: f64, lat: f64, truncate: Option) -> (f64, f64) { (x, y) } +#[must_use] pub fn lnglat(x: f64, y: f64, truncate: Option) -> LngLat { let lng = x / EARTH_RADIUS * 180.0 / PI; let lat = (2.0 * (y / EARTH_RADIUS).exp().atan() - PI * 0.5) * 180.0 / PI; @@ -252,6 +275,7 @@ pub fn lnglat(x: f64, y: f64, truncate: Option) -> LngLat { } } +#[must_use] pub fn parent(x: u32, y: u32, z: u8, n: Option) -> Tile { let n = n.unwrap_or(0); if n == 0 { @@ -265,6 +289,7 @@ pub fn parent(x: u32, y: u32, z: u8, n: Option) -> Tile { } } +#[must_use] pub fn children(x: u32, y: u32, z: u8, zoom: Option) -> Vec { let zoom = zoom.unwrap_or(z + 1); let tile = Tile { x, y, z }; @@ -282,6 +307,7 @@ pub fn children(x: u32, y: u32, z: u8, zoom: Option) -> Vec { tiles } +#[must_use] pub fn siblings(x: u32, y: u32, z: u8) -> Vec { let sibrel = SiblingRelationship::from((x, y)); match sibrel { @@ -363,6 +389,7 @@ fn _neighbors_middle_tile(x: u32, y: u32, z: u8) -> Vec { ] } +#[must_use] pub fn neighbors(x: u32, y: u32, z: u8) -> Vec { if z == 0 { return Vec::new(); @@ -430,6 +457,7 @@ pub fn neighbors(x: u32, y: u32, z: u8) -> Vec { /// let quadkey = xyz2quadkey(486, 332, 10); /// assert_eq!(quadkey, "0313102310"); /// ``` +#[must_use] pub fn xyz2quadkey(x: u32, y: u32, z: u8) -> String { let mut quadkey = String::new(); for i in (0..z).rev() { @@ -486,6 +514,7 @@ impl From for (u32, u32, u8) { } } +#[must_use] pub fn xyz2bbox(x: u32, y: u32, z: u8) -> WebMercatorBbox { let tile_size = EARTH_CIRCUMFERENCE / 2.0_f64.powi(i32::from(z)); let left = f64::from(x) * tile_size - EARTH_CIRCUMFERENCE / 2.0; @@ -500,6 +529,7 @@ pub fn xyz2bbox(x: u32, y: u32, z: u8) -> WebMercatorBbox { } } +#[must_use] pub fn as_zooms(zoom_or_zooms: ZoomOrZooms) -> Vec { match zoom_or_zooms { ZoomOrZooms::Zoom(zoom) => { @@ -519,10 +549,12 @@ fn tiles_range_zoom( (minx..=maxx).flat_map(move |i| (miny..=maxy).map(move |j| (i, j, zoom))) } +#[must_use] pub fn tile(lng: f64, lat: f64, zoom: u8, truncate: Option) -> Tile { Tile::from_lnglat_zoom(lng, lat, zoom, truncate) } +#[must_use] pub fn bounding_tile(bbox: BBox, truncate: Option) -> Tile { let (west, south, east, north) = bbox_truncate(bbox.west, bbox.south, bbox.east, bbox.north, truncate); @@ -563,6 +595,7 @@ pub fn bounding_tile(bbox: BBox, truncate: Option) -> Tile { // (minx, miny, maxx, maxy) // } +#[must_use] pub fn tile_ranges(bounds: (f64, f64, f64, f64), zooms: ZoomOrZooms) -> TileRanges { let zooms = as_zooms(zooms); let bboxthing = BBox { @@ -621,6 +654,7 @@ pub fn tile_ranges(bounds: (f64, f64, f64, f64), zooms: ZoomOrZooms) -> TileRang TileRanges::from(ranges) } +#[must_use] pub fn tiles_count(bounds: (f64, f64, f64, f64), zooms: ZoomOrZooms) -> u64 { let ranges = tile_ranges(bounds, zooms); ranges.length() @@ -707,6 +741,7 @@ fn merge(merge_set: &HashSet) -> (HashSet, bool) { } #[allow(dead_code)] +#[must_use] pub fn simplify(tiles: HashSet) -> HashSet { // Parse tiles from the input sequence let mut _tiles = tiles.into_iter().collect::>(); diff --git a/crates/utiles/src/libtiletype.rs b/crates/utiles/src/libtiletype.rs index 33421086..c91cb974 100644 --- a/crates/utiles/src/libtiletype.rs +++ b/crates/utiles/src/libtiletype.rs @@ -2,20 +2,23 @@ pub enum TileType { Unknown = 0, Gif = 1, Jpg = 2, - Pbf = 3, - Pbfgz = 4, - Png = 5, - Webp = 6, + Json = 3, + Pbf = 4, + Pbfgz = 5, + Png = 6, + Webp = 7, } pub const TILETYPE_UNKNOWN: usize = 0; pub const TILETYPE_GIF: usize = 1; pub const TILETYPE_JPG: usize = 2; -pub const TILETYPE_PBF: usize = 3; -pub const TILETYPE_PBFGZ: usize = 4; -pub const TILETYPE_PNG: usize = 5; -pub const TILETYPE_WEBP: usize = 6; +pub const TILETYPE_JSON: usize = 3; +pub const TILETYPE_PBF: usize = 4; +pub const TILETYPE_PBFGZ: usize = 5; +pub const TILETYPE_PNG: usize = 6; +pub const TILETYPE_WEBP: usize = 7; +#[must_use] pub fn tiletype(buffer: &[u8]) -> TileType { if buffer.len() >= 8 { if buffer[0] == 0x89 @@ -56,16 +59,21 @@ pub fn tiletype(buffer: &[u8]) -> TileType { return TileType::Pbf; } else if buffer[0] == 0x1f && buffer[1] == 0x8b { return TileType::Pbfgz; + // if starts with '{' or '[' json + } else if buffer[0] == 0x7b || buffer[0] == 0x5b { + return TileType::Json; } } TileType::Unknown } +#[must_use] pub fn enum2const(tiletype: TileType) -> usize { match tiletype { TileType::Unknown => TILETYPE_UNKNOWN, TileType::Gif => TILETYPE_GIF, TileType::Jpg => TILETYPE_JPG, + TileType::Json => TILETYPE_JSON, TileType::Pbf => TILETYPE_PBF, TileType::Pbfgz => TILETYPE_PBFGZ, TileType::Png => TILETYPE_PNG, @@ -73,11 +81,13 @@ pub fn enum2const(tiletype: TileType) -> usize { } } +#[must_use] pub fn const2enum(tiletype: usize) -> TileType { match tiletype { TILETYPE_UNKNOWN => TileType::Unknown, TILETYPE_GIF => TileType::Gif, TILETYPE_JPG => TileType::Jpg, + TILETYPE_JSON => TileType::Json, TILETYPE_PBF => TileType::Pbf, TILETYPE_PBFGZ => TileType::Pbfgz, TILETYPE_PNG => TileType::Png, @@ -86,10 +96,12 @@ pub fn const2enum(tiletype: usize) -> TileType { } } +#[must_use] pub fn headers(tiletype: TileType) -> Vec<(&'static str, &'static str)> { match tiletype { TileType::Png => vec![("Content-Type", "image/png")], TileType::Jpg => vec![("Content-Type", "image/jpeg")], + TileType::Json => vec![("Content-Type", "application/json")], TileType::Gif => vec![("Content-Type", "image/gif")], TileType::Webp => vec![("Content-Type", "image/webp")], TileType::Pbf => vec![ @@ -104,12 +116,14 @@ pub fn headers(tiletype: TileType) -> Vec<(&'static str, &'static str)> { } } +#[must_use] pub fn tiletype_str(buffer: &[u8]) -> String { let tiletype = tiletype(buffer); match tiletype { TileType::Unknown => "unknown".to_string(), TileType::Gif => "gif".to_string(), TileType::Jpg => "jpg".to_string(), + TileType::Json => "json".to_string(), TileType::Pbf => "pbf".to_string(), TileType::Pbfgz => "pbfgz".to_string(), TileType::Png => "png".to_string(), diff --git a/crates/utiles/src/lnglat.rs b/crates/utiles/src/lnglat.rs index d04d0476..e76123a6 100644 --- a/crates/utiles/src/lnglat.rs +++ b/crates/utiles/src/lnglat.rs @@ -18,28 +18,34 @@ impl From<(f64, f64)> for LngLat { } impl LngLat { + #[must_use] pub fn new(lng: f64, lat: f64) -> Self { LngLat { xy: coord! { x: lng, y: lat}, } } + #[must_use] pub fn lng(&self) -> f64 { self.xy.x } + #[must_use] pub fn lat(&self) -> f64 { self.xy.y } + #[must_use] pub fn lon(&self) -> f64 { self.xy.x } + #[must_use] pub fn x(&self) -> f64 { self.xy.x } + #[must_use] pub fn y(&self) -> f64 { self.xy.y } diff --git a/crates/utiles/src/mbtiles/metadata2tilejson.rs b/crates/utiles/src/mbtiles/metadata2tilejson.rs new file mode 100644 index 00000000..4edeb930 --- /dev/null +++ b/crates/utiles/src/mbtiles/metadata2tilejson.rs @@ -0,0 +1,72 @@ +use std::error::Error; +use std::fmt::Display; +use std::str::FromStr; + +use serde_json::{Value as JSONValue, Value}; + +use crate::mbtiles::metadata_row::MbtilesMetadataRow; +use tilejson::{tilejson, Bounds, Center, TileJSON}; +use tracing::{info, warn}; + +fn to_val(val: Result, title: &str) -> Option { + match val { + Ok(v) => Some(v), + Err(_err) => { + // let name = &self.filename; + warn!("Unable to parse metadata {title}"); + None + } + } +} + +/// Convert metadata rows to a TileJSON object +/// (ripped from martin-mbtiles thank y'all very much) +pub fn metadata2tilejson( + metadata: Vec, +) -> Result> { + let mut tj = tilejson! {tiles : vec![]}; + // let mut layer_type: Option = None; + let mut json: Option = None; + + for row in metadata { + let name = row.name; + let value = row.value; + match name.as_ref() { + "name" => tj.name = Some(value), + "version" => tj.version = Some(value), + "bounds" => tj.bounds = to_val(Bounds::from_str(value.as_str()), &name), + "center" => tj.center = to_val(Center::from_str(value.as_str()), &name), + "minzoom" => tj.minzoom = to_val(value.parse(), &name), + "maxzoom" => tj.maxzoom = to_val(value.parse(), &name), + "description" => tj.description = Some(value), + "attribution" => tj.attribution = Some(value), + // "type" => layer_type = Some(value), + "legend" => tj.legend = Some(value), + "template" => tj.template = Some(value), + "json" => json = to_val(serde_json::from_str(&value), &name), + "format" | "generator" => { + tj.other.insert(name, Value::String(value)); + } + _ => { + // let file = &filename; + // info!("{file} has an unrecognized metadata value {name}={value}"); + info!("unrecognized metadata value {name}={value}"); + tj.other.insert(name, Value::String(value)); + } + } + } + + if let Some(JSONValue::Object(obj)) = &mut json { + if let Some(value) = obj.remove("vector_layers") { + if let Ok(v) = serde_json::from_value(value) { + tj.vector_layers = Some(v); + } else { + warn!( + "Unable to parse metadata vector_layers value", + // self.filename + ); + } + } + } + Ok(tj) +} diff --git a/crates/utiles/src/mbtiles/metadata_row.rs b/crates/utiles/src/mbtiles/metadata_row.rs new file mode 100644 index 00000000..e5a53ed0 --- /dev/null +++ b/crates/utiles/src/mbtiles/metadata_row.rs @@ -0,0 +1,7 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct MbtilesMetadataRow { + pub name: String, + pub value: String, +} diff --git a/crates/utiles/src/mbtiles/mod.rs b/crates/utiles/src/mbtiles/mod.rs new file mode 100644 index 00000000..7a8643f9 --- /dev/null +++ b/crates/utiles/src/mbtiles/mod.rs @@ -0,0 +1,3 @@ +mod metadata2tilejson; +pub mod metadata_row; +pub use crate::mbtiles::metadata2tilejson::metadata2tilejson; diff --git a/crates/utiles/src/parsing.rs b/crates/utiles/src/parsing.rs new file mode 100644 index 00000000..0c636764 --- /dev/null +++ b/crates/utiles/src/parsing.rs @@ -0,0 +1,107 @@ +use crate::bbox::BBox; +use crate::geojson::geojson_coords; +use geo_types::Coord; +use serde_json::Value; +use tracing::debug; + +// pub fn parse_bbox(s: &str) -> serde_json::Result { +pub fn parse_bbox(s: &str) -> serde_json::Result { + // if the first char is "{" assume it is geojson-like + debug!("parse_bbox: {}", s); + if s.starts_with('{') { + // parse to serde_json::Value + let v: Value = serde_json::from_str(s)?; + // if it has a "bbox" key, use that + if v["bbox"].is_array() { + let bbox: (f64, f64, f64, f64) = serde_json::from_value(v["bbox"].clone())?; + return Ok(BBox::from(bbox)); + } + return Ok(geojson_bounds(s)); + } + + let v: Value = serde_json::from_str(s)?; + + debug!("{}", v); + // Assume a single pair of coordinates represents a CoordTuple + // and a four-element array represents a BBoxTuple + let bbox = match v.as_array().map(|arr| arr.len()) { + Some(2) => { + let coord: (f64, f64) = serde_json::from_value::<(f64, f64)>(v)?; + Ok(BBox::new(coord.0, coord.1, coord.0, coord.1)) + } + Some(4) => { + let bbox: (f64, f64, f64, f64) = serde_json::from_value(v)?; + Ok(BBox::from(bbox)) + } + _ => panic!("Expected a two-element array or a four-element array"), + }; + debug!("bbox: {:?}", bbox); + bbox +} + +pub fn coords2bounds(mut coords: I) -> Option<(f64, f64, f64, f64)> +where + I: Iterator, +{ + // Initialize the bounds with the first coordinate. + let first_coord = coords.next()?; + let mut min_x = first_coord.x; + let mut max_x = first_coord.x; + let mut min_y = first_coord.y; + let mut max_y = first_coord.y; + + // Iterate through the coordinates to find the extremes. + for coord in coords { + if coord.x < min_x { + min_x = coord.x; + } + if coord.x > max_x { + max_x = coord.x; + } + if coord.y < min_y { + min_y = coord.y; + } + if coord.y > max_y { + max_y = coord.y; + } + } + + Some((min_x, min_y, max_x, max_y)) +} +#[must_use] +pub fn geojson_bounds(geojson_str: &str) -> BBox { + let coords = geojson_coords(geojson_str); + let bounds = coords2bounds(coords).unwrap(); + BBox::new(bounds.0, bounds.1, bounds.2, bounds.3) +} + +#[cfg(test)] +mod tests { + use crate::bbox::*; + use crate::parsing::parse_bbox; + + #[test] + fn parse_bbox_simple() { + let string = r#"[-180.0, -85.0, 180.0, 85.0]"#; + let bbox_result = parse_bbox(string); + // assert!(bbox_result.is_ok()); + let bbox = bbox_result.unwrap(); + assert_eq!(bbox, BBox::new(-180.0, -85.0, 180.0, 85.0)); + } + + #[test] + fn parse_bbox_from_coords() { + let string = "[-180.0, -85.0]"; + let bbox_result = parse_bbox(string); + // assert!(bbox_result.is_ok()); + let bbox = bbox_result.unwrap(); + assert_eq!(bbox, BBox::new(-180.0, -85.0, -180.0, -85.0)); + } + + #[test] + fn parse_bbox_bad() { + let string = r#"[-180.0,]"#; + let bbox_result = parse_bbox(string); + assert!(bbox_result.is_err()); + } +} diff --git a/crates/utiles/src/pmtiles.rs b/crates/utiles/src/pmtiles.rs index f047cd8b..f1a6be19 100644 --- a/crates/utiles/src/pmtiles.rs +++ b/crates/utiles/src/pmtiles.rs @@ -1,4 +1,5 @@ -pub fn xyz2id(x: u32, y: u32, z: u8) -> u64 { +#[must_use] +pub fn xyz2pmid(x: u32, y: u32, z: u8) -> u64 { if z == 0 { return 0; } @@ -8,10 +9,12 @@ pub fn xyz2id(x: u32, y: u32, z: u8) -> u64 { } #[allow(dead_code)] -pub fn zxy2id(z: u8, x: u32, y: u32) -> u64 { - xyz2id(x, y, z) +#[must_use] +pub fn zxy2pmid(z: u8, x: u32, y: u32) -> u64 { + xyz2pmid(x, y, z) } +#[must_use] pub fn calculate_h_o(i: u64) -> (u64, u8) { if i == 0 { return (0, 0); @@ -28,7 +31,8 @@ pub fn calculate_h_o(i: u64) -> (u64, u8) { } } -pub fn id2xyz(i: u64) -> (u32, u32, u8) { +#[must_use] +pub fn pmid2xyz(i: u64) -> (u32, u32, u8) { if i == 0 { return (0, 0, 0); } @@ -38,12 +42,14 @@ pub fn id2xyz(i: u64) -> (u32, u32, u8) { } #[allow(dead_code)] -pub fn id2zxy(i: u64) -> (u8, u32, u32) { - let (x, y, z) = id2xyz(i); +#[must_use] +pub fn pmid2zxy(i: u64) -> (u8, u32, u32) { + let (x, y, z) = pmid2xyz(i); (z, x, y) } // Fast parent ID calculation without converting to ZXY (ported from pmtiles go) +#[must_use] pub fn parent_id(i: u64) -> u64 { let mut acc: u64 = 0; let mut last_acc: u64 = 0; @@ -66,25 +72,25 @@ mod tests { #[test] fn test_zxy_to_id() { - assert_eq!(0, zxy2id(0, 0, 0)); - assert_eq!(1, zxy2id(1, 0, 0)); - assert_eq!(2, zxy2id(1, 0, 1)); - assert_eq!(3, zxy2id(1, 1, 1)); - assert_eq!(4, zxy2id(1, 1, 0)); - assert_eq!(5, zxy2id(2, 0, 0)); + assert_eq!(0, zxy2pmid(0, 0, 0)); + assert_eq!(1, zxy2pmid(1, 0, 0)); + assert_eq!(2, zxy2pmid(1, 0, 1)); + assert_eq!(3, zxy2pmid(1, 1, 1)); + assert_eq!(4, zxy2pmid(1, 1, 0)); + assert_eq!(5, zxy2pmid(2, 0, 0)); } #[test] fn test_id_to_zxy() { - let (z, x, y) = id2zxy(0); + let (z, x, y) = pmid2zxy(0); assert_eq!(0, z); assert_eq!(0, x); assert_eq!(0, y); - let (z, x, y) = id2zxy(1); + let (z, x, y) = pmid2zxy(1); assert_eq!(1, z); assert_eq!(0, x); assert_eq!(0, y); - let (z, x, y) = id2zxy(19078479); + let (z, x, y) = pmid2zxy(19078479); assert_eq!(12, z); assert_eq!(3423, x); assert_eq!(1763, y); @@ -95,8 +101,8 @@ mod tests { for z in 0..10 { for x in 0..(1 << z) { for y in 0..(1 << z) { - let id = zxy2id(z, x, y); - let (rz, rx, ry) = id2zxy(id); + let id = zxy2pmid(z, x, y); + let (rz, rx, ry) = pmid2zxy(id); assert_eq!(z, rz); assert_eq!(x, rx); assert_eq!(y, ry); @@ -109,19 +115,19 @@ mod tests { fn test_extremes() { for tz in 0..32 { let dim = (1 << tz) - 1; - let (z, x, y) = id2zxy(zxy2id(tz, 0, 0)); + let (z, x, y) = pmid2zxy(zxy2pmid(tz, 0, 0)); assert_eq!(tz, z); assert_eq!(0, x); assert_eq!(0, y); - let (z, x, y) = id2zxy(zxy2id(z, dim, 0)); + let (z, x, y) = pmid2zxy(zxy2pmid(z, dim, 0)); assert_eq!(tz, z); assert_eq!(dim, x); assert_eq!(0, y); - let (z, x, y) = id2zxy(zxy2id(z, 0, dim)); + let (z, x, y) = pmid2zxy(zxy2pmid(z, 0, dim)); assert_eq!(tz, z); assert_eq!(0, x); assert_eq!(dim, y); - let (z, x, y) = id2zxy(zxy2id(z, dim, dim)); + let (z, x, y) = pmid2zxy(zxy2pmid(z, dim, dim)); assert_eq!(tz, z); assert_eq!(dim, x); assert_eq!(dim, y); @@ -130,26 +136,26 @@ mod tests { #[test] fn test_parent() { - assert_eq!(zxy2id(0, 0, 0), parent_id(zxy2id(1, 0, 0))); + assert_eq!(zxy2pmid(0, 0, 0), parent_id(zxy2pmid(1, 0, 0))); - assert_eq!(zxy2id(1, 0, 0), parent_id(zxy2id(2, 0, 0))); - assert_eq!(zxy2id(1, 0, 0), parent_id(zxy2id(2, 0, 1))); - assert_eq!(zxy2id(1, 0, 0), parent_id(zxy2id(2, 1, 0))); - assert_eq!(zxy2id(1, 0, 0), parent_id(zxy2id(2, 1, 1))); + assert_eq!(zxy2pmid(1, 0, 0), parent_id(zxy2pmid(2, 0, 0))); + assert_eq!(zxy2pmid(1, 0, 0), parent_id(zxy2pmid(2, 0, 1))); + assert_eq!(zxy2pmid(1, 0, 0), parent_id(zxy2pmid(2, 1, 0))); + assert_eq!(zxy2pmid(1, 0, 0), parent_id(zxy2pmid(2, 1, 1))); - assert_eq!(zxy2id(1, 0, 1), parent_id(zxy2id(2, 0, 2))); - assert_eq!(zxy2id(1, 0, 1), parent_id(zxy2id(2, 0, 3))); - assert_eq!(zxy2id(1, 0, 1), parent_id(zxy2id(2, 1, 2))); - assert_eq!(zxy2id(1, 0, 1), parent_id(zxy2id(2, 1, 3))); + assert_eq!(zxy2pmid(1, 0, 1), parent_id(zxy2pmid(2, 0, 2))); + assert_eq!(zxy2pmid(1, 0, 1), parent_id(zxy2pmid(2, 0, 3))); + assert_eq!(zxy2pmid(1, 0, 1), parent_id(zxy2pmid(2, 1, 2))); + assert_eq!(zxy2pmid(1, 0, 1), parent_id(zxy2pmid(2, 1, 3))); - assert_eq!(zxy2id(1, 1, 0), parent_id(zxy2id(2, 2, 0))); - assert_eq!(zxy2id(1, 1, 0), parent_id(zxy2id(2, 2, 1))); - assert_eq!(zxy2id(1, 1, 0), parent_id(zxy2id(2, 3, 0))); - assert_eq!(zxy2id(1, 1, 0), parent_id(zxy2id(2, 3, 1))); + assert_eq!(zxy2pmid(1, 1, 0), parent_id(zxy2pmid(2, 2, 0))); + assert_eq!(zxy2pmid(1, 1, 0), parent_id(zxy2pmid(2, 2, 1))); + assert_eq!(zxy2pmid(1, 1, 0), parent_id(zxy2pmid(2, 3, 0))); + assert_eq!(zxy2pmid(1, 1, 0), parent_id(zxy2pmid(2, 3, 1))); - assert_eq!(zxy2id(1, 1, 1), parent_id(zxy2id(2, 2, 2))); - assert_eq!(zxy2id(1, 1, 1), parent_id(zxy2id(2, 2, 3))); - assert_eq!(zxy2id(1, 1, 1), parent_id(zxy2id(2, 3, 2))); - assert_eq!(zxy2id(1, 1, 1), parent_id(zxy2id(2, 3, 3))); + assert_eq!(zxy2pmid(1, 1, 1), parent_id(zxy2pmid(2, 2, 2))); + assert_eq!(zxy2pmid(1, 1, 1), parent_id(zxy2pmid(2, 2, 3))); + assert_eq!(zxy2pmid(1, 1, 1), parent_id(zxy2pmid(2, 3, 2))); + assert_eq!(zxy2pmid(1, 1, 1), parent_id(zxy2pmid(2, 3, 3))); } } diff --git a/crates/utiles/src/projection.rs b/crates/utiles/src/projection.rs new file mode 100644 index 00000000..63787719 --- /dev/null +++ b/crates/utiles/src/projection.rs @@ -0,0 +1,29 @@ +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Debug, Serialize, Deserialize)] +pub enum Projection { + Geographic, + Mercator, +} + +impl From for Projection { + fn from(s: String) -> Self { + match s.as_str() { + "mercator" => Projection::Mercator, + "geographic" => Projection::Geographic, + _ => { + panic!("Invalid projection: {s}"); + } + } + } +} + +impl fmt::Display for Projection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Projection::Geographic => write!(f, "geographic"), + Projection::Mercator => write!(f, "mercator"), + } + } +} diff --git a/crates/utiles/src/tile.rs b/crates/utiles/src/tile.rs index cfc34f96..b8f4ddba 100644 --- a/crates/utiles/src/tile.rs +++ b/crates/utiles/src/tile.rs @@ -1,12 +1,49 @@ +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Value}; use std::cmp::Ordering; use std::error::Error; +use std::str::FromStr; -use serde::{Deserialize, Serialize}; +use crate::utile; use crate::bbox::BBox; use crate::constants::EPSILON; use crate::lnglat::LngLat; -use crate::{ll, lr, pmtiles, traits, ul, ur, XYZ}; +use crate::projection::Projection; +use crate::tile_feature::TileFeature; +use crate::tile_tuple::XYZ; +use crate::{ + bounds, children, flipy, ll, lr, neighbors, parent, pmtiles, quadkey2tile, + siblings, traits, ul, ur, xy, xyz2quadkey, +}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct TileFeatureGeometry { + #[serde(rename = "type")] + pub type_: String, + pub coordinates: Vec>>, +} + +#[derive(Debug, Serialize)] +pub struct FeatureOptions { + pub fid: Option, // feature id + pub props: Option>, + pub projection: Projection, + pub buffer: Option, + pub precision: Option, +} + +impl Default for FeatureOptions { + fn default() -> Self { + FeatureOptions { + fid: None, + props: None, + projection: Projection::Geographic, + buffer: None, + precision: None, + } + } +} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Tile { @@ -33,7 +70,7 @@ impl traits::Utiles for Tile { } fn bbox(&self) -> BBox { - let (west, south, east, north) = crate::bounds(self.x, self.y, self.z); + let (west, south, east, north) = bounds(self.x, self.y, self.z); BBox { north, south, @@ -81,45 +118,77 @@ impl Ord for Tile { } } +impl FromStr for Tile { + type Err = Box; + + fn from_str(s: &str) -> Result { + // if it starts with '{' assume json obj + if s.starts_with('{') { + // if '{' assume its an obj + return Ok(Tile::from_json_obj(s)); + } else if s.starts_with('[') { + return Ok(Tile::from_json_arr(s)); + } + // assume its a quadkey + let res = quadkey2tile(s); + match res { + Ok(tile) => Ok(tile), + Err(e) => { + panic!("Invalid quadkey: {e}"); + } + } + } +} + impl Tile { + #[must_use] pub fn new(x: u32, y: u32, z: u8) -> Self { Tile { x, y, z } } #[allow(dead_code)] + #[must_use] pub fn valid(&self) -> bool { crate::valid(self.x, self.y, self.z) } + #[must_use] pub fn x(&self) -> u32 { self.x } + #[must_use] pub fn y(&self) -> u32 { self.y } + #[must_use] pub fn z(&self) -> u8 { self.z } + #[must_use] pub fn zoom(&self) -> u8 { self.z } + #[must_use] pub fn bounds(&self) -> (f64, f64, f64, f64) { - crate::bounds(self.x, self.y, self.z) + bounds(self.x, self.y, self.z) } + #[must_use] pub fn pmtileid(&self) -> u64 { - pmtiles::xyz2id(self.x, self.y, self.z) + pmtiles::xyz2pmid(self.x, self.y, self.z) } + #[must_use] pub fn from_pmtileid(id: u64) -> Self { - let (x, y, z) = pmtiles::id2xyz(id); + let (x, y, z) = pmtiles::pmid2xyz(id); Tile::new(x, y, z) } + #[must_use] pub fn fmt_zxy(&self, sep: Option<&str>) -> String { match sep { Some(sep) => format!("{}{}{}{}{}", self.z, sep, self.x, sep, self.y), @@ -127,6 +196,7 @@ impl Tile { } } + #[must_use] pub fn fmt_zxy_ext(&self, ext: &str, sep: Option<&str>) -> String { match sep { Some(sep) => { @@ -136,16 +206,18 @@ impl Tile { } } + #[must_use] pub fn parent_id(&self) -> u64 { pmtiles::parent_id(self.pmtileid()) } pub fn from_quadkey(quadkey: &str) -> Result> { - crate::quadkey2tile(quadkey) + quadkey2tile(quadkey) } + #[must_use] pub fn from_qk(qk: &str) -> Self { - let res = crate::quadkey2tile(qk); + let res = quadkey2tile(qk); match res { Ok(tile) => tile, Err(e) => { @@ -154,14 +226,53 @@ impl Tile { } } + #[must_use] + pub fn from_json_obj(json: &str) -> Self { + let res = serde_json::from_str(json); + match res { + Ok(tile) => tile, + Err(e) => { + panic!("Invalid json_arr: {e}"); + } + } + } + + #[must_use] + pub fn from_json_arr(json: &str) -> Self { + let res = serde_json::from_str(json); + match res { + Ok(tile) => tile, + Err(e) => { + panic!("Invalid json_arr: {e}"); + } + } + } + + #[must_use] + pub fn from_json(json: &str) -> Self { + if json.starts_with('[') { + return Self::from_json_arr(json); + } + Self::from_json_obj(json) + } + + #[must_use] + pub fn from_json_loose(json: &str) -> Self { + let v = serde_json::from_str::(json).unwrap(); + Self::from(v) + } + + #[must_use] pub fn quadkey(&self) -> String { - crate::xyz2quadkey(self.x, self.y, self.z) + xyz2quadkey(self.x, self.y, self.z) } + #[must_use] pub fn qk(&self) -> String { - crate::xyz2quadkey(self.x, self.y, self.z) + xyz2quadkey(self.x, self.y, self.z) } + #[must_use] pub fn from_lnglat_zoom( lng: f64, lat: f64, @@ -201,34 +312,41 @@ impl Tile { } } + #[must_use] pub fn ul(&self) -> LngLat { ul(self.x, self.y, self.z) } + #[must_use] pub fn ll(&self) -> LngLat { ll(self.x, self.y, self.z) } + #[must_use] pub fn ur(&self) -> LngLat { ur(self.x, self.y, self.z) } + #[must_use] pub fn lr(&self) -> LngLat { lr(self.x, self.y, self.z) } + #[must_use] pub fn bbox(&self) -> (f64, f64, f64, f64) { let ul = self.ul(); let lr = self.lr(); (ul.lng(), lr.lat(), lr.lng(), ul.lat()) } + #[must_use] pub fn center(&self) -> LngLat { let ul = self.ul(); let lr = self.lr(); LngLat::new((ul.lng() + lr.lng()) / 2.0, (ul.lat() + lr.lat()) / 2.0) } + #[must_use] pub fn up(&self) -> Self { Self { x: self.x + 1, @@ -237,6 +355,7 @@ impl Tile { } } + #[must_use] pub fn down(&self) -> Self { Self { x: self.x - 1, @@ -245,6 +364,7 @@ impl Tile { } } + #[must_use] pub fn left(&self) -> Self { Self { x: self.x, @@ -253,6 +373,7 @@ impl Tile { } } + #[must_use] pub fn right(&self) -> Self { Self { x: self.x, @@ -261,6 +382,7 @@ impl Tile { } } + #[must_use] pub fn up_left(&self) -> Self { Self { x: self.x + 1, @@ -269,6 +391,7 @@ impl Tile { } } + #[must_use] pub fn up_right(&self) -> Self { Self { x: self.x + 1, @@ -277,6 +400,7 @@ impl Tile { } } + #[must_use] pub fn down_left(&self) -> Self { Self { x: self.x - 1, @@ -285,6 +409,7 @@ impl Tile { } } + #[must_use] pub fn down_right(&self) -> Self { Self { x: self.x - 1, @@ -293,22 +418,27 @@ impl Tile { } } + #[must_use] pub fn neighbors(&self) -> Vec { - crate::neighbors(self.x, self.y, self.z) + neighbors(self.x, self.y, self.z) } + #[must_use] pub fn children(&self, zoom: Option) -> Vec { - crate::children(self.x, self.y, self.z, zoom) + children(self.x, self.y, self.z, zoom) } + #[must_use] pub fn parent(&self, zoom: Option) -> Self { - crate::parent(self.x, self.y, self.z, zoom) + parent(self.x, self.y, self.z, zoom) } + #[must_use] pub fn siblings(&self) -> Vec { - crate::siblings(self.x, self.y, self.z) + siblings(self.x, self.y, self.z) } + #[must_use] pub fn sql_where(&self, flip: Option) -> String { // classic mbtiles sqlite query: // 'SELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?', @@ -319,7 +449,7 @@ impl Tile { "(zoom_level = {} AND tile_column = {} AND tile_row = {})", self.z, self.x, - crate::flipy(self.y, self.z) + flipy(self.y, self.z) ), false => format!( "(zoom_level = {} AND tile_column = {} AND tile_row = {})", @@ -327,4 +457,237 @@ impl Tile { ), } } + + #[must_use] + pub fn json_arr_min(&self) -> String { + format!("[{},{},{}]", self.x, self.y, self.z) + } + + #[must_use] + pub fn json_arr(&self) -> String { + format!("[{}, {}, {}]", self.x, self.y, self.z) + } + + #[must_use] + pub fn json_obj(&self) -> String { + serde_json::to_string(self).unwrap() + } + + #[must_use] + pub fn tuple_string(&self) -> String { + format!("({}, {}, {})", self.x, self.y, self.z) + } + + pub fn feature( + &self, + opts: &FeatureOptions, + ) -> Result> { + let buffer = opts.buffer.unwrap_or(0.0); + let precision = opts.precision.unwrap_or(-1); + // Compute the bounds + let (west, south, east, north) = self.bbox(); + // Handle projected coordinates + let (mut west, mut south, mut east, mut north) = match opts.projection { + // Projection::Geographic=> (west, south, east, north), + Projection::Mercator => { + // let (east_merc, north_merc) = utiles::xy(east, north, Some(false)); + let (west_merc, south_merc) = xy(west, south, None); + let (east_merc, north_merc) = xy(east, north, None); + (west_merc, south_merc, east_merc, north_merc) + } + _ => (west, south, east, north), + }; + + // Apply buffer + west -= buffer; + south -= buffer; + east += buffer; + north += buffer; + + // Apply precision + if precision >= 0 { + let precision_factor = 10_f64.powi(precision); + west = (west * precision_factor).round() / precision_factor; + south = (south * precision_factor).round() / precision_factor; + east = (east * precision_factor).round() / precision_factor; + north = (north * precision_factor).round() / precision_factor; + } + + // Compute bbox and geometry + let bbox = ( + west.min(east), + south.min(north), + west.max(east), + south.max(north), + ); + let xyz = self.tuple_string(); + let geometry_coordinates = vec![vec![ + vec![west, south], + vec![west, north], + vec![east, north], + vec![east, south], + vec![west, south], + ]]; + let mut properties: Map = Map::new(); + properties.insert("title".to_string(), Value::from(format!("XYZ tile {xyz}"))); + properties.extend(opts.props.clone().unwrap_or_default()); + let id = match opts.fid.clone() { + Some(fid) => fid, + None => xyz, + }; + let tile_feature = TileFeature { + id, + type_: "Feature".to_string(), + geometry: TileFeatureGeometry { + type_: "Polygon".to_string(), + coordinates: geometry_coordinates, + }, + bbox, + properties, + }; + Ok(tile_feature) + } +} + +impl From<(u32, u32, u8)> for Tile { + fn from(tuple: (u32, u32, u8)) -> Self { + XYZ::from(tuple).into() + } +} + +impl From<&Map> for Tile { + fn from(map: &Map) -> Self { + let x = map["x"].as_u64().unwrap() as u32; + let y = map["y"].as_u64().unwrap() as u32; + let z = map["z"].as_u64().unwrap() as u8; + utile!(x, y, z) + } +} + +impl From<&Vec> for Tile { + fn from(arr: &Vec) -> Self { + if arr.len() < 3 { + panic!( + "Invalid json value: {}", + serde_json::to_string(&arr).unwrap() + ); + } + let x = arr[0].as_u64().unwrap() as u32; + let y = arr[1].as_u64().unwrap() as u32; + let z = arr[2].as_u64().unwrap() as u8; + Tile::from((x, y, z)) + } +} +impl From> for Tile { + fn from(arr: Vec) -> Self { + Tile::from(&arr) + // + // if arr.len() < 3 { + // panic!( + // "Invalid json value: {}", + // serde_json::to_string(&arr).unwrap() + // ); + // } + // let x = arr[0].as_u64().unwrap() as u32; + // let y = arr[1].as_u64().unwrap() as u32; + // let z = arr[2].as_u64().unwrap() as u8; + // Tile::from((x, y, z)) + } +} + +impl From<&Value> for Tile { + fn from(val: &Value) -> Self { + // is array? [x, y, z] + match val { + Value::Array(v) => { + if v.len() < 3 { + panic!( + "Invalid json value: {}", + serde_json::to_string(&v).unwrap() + ); + } + Tile::from(v) + // let tuple = serde_json::from_value::(val).unwrap(); + // return Tile::from(tuple); + } + Value::Object(v) => { + // if it has a "tile" key, use that + // if has 'tile' key, use that + if v.contains_key("tile") + && v["tile"].is_array() + && v["tile"].as_array().unwrap().len() == 3 + { + let tuple = + serde_json::from_value::(v["tile"].clone()).unwrap(); + return Tile::from(tuple); + } + Tile::from(v) + } + _ => { + panic!("Invalid json value: {val}"); + } + } + } +} + +impl From for Tile { + fn from(val: Value) -> Self { + Tile::from(&val) + } +} + +impl From<&str> for Tile { + fn from(s: &str) -> Self { + Tile::from_json(s) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_json_obj() { + let json_obj = r#"{"x": 1, "y": 2, "z": 3}"#; + let tile = Tile::from_json_obj(json_obj); + assert_eq!(tile, Tile::new(1, 2, 3)); + } + + #[test] + fn parse_json_arr() { + let json_arr = r#"[1, 2, 3]"#; + let tile = Tile::from_json_arr(json_arr); + assert_eq!(tile, Tile::new(1, 2, 3)); + } + + #[test] + fn parse_quadkey() { + let quadkey = "023010203"; + let tile = quadkey.parse::(); + assert_eq!(tile.unwrap(), Tile::new(81, 197, 9)); + } + + #[test] + fn tile_from_value_obj() { + let json_obj = r#"{"x": 1, "y": 2, "z": 3}"#; + let val_obj = serde_json::from_str::(json_obj).unwrap(); + let tile_from_obj = Tile::from(val_obj); + assert_eq!(tile_from_obj, Tile::new(1, 2, 3)); + } + + #[test] + fn tile_from_value_arr() { + let json_arr = r#"[1, 2, 3]"#; + let val_arr = serde_json::from_str::(json_arr).unwrap(); + let tile_from_arr = Tile::from(val_arr); + assert_eq!(tile_from_arr, Tile::new(1, 2, 3)); + } + #[test] + fn tile_from_value_obj_with_array() { + let json_obj_with_tile_array = r#"{"tile": [1, 2, 3]}"#; + let val_obj_with_tile_array = + serde_json::from_str::(json_obj_with_tile_array).unwrap(); + let tile_from_obj_with_tile_array = Tile::from(val_obj_with_tile_array); + assert_eq!(tile_from_obj_with_tile_array, Tile::new(1, 2, 3)); + } } diff --git a/crates/utiles/src/tile_feature.rs b/crates/utiles/src/tile_feature.rs new file mode 100644 index 00000000..862ecc10 --- /dev/null +++ b/crates/utiles/src/tile_feature.rs @@ -0,0 +1,43 @@ +use crate::tile::TileFeatureGeometry; +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Value}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct TileFeature { + pub id: String, + + #[serde(rename = "type")] + pub type_: String, + + pub geometry: TileFeatureGeometry, + pub bbox: (f64, f64, f64, f64), + pub properties: Map, +} + +impl TileFeature { + pub fn to_json(&self) -> String { + serde_json::to_string(self).unwrap() + } + + pub fn bbox_lons(&self) -> Vec { + vec![self.bbox.0, self.bbox.2] + } + + pub fn bbox_lats(&self) -> Vec { + vec![self.bbox.1, self.bbox.3] + } + + pub fn extents_string(&self) -> String { + format!( + "{} {} {} {}", + self.bbox.0, self.bbox.1, self.bbox.2, self.bbox.3 + ) + } + + pub fn bbox_json(&self) -> String { + format!( + "[{},{},{},{}]", + self.bbox.0, self.bbox.1, self.bbox.2, self.bbox.3 + ) + } +} diff --git a/crates/utiles/src/tile_range.rs b/crates/utiles/src/tile_range.rs index 0118c0ed..a75a488c 100644 --- a/crates/utiles/src/tile_range.rs +++ b/crates/utiles/src/tile_range.rs @@ -1,3 +1,4 @@ +#[derive(Debug)] pub struct TileRange { curx: u32, cury: u32, @@ -9,6 +10,7 @@ pub struct TileRange { } impl TileRange { + #[must_use] pub fn new(minx: u32, maxx: u32, miny: u32, maxy: u32, zoom: u8) -> Self { Self { curx: minx, @@ -21,26 +23,33 @@ impl TileRange { } } + #[must_use] pub fn minx(&self) -> u32 { self.minx } + #[must_use] pub fn maxx(&self) -> u32 { self.maxx } + #[must_use] pub fn miny(&self) -> u32 { self.miny } + #[must_use] pub fn maxy(&self) -> u32 { self.maxy } + #[must_use] pub fn zoom(&self) -> u8 { self.zoom } + #[must_use] pub fn length(&self) -> u64 { ((self.maxx - self.minx + 1) * (self.maxy - self.miny + 1)) as u64 } + #[must_use] pub fn sql_where(&self, flip: Option) -> String { // classic mbtiles sqlite query: // 'SELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?', @@ -82,21 +91,25 @@ impl Iterator for TileRange { } } +#[derive(Debug)] pub struct TileRanges { ranges: Vec, } impl TileRanges { + #[must_use] pub fn new(minx: u32, maxx: u32, miny: u32, maxy: u32, zoom: u8) -> Self { Self { ranges: vec![TileRange::new(minx, maxx, miny, maxy, zoom)], } } + #[must_use] pub fn length(&self) -> u64 { self.ranges.iter().map(|r| r.length()).sum() } + #[must_use] pub fn sql_where(&self, flip: Option) -> String { self.ranges .iter() diff --git a/crates/utiles/src/tile_tuple.rs b/crates/utiles/src/tile_tuple.rs new file mode 100644 index 00000000..3fbf2b87 --- /dev/null +++ b/crates/utiles/src/tile_tuple.rs @@ -0,0 +1,11 @@ +use serde::Deserialize; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize)] +#[allow(clippy::upper_case_acronyms)] +pub struct XYZ(pub u32, pub u32, pub u8); + +impl From<(u32, u32, u8)> for XYZ { + fn from(xyz: (u32, u32, u8)) -> Self { + XYZ(xyz.0, xyz.1, xyz.2) + } +} diff --git a/crates/utiles/src/tilejson.rs b/crates/utiles/src/tilejson.rs new file mode 100644 index 00000000..5fdd7068 --- /dev/null +++ b/crates/utiles/src/tilejson.rs @@ -0,0 +1,14 @@ +use serde_json; +use tilejson::TileJSON; + +#[must_use] +pub fn tilejson_stringify(tj: &TileJSON, fmt: Option) -> String { + match fmt { + Some(false) => serde_json::to_string(&tj).unwrap(), + _ => serde_json::to_string_pretty(&tj).unwrap(), + } +} + +pub fn tilejson_parse(s: &str) -> Result { + serde_json::from_str(s) +} diff --git a/crates/utilesqlite/Cargo.lock b/crates/utilesqlite/Cargo.lock new file mode 100644 index 00000000..6464e641 --- /dev/null +++ b/crates/utilesqlite/Cargo.lock @@ -0,0 +1,741 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fast_hilbert" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ec2bbe15af87954c739e236021f4411766c0f2b9c4a5f0b9317bcf6048ebf8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "geo-types" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9705398c5c7b26132e74513f4ee7c1d7dafd786004991b375c172be2be0eecaa" +dependencies = [ + "approx", + "num-traits", + "serde", +] + +[[package]] +name = "geojson" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d728c1df1fbf328d74151efe6cb0586f79ee813346ea981add69bd22c9241b" +dependencies = [ + "geo-types", + "log", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[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.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +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", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[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 = "rusqlite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" +dependencies = [ + "bitflags 2.4.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_tuple" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f025b91216f15a2a32aa39669329a475733590a015835d1783549a56d09427" +dependencies = [ + "serde", + "serde_tuple_macros", +] + +[[package]] +name = "serde_tuple_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4076151d1a2b688e25aaf236997933c66e18b870d0369f8b248b8ab2be630d7e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "tilejson" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a16212e5ea60f2b406835981338a1df9b206fab312e3470502d60f69e590a9c9" +dependencies = [ + "serde", + "serde_json", + "serde_tuple", +] + +[[package]] +name = "tokio" +version = "1.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "tokio-rusqlite" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aa66395f5ff117faee90c9458232c936405f9227ad902038000b74b3bc1feac" +dependencies = [ + "crossbeam-channel", + "rusqlite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utiles" +version = "0.0.1" +dependencies = [ + "anyhow", + "fast_hilbert", + "geo-types", + "geojson", + "serde", + "serde_json", + "thiserror", + "tilejson", + "tracing", +] + +[[package]] +name = "utilesqlite" +version = "0.1.0" +dependencies = [ + "rusqlite", + "serde", + "serde_json", + "tilejson", + "tokio", + "tokio-rusqlite", + "tracing", + "utiles", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[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_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[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_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[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_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "zerocopy" +version = "0.7.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd369a67c0edfef15010f980c3cbe45d7f651deac2cd67ce097cd801de16557" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f140bda219a26ccc0cdb03dba58af72590c53b22642577d88a927bc5c87d6b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] diff --git a/crates/utilesqlite/Cargo.toml b/crates/utilesqlite/Cargo.toml new file mode 100644 index 00000000..39cbd9d0 --- /dev/null +++ b/crates/utilesqlite/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "utilesqlite" +version = "0.1.0" +edition = "2021" + +[lib] +name = "utilesqlite" +path = "src/lib.rs" + +[[bin]] +name = "utilesql" +path = "src/bin.rs" + +[dependencies] +rusqlite = { version = "0.29.0", features = ["bundled", "vtab", "blob"] } +tilejson = "0.3.2" +tracing = { version = "0.1.40", features = [] } +tokio = { version = "1.33.0", features = ["full"] } +tokio-rusqlite = "0.4.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.96" +utiles = { path = "../utiles" } \ No newline at end of file diff --git a/crates/utilesqlite/src/bin.rs b/crates/utilesqlite/src/bin.rs new file mode 100644 index 00000000..57c78787 --- /dev/null +++ b/crates/utilesqlite/src/bin.rs @@ -0,0 +1,5 @@ +#[tokio::main] +async fn main() -> tokio_rusqlite::Result<()> { + println!("utilesqlite::main() ~ TBD"); + Ok(()) +} diff --git a/crates/utilesqlite/src/lib.rs b/crates/utilesqlite/src/lib.rs new file mode 100644 index 00000000..454b6fd1 --- /dev/null +++ b/crates/utilesqlite/src/lib.rs @@ -0,0 +1 @@ +pub mod mbtiles; diff --git a/crates/utilesqlite/src/mbtiles.rs b/crates/utilesqlite/src/mbtiles.rs new file mode 100644 index 00000000..02779ab5 --- /dev/null +++ b/crates/utilesqlite/src/mbtiles.rs @@ -0,0 +1,67 @@ +use std::error::Error; +use std::path::Path; + +use rusqlite::{Connection, Result as RusqliteResult}; +use tilejson::TileJSON; +use tracing::error; +use utiles::mbtiles::metadata2tilejson; +use utiles::mbtiles::metadata_row::MbtilesMetadataRow; + +pub struct Mbtiles { + conn: Connection, +} + +impl Mbtiles { + pub fn from_conn(conn: Connection) -> Mbtiles { + Mbtiles { conn } + } + + pub fn metadata(&self) -> RusqliteResult> { + mbtiles_metadata(&self.conn) + } + + pub fn tilejson(&self) -> Result> { + let metadata = self.metadata()?; + let tj = metadata2tilejson(metadata); + match tj { + Ok(t) => Ok(t), + Err(e) => { + error!("Error parsing metadata to TileJSON: {}", e); + Err(e) + } + } + // return Ok(tj); + } + + pub fn from_filepath(fspath: &str) -> RusqliteResult { + let conn = Connection::open(fspath)?; + let mbt = Mbtiles { conn }; + Ok(mbt) + } + + pub fn from_filepath_str(fspath: &str) -> Result> { + let conn = Connection::open(fspath)?; + let mbt = Mbtiles { conn }; + Ok(mbt) + } +} + +impl From<&Path> for Mbtiles { + fn from(path: &Path) -> Self { + let conn = Connection::open(path).unwrap(); + Mbtiles { conn } + } +} + +pub fn mbtiles_metadata(conn: &Connection) -> RusqliteResult> { + let mut stmt = conn.prepare("SELECT name, value FROM metadata")?; + let mdata = stmt + .query_map([], |row| { + Ok(MbtilesMetadataRow { + name: row.get(0)?, + value: row.get(1)?, + }) + })? + .collect::, rusqlite::Error>>()?; + Ok(mdata) +} diff --git a/noxfile.py b/noxfile.py index e69de29b..63e5aa90 100644 --- a/noxfile.py +++ b/noxfile.py @@ -0,0 +1,34 @@ +"""Nox sessions for linting, docs, and testing.""" +from __future__ import annotations + +import argparse +import os +import shutil +from pathlib import Path + +import nox + +DIR = Path(__file__).parent.resolve() + +nox.options.sessions = ["test"] + +def _session_install_test_deps(session: nox.Session) -> None: + session.install("pytest", "hypothesis", "pytest-cov", "pytest-benchmark", "tomli") + +@nox.session +def test(session: nox.Session) -> None: + """Run the unit and regular tests.""" + session.install("maturin") + _session_install_test_deps(session) + session.run("maturin", "develop", "--release", "--extras=test") + session.run("pytest") + +@nox.session +def test_wheel(session: nox.Session) -> None: + """Run the unit and regular tests.""" + # install from dist... + session.install("utiles", ) + session.install("maturin") + _session_install_test_deps(session) + session.run("maturin", "build", "--release", "--extras=test") + session.run("pytest") diff --git a/pyproject.toml b/pyproject.toml index aa41103a..e67decb9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["maturin>=0.14,<0.15"] +requires = ["maturin>=0.15"] build-backend = "maturin" [project] @@ -20,6 +20,8 @@ dependencies = [ [project.scripts] utiles = "utiles.cli:cli" +ut = "utiles.cli:cli" +utilesv1 = "utiles._legacy.cli:cli" [project.entry-points."rasterio.rio_plugins"] utiles = "utiles.rio_plugin:rio_utiles" @@ -28,80 +30,29 @@ ut = "utiles.rio_plugin:rio_ut" [tool.maturin] python-source = "python" features = ["pyo3/extension-module"] -module-name = "utiles.libutiles" +bindings = "pyo3" +module-name = "utiles._utiles" [tool.pytest.ini_options] testpaths = [ - "tests", -# "python" + "tests" ] addopts = [ - "--doctest-modules" + "--doctest-modules", + "-v" ] markers = [ "slow: marks tests as slow (deselect with '-m \"not slow\"')", "bench" ] -# ============================================================================= -[tool.hatch.envs.default] -dependencies = [ - "coverage[toml]>=6.5", - "mercantile", - "click", - "typing_extensions", - "tomli", - "hypothesis", - "maturin", - "pytest", - "pytest-benchmark", - "pytest-cov", -] - -[tool.hatch.envs.default.scripts] -test = "pytest {args:tests}" -test-cov = "coverage run -m pytest {args:tests}" -cov-report = [ - "- coverage combine", - "coverage report", -] -cov = [ - "test-cov", - "cov-report", -] - -[[tool.hatch.envs.all.matrix]] -python = ["3.8", "3.9", "3.10", "3.11", "3.12"] - -[tool.hatch.envs.lint] -detached = true -dependencies = [ - "black>=23.1.0", - "mypy>=1.0.0", - "ruff>=0.0.265", -] -[tool.hatch.envs.lint.scripts] -typing = "mypy --install-types --non-interactive {args:utiles tests}" -style = [ - "ruff {args:.}", - "black --check --diff {args:.}", -] -fmt = [ - "black {args:.}", - "ruff --fix {args:.}", - "style", -] -all = [ - "style", - "typing", -] [tool.black] -target-version = ["py37"] +target-version = ["py38"] line-length = 88 [tool.ruff] -target-version = "py37" +target-version = "py38" line-length = 88 select = [ "A", @@ -159,6 +110,7 @@ exclude = [ "venv", ] ignore = [ + "TID252", "A003", # Allow non-abstract empty methods in abstract base classes "B027", @@ -219,3 +171,56 @@ exclude_lines = [ [tool.mypy] strict = true ignore_missing_imports = true + +# ============================================================================= +# [tool.hatch.envs.default] +# dependencies = [ +# "coverage[toml]>=6.5", +# "mercantile", +# "click", +# "typing_extensions", +# "tomli", +# "hypothesis", +# "maturin", +# "pytest", +# "pytest-benchmark", +# "pytest-cov", +# ] + +# [tool.hatch.envs.default.scripts] +# test = "pytest {args:tests}" +# test-cov = "coverage run -m pytest {args:tests}" +# cov-report = [ +# "- coverage combine", +# "coverage report", +# ] +# cov = [ +# "test-cov", +# "cov-report", +# ] + +# [[tool.hatch.envs.all.matrix]] +# python = ["3.8", "3.9", "3.10", "3.11", "3.12"] + +# [tool.hatch.envs.lint] +# detached = true +# dependencies = [ +# "black>=23.1.0", +# "mypy>=1.0.0", +# "ruff>=0.0.265", +# ] +# [tool.hatch.envs.lint.scripts] +# typing = "mypy --install-types --non-interactive {args:utiles tests}" +# style = [ +# "ruff {args:.}", +# "black --check --diff {args:.}", +# ] +# fmt = [ +# "black {args:.}", +# "ruff --fix {args:.}", +# "style", +# ] +# all = [ +# "style", +# "typing", +# ] diff --git a/python/utiles/__about__.py b/python/utiles/__about__.py index 7ca6fbfb..82e2e771 100644 --- a/python/utiles/__about__.py +++ b/python/utiles/__about__.py @@ -1,8 +1,15 @@ """Package metadata/info""" -from utiles.libutiles import __version_lib__ +from utiles._utiles import __build_profile__, __version_lib__ -__all__ = ("__title__", "__description__", "__pkgroot__", "__version__") +__all__ = ( + "__title__", + "__description__", + "__pkgroot__", + "__version__", + "__version_lib__", + "__build_profile__", +) __title__ = "utiles" __description__ = "utiles = utils + tiles + rust" __pkgroot__ = __file__.replace("__about__.py", "").rstrip("/\\") diff --git a/python/utiles/__init__.py b/python/utiles/__init__.py index 7a1869f1..c90a6204 100644 --- a/python/utiles/__init__.py +++ b/python/utiles/__init__.py @@ -4,10 +4,12 @@ import math from typing import List, Sequence, Tuple, Union -from utiles.libutiles import ( +from utiles._utiles import ( TILETYPE_GIF, TILETYPE_JPG, + TILETYPE_JSON, TILETYPE_PBF, + TILETYPE_PBFGZ, TILETYPE_PNG, TILETYPE_UNKNOWN, TILETYPE_WEBP, @@ -47,6 +49,7 @@ tiletype_str, truncate_lnglat, ul, + ut_cli, xy, xy_bounds, xyz, @@ -63,7 +66,9 @@ "LngLatBbox", "TILETYPE_GIF", "TILETYPE_JPG", + "TILETYPE_JSON", "TILETYPE_PBF", + "TILETYPE_PBFGZ", "TILETYPE_PNG", "TILETYPE_UNKNOWN", "TILETYPE_WEBP", @@ -102,6 +107,7 @@ "tiletype_str", "truncate_lnglat", "ul", + "ut_cli", "xy", "xy_bounds", "xyz", @@ -148,5 +154,9 @@ def tiletile_str(n: int) -> str: return "webp" elif n == TILETYPE_PBF: return "pbf" + elif n == TILETYPE_PBFGZ: + return "pbfgz" + elif n == TILETYPE_JSON: + return "json" else: return "unknown" diff --git a/python/utiles/__main__.py b/python/utiles/__main__.py index 318c6fab..bc3aed63 100644 --- a/python/utiles/__main__.py +++ b/python/utiles/__main__.py @@ -5,15 +5,71 @@ import sys from typing import Dict, Union -from utiles import libutiles +from utiles import _utiles as libutiles from utiles.__about__ import __pkgroot__, __title__, __version__ +def _nbytes_str(nbytes: Union[int, float]) -> str: + """Format nbytesber of bytes to human readable form + + Ripped from `fmts` library which I wrote... + + ref: https://github.com/dynamic-graphics-inc/dgpy-libs/blob/main/libs/fmts/README.md + + Args: + nbytes: number of bytes + + Returns: + str: nbytesber of bytes formatted + + Raises: + ValueError: If given number of bytes is invalid/negative + + Examples: + >>> _nbytes_str(100) + '100.0 bytes' + >>> _nbytes_str(1000) + '1000.0 bytes' + >>> _nbytes_str(10000) + '9.8 KB' + >>> _nbytes_str(100000) + '97.7 KB' + >>> _nbytes_str(1000000) + '976.6 KB' + >>> _nbytes_str(10_000_000) + '9.5 MB' + >>> _nbytes_str(100_000_000) + '95.4 MB' + >>> _nbytes_str(1000000000) + '953.7 MB' + >>> _nbytes_str(10000000000) + '9.3 GB' + >>> _nbytes_str(100000000000) + '93.1 GB' + >>> _nbytes_str(1000000000000) + '931.3 GB' + >>> _nbytes_str(10000000000000) + '9.1 TB' + >>> _nbytes_str(100000000000000) + '90.9 TB' + + """ + for x in ["bytes", "KB", "MB", "GB", "TB"]: + if nbytes < 1024.0 or x == "TB": + _str = f"{nbytes:3.1f} {x}" + return _str + nbytes /= 1024.0 + msg = f"Invalid number of bytes: {nbytes}" + raise ValueError(msg) # pragma: no cover + + def _utiles_ext_info() -> Dict[str, Union[str, int]]: size = os.path.getsize(libutiles.__file__) return { "abspath": os.path.abspath(libutiles.__file__), "fsize": size, + "fsize_str": _nbytes_str(size), + "build_profile": libutiles.__build_profile__, } @@ -36,3 +92,7 @@ def main() -> None: if __name__ == "__main__": if sys.argv[-1].endswith("__main__.py"): main() + else: + from utiles._cli import cli + + cli() diff --git a/python/utiles/_click.py b/python/utiles/_click.py new file mode 100644 index 00000000..5729c3fb --- /dev/null +++ b/python/utiles/_click.py @@ -0,0 +1,47 @@ +"""Utiles cli wrapped w/ click""" +from __future__ import annotations + +import logging + +import click + +from utiles import __version__ +from utiles.cli import cli + +logger = logging.getLogger(__name__) + + +class NoHelpCommand(click.Command): + def get_help_option(self, _ctx: click.Context) -> None: + return None + + +# The CLI command group. +def _click_cli(name: str) -> NoHelpCommand: + @click.command( + name=name, + cls=NoHelpCommand, + help="utiles cli (python-rust)", + no_args_is_help=False, + context_settings={ + "ignore_unknown_options": True, + "allow_extra_args": True, + }, + ) + @click.version_option(version=__version__, message="%(version)s") + def _cli_fn() -> None: + """Execute the main utiles command""" + try: + cli() + except Exception as e: + logger.error(e) + raise click.BadParameter(str(e)) from e + + return _cli_fn + + +utiles_click = _click_cli("utiles") +ut_click = _click_cli("ut") + +if __name__ == "__main__": + utiles_click() diff --git a/python/utiles/_legacy/__init__.py b/python/utiles/_legacy/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python/utiles/_legacy/cli.py b/python/utiles/_legacy/cli.py new file mode 100644 index 00000000..ee7a19d1 --- /dev/null +++ b/python/utiles/_legacy/cli.py @@ -0,0 +1,581 @@ +"""Utiles cli""" +from __future__ import annotations + +import json +import logging +import sys +from typing import Any, Dict, Iterable, List, Optional, Tuple, Union + +import click + +import utiles + + +def configure_logging(verbosity: int) -> None: + """Configure logging level + + Parameters + ---------- + verbosity : int + The number of `-v` options from the command line. + + Returns + ------- + None + """ + log_level = max(10, 30 - 10 * verbosity) + logging.basicConfig(stream=sys.stderr, level=log_level) + + +logger = logging.getLogger(__name__) + +RS = "\x1e" + + +def normalize_input(input: str) -> List[str]: + """Normalize file or string input.""" + try: + src = click.open_file(input).readlines() + except OSError: + src = [input] + return src + + +def iter_lines(lines: List[str]) -> Iterable[str]: + """Iterate over lines of input, stripping and skipping.""" + for line in lines: + line_stripped = line.strip() + if line_stripped: + yield line_stripped + + +# The CLI command group. +@click.group(name="utiles", help="utiles cli (python)") +@click.option("--verbose", "-v", count=True, help="Increase verbosity.") +@click.option("--quiet", "-q", count=True, help="Decrease verbosity.") +@click.version_option(version=utiles.__version__, message="%(version)s") +@click.pass_context +def cli(ctx: click.Context, verbose: int, quiet: int) -> None: + """Execute the main utiles command""" + verbosity = verbose - quiet + configure_logging(verbosity) + ctx.obj = {} + ctx.obj["verbosity"] = verbosity + + +# Commands are below. + + +# The shapes command. +@cli.command(short_help="Print the shapes of tiles as GeoJSON.") +# This input is either a filename, stdin, or a string. +@click.argument("input", default="-", required=False) +# Coordinate precision option. +@click.option( + "--precision", type=int, default=None, help="Decimal precision of coordinates." +) +# JSON formatting options. +@click.option( + "--indent", default=None, type=int, help="Indentation level for JSON output" +) +@click.option( + "--compact/--no-compact", default=False, help="Use compact separators (',', ':')." +) +# Geographic (default) or Mercator switch. +@click.option( + "--geographic", + "projected", + flag_value="geographic", + default=True, + help="Output in geographic coordinates (the default).", +) +@click.option( + "--mercator", + "projected", + flag_value="mercator", + help="Output in Web Mercator coordinates.", +) +@click.option( + "--seq", + is_flag=True, + default=False, + help="Write a RS-delimited JSON sequence (default is LF).", +) +# GeoJSON feature (default) or collection switch. Meaningful only +# when --x-json-seq is used. +@click.option( + "--feature", + "output_mode", + flag_value="feature", + default=True, + help="Output as sequence of GeoJSON features (the default).", +) +@click.option( + "--bbox", + "output_mode", + flag_value="bbox", + help="Output as sequence of GeoJSON bbox arrays.", +) +@click.option( + "--collect", + is_flag=True, + default=False, + help="Output as a GeoJSON feature collections.", +) +# Optionally write out bboxen in a form that goes +# straight into GDAL utilities like gdalwarp. +@click.option( + "--extents/--no-extents", + default=False, + help="Write shape extents as ws-separated strings (default is False).", +) +# Optionally buffer the shapes by shifting the x and y values of each +# vertex by a constant number of decimal degrees or meters (depending +# on whether --geographic or --mercator is in effect). +@click.option( + "--buffer", + type=float, + default=None, + help="Shift shape x and y values by a constant number", +) +@click.pass_context +def shapes( + _ctx: click.Context, + input: str, + precision: Optional[int] = None, + indent: Optional[int] = None, + projected: str = "geographic", + output_mode: str = "feature", + buffer: Optional[float] = None, + compact: bool = False, + seq: bool = False, + collect: bool = False, + extents: bool = False, +) -> None: + """Print tiles as GeoJSON feature collections or sequences. + + Input may be a compact newline-delimited sequences of JSON or + a pretty-printed ASCII RS-delimited sequence of JSON (like + https://tools.ietf.org/html/rfc8142 and + https://tools.ietf.org/html/rfc7159). + + Tile descriptions may be either an [x, y, z] array or a JSON + object of the form + + {"tile": [x, y, z], "properties": {"name": "foo", ...}} + + In the latter case, the properties object will be used to update + the properties object of the output feature. + + Example: + + \b + echo "[486, 332, 10]" | utiles shapes --precision 4 --bbox + [-9.1406, 53.1204, -8.7891, 53.3309] + + """ + dump_kwds: Dict[str, Union[bool, int, Tuple[str, ...]]] = {"sort_keys": True} + if indent: + dump_kwds["indent"] = indent + if compact: + dump_kwds["separators"] = (",", ":") + + src = normalize_input(input) + features = [] + col_xs = [] + col_ys = [] + + for _i, line in enumerate(iter_lines(src)): + obj = json.loads(line) + if isinstance(obj, dict): + x, y, z = obj["tile"][:3] + props = obj.get("properties") + fid = obj.get("id") + elif isinstance(obj, list): + x, y, z = obj[:3] + props = {} + fid = None + else: + msg = f"{obj}" + raise click.BadParameter(msg, param=input, param_hint="input") # type: ignore + + feature = utiles.feature( + (x, y, z), + fid=fid, + props=props, + projected=projected, + buffer=buffer, + precision=precision, + ) + bbox = feature["bbox"] + w, s, e, n = bbox + col_xs.extend([w, e]) + col_ys.extend([s, n]) + + if collect: + features.append(feature) + elif extents: + click.echo(" ".join(map(str, bbox))) + else: + if seq: + click.echo(RS) + if output_mode == "bbox": + click.echo(json.dumps(bbox, **dump_kwds)) # type: ignore + elif output_mode == "feature": + click.echo(json.dumps(feature, **dump_kwds)) # type: ignore + + if collect and features: + bbox = [min(col_xs), min(col_ys), max(col_xs), max(col_ys)] + click.echo( + json.dumps( + {"type": "FeatureCollection", "bbox": bbox, "features": features}, + **dump_kwds, # type: ignore + ) + ) + + +# The tiles command. +@cli.command( + short_help="Print tiles that overlap or contain a lng/lat point, " + "bounding box, or GeoJSON objects." +) +# Mandatory Mercator zoom level argument. +@click.argument("zoom", type=int, default=-1) +# This input is either a filename, stdin, or a string. +# Has to follow the zoom arg. +@click.argument("input", default="-", required=False) +@click.option( + "--seq/--lf", + default=False, + help="Write a RS-delimited JSON sequence (default is LF).", +) +@click.pass_context +def tiles( + _ctx: click.Context, + zoom: int = -1, + input: str = "-", + seq: bool = False, +) -> None: + """Lists Web Mercator tiles at ZOOM level intersecting + GeoJSON [west, south, east, north] bounding boxen, features, or + collections read from stdin. Output is a JSON + [x, y, z] array. + + Input may be a compact newline-delimited sequences of JSON or + a pretty-printed ASCII RS-delimited sequence of JSON (like + https://tools.ietf.org/html/rfc8142 and + https://tools.ietf.org/html/rfc7159). + + Example: + + \b + $ echo "[-105.05, 39.95, -105, 40]" | utiles tiles 12 + [852, 1550, 12] + [852, 1551, 12] + [853, 1550, 12] + [853, 1551, 12] + + """ + src = iter(normalize_input(input)) + first_line = next(src) + + # If input is RS-delimited JSON sequence. + if first_line.startswith(RS): + + def feature_gen() -> Iterable[Dict[str, Any]]: + buffer = first_line.strip(RS) + for line in src: + if line.startswith(RS): + if buffer: + yield json.loads(buffer) + buffer = line.strip(RS) + else: + buffer += line + else: + yield json.loads(buffer) + + else: + + def feature_gen() -> Iterable[Dict[str, Any]]: + yield json.loads(first_line) + for line in src: + yield json.loads(line) + + for obj in feature_gen(): + if isinstance(obj, list): + bbox = obj + if len(bbox) == 2: + bbox += bbox + elif len(bbox) != 4: + msg = f"{bbox}" + raise click.BadParameter(msg, param=input, param_hint="input") + elif isinstance(obj, dict): + if "bbox" in obj: + bbox = obj["bbox"] + else: + bbox = utiles.geojson_bounds(obj) + + west, south, east, north = bbox + epsilon = 1.0e-10 + + if east != west and north != south: + # 2D bbox + # shrink the bounds a small amount so that + # shapes/tiles round trip. + west += epsilon + south += epsilon + east -= epsilon + north -= epsilon + + for tile in utiles.tiles(west, south, east, north, [zoom], truncate=False): + vals = (tile.x, tile.y, zoom) + output = json.dumps(vals) + if seq: + click.echo(RS) + click.echo(output) + + +# The bounding-tile command. +@cli.command( + "bounding-tile", + short_help="Print the bounding tile of a lng/lat point, " + "bounding box, or GeoJSON objects.", +) +# This input is either a filename, stdin, or a string. +@click.argument("input", default="-", required=False) +@click.option( + "--seq/--lf", + default=False, + help="Write a RS-delimited JSON sequence (default is LF).", +) +@click.pass_context +def bounding_tile(_ctx: click.Context, input: str, seq: bool = False) -> None: + """Print the Web Mercator tile at ZOOM level bounding + GeoJSON [west, south, east, north] bounding boxes, features, or + collections read from stdin. + + Input may be a compact newline-delimited sequences of JSON or + a pretty-printed ASCII RS-delimited sequence of JSON (like + https://tools.ietf.org/html/rfc8142 and + https://tools.ietf.org/html/rfc7159). + + Example: + + \b + echo "[-105.05, 39.95, -105, 40]" | utiles bounding-tile + [426, 775, 11] + + """ + src = iter(normalize_input(input)) + first_line = next(src) + + # If input is RS-delimited JSON sequence. + if first_line.startswith(RS): + + def feature_gen() -> Iterable[Dict[str, Any]]: + buffer = first_line.strip(RS) + for line in src: + if line.startswith(RS): + if buffer: + yield json.loads(buffer) + buffer = line.strip(RS) + else: + buffer += line + else: + yield json.loads(buffer) + + else: + + def feature_gen() -> Iterable[Dict[str, Any]]: + yield json.loads(first_line) + for line in src: + yield json.loads(line) + + for obj in feature_gen(): + if isinstance(obj, list): + bbox = obj + if len(bbox) == 2: + bbox += bbox + elif len(bbox) != 4: + msg = f"{bbox}" + raise click.BadParameter(msg, param=input, param_hint="input") + + elif isinstance(obj, dict): + if "bbox" in obj: + bbox = obj["bbox"] + else: + bbox = utiles.geojson_bounds(obj) + + west, south, east, north = bbox + vals = utiles.bounding_tile(west, south, east, north, truncate=False) + + # print(vals) + # output = json.dumps(vals) + output = vals.json(obj=False) + + if seq: + click.echo(RS) + + click.echo(output) + + +# The children command. +@cli.command(short_help="Print the children of the tile.") +@click.argument("input", default="-", required=False) +@click.option( + "--depth", + type=int, + default=1, + help="Number of zoom levels to traverse (default is 1).", +) +@click.pass_context +def children(_ctx: click.Context, input: str, depth: int = 1) -> None: + """Takes [x, y, z] tiles as input and writes children to stdout + in the same form. + + Input may be a compact newline-delimited sequences of JSON or + a pretty-printed ASCII RS-delimited sequence of JSON (like + https://tools.ietf.org/html/rfc8142 and + https://tools.ietf.org/html/rfc7159). + + Example: + + \b + echo "[486, 332, 10]" | utiles children + [972, 664, 11] + [973, 664, 11] + [973, 665, 11] + [972, 665, 11] + + """ + src = normalize_input(input) + for line in iter_lines(src): + tiles = [json.loads(line)[:3]] + for _i in range(depth): + tiles = sum([utiles.children(t) for t in tiles], []) + for t in tiles: + click.echo(t.json(obj=False)) + + +# The parent command. +@cli.command(short_help="Print the parent tile.") +@click.argument("input", default="-", required=False) +@click.option( + "--depth", + type=int, + default=1, + help="Number of zoom levels to traverse (default is 1).", +) +@click.pass_context +def parent(_ctx: click.Context, input: str, depth: int = 1) -> None: + """Takes [x, y, z] tiles as input and writes parents to stdout + in the same form. + + Input may be a compact newline-delimited sequences of JSON or + a pretty-printed ASCII RS-delimited sequence of JSON (like + https://tools.ietf.org/html/rfc8142 and + https://tools.ietf.org/html/rfc7159). + + Example: + + \b + echo "[486, 332, 10]" | utiles parent + [243, 166, 9] + + """ + src = normalize_input(input) + for line in iter_lines(src): + parsed = json.loads(line)[:3] + tile = utiles.parse_tile_arg(parsed) + if tile[2] - depth < 0: + msg = f"Invalid parent level: {tile[2] - depth}" + raise click.UsageError(msg) + for _i in range(depth): + ptile = utiles.parent(tile) + if ptile is None: + msg = f"Invalid parent level: {tile[2] - depth}" + raise click.UsageError(msg) + tile = ptile + output = tile.json(obj=False) + click.echo(output) + + +# The neighbors command. +@cli.command(short_help="Print the neighbors of the tile.") +@click.argument("input", default="-", required=False) +@click.pass_context +def neighbors(_ctx: click.Context, input: str) -> None: + """Takes [x, y, z] tiles as input and writes adjacent + tiles on the same zoom level to stdout in the same form. + + There are no ordering guarantees for the output tiles. + + Input may be a compact newline-delimited sequences of JSON or + a pretty-printed ASCII RS-delimited sequence of JSON (like + https://tools.ietf.org/html/rfc8142 and + https://tools.ietf.org/html/rfc7159). + + Example: + + \b + echo "[486, 332, 10]" | utiles neighbors + [485, 331, 10] + [485, 332, 10] + [485, 333, 10] + [486, 331, 10] + [486, 333, 10] + [487, 331, 10] + [487, 332, 10] + [487, 333, 10] + + """ + src = normalize_input(input) + for line in iter_lines(src): + tile = json.loads(line)[:3] + tiles = utiles.neighbors(tile) + for t in tiles: + click.echo(t.json(obj=False)) + + +@cli.command(short_help="Convert to/from quadkeys.") +@click.argument("input", default="-", required=False) +@click.pass_context +def quadkey(_ctx: click.Context, input: str) -> None: + """Takes [x, y, z] tiles or quadkeys as input and writes + quadkeys or a [x, y, z] tiles to stdout, respectively. + + Input may be a compact newline-delimited sequences of JSON or + a pretty-printed ASCII RS-delimited sequence of JSON (like + https://tools.ietf.org/html/rfc8142 and + https://tools.ietf.org/html/rfc7159). + + Examples: + + \b + echo "[486, 332, 10]" | utiles quadkey + 0313102310 + + \b + echo "0313102310" | utiles quadkey + [486, 332, 10] + + """ + src = normalize_input(input) + try: + for line in iter_lines(src): + if line[0] == "[": + tile = json.loads(line)[:3] + output = utiles.quadkey(tile) + else: + tile = utiles.quadkey_to_tile(line) + output = tile.json(obj=False) + click.echo(output) + except ValueError as ve: + e = click.BadParameter( + f"{input}", param=click.Parameter("input", type=str), param_hint="input" + ) + raise e from ve + + +if __name__ == "__main__": + cli() diff --git a/python/utiles/libutiles.pyi b/python/utiles/_utiles.pyi similarity index 96% rename from python/utiles/libutiles.pyi rename to python/utiles/_utiles.pyi index 755676b1..e7d11f64 100644 --- a/python/utiles/libutiles.pyi +++ b/python/utiles/_utiles.pyi @@ -5,16 +5,16 @@ from typing import ( Collection, Iterable, Iterator, + Literal, Optional, Sequence, Set, Tuple, + TypedDict, Union, overload, ) -from typing_extensions import Literal, TypedDict - __version_lib__: str __build_profile__: Literal["debug", "release"] TupleIntInt = Tuple[int, int] @@ -22,7 +22,9 @@ TupleIntIntInt = Tuple[int, int, int] TILETYPE_GIF: int TILETYPE_JPG: int +TILETYPE_JSON: int TILETYPE_PBF: int +TILETYPE_PBFGZ: int TILETYPE_PNG: int TILETYPE_UNKNOWN: int TILETYPE_WEBP: int @@ -206,6 +208,14 @@ def tiles_list( zooms: list[int] | tuple[int, ...] | int, truncate: bool = ..., ) -> list[Tile]: ... +def tiles_count( + west: float, + south: float, + east: float, + north: float, + zooms: list[int] | tuple[int, ...] | int, + truncate: bool = ..., +) -> int: ... def tiletype(buf: bytes) -> int: ... def tiletype2headers(tiletype_int: int) -> list[tuple[str, str]]: ... def tiletype_str(buf: bytes) -> str: ... @@ -224,3 +234,6 @@ def _coords(obj: Any) -> Iterable[Tuple[float, float]]: ... def geojson_bounds(obj: Any) -> LngLatBbox: ... def pmtileid(*tile: TileLike) -> int: ... def from_pmtileid(pmtileid: int) -> Tile: ... + +# CLI +def ut_cli(args: list[str]) -> None: ... diff --git a/python/utiles/cli.py b/python/utiles/cli.py index ee7a19d1..88d936c0 100644 --- a/python/utiles/cli.py +++ b/python/utiles/cli.py @@ -1,580 +1,25 @@ """Utiles cli""" from __future__ import annotations -import json import logging import sys -from typing import Any, Dict, Iterable, List, Optional, Tuple, Union - -import click - -import utiles - - -def configure_logging(verbosity: int) -> None: - """Configure logging level - - Parameters - ---------- - verbosity : int - The number of `-v` options from the command line. - - Returns - ------- - None - """ - log_level = max(10, 30 - 10 * verbosity) - logging.basicConfig(stream=sys.stderr, level=log_level) +from utiles import ut_cli logger = logging.getLogger(__name__) -RS = "\x1e" - - -def normalize_input(input: str) -> List[str]: - """Normalize file or string input.""" - try: - src = click.open_file(input).readlines() - except OSError: - src = [input] - return src - - -def iter_lines(lines: List[str]) -> Iterable[str]: - """Iterate over lines of input, stripping and skipping.""" - for line in lines: - line_stripped = line.strip() - if line_stripped: - yield line_stripped - - -# The CLI command group. -@click.group(name="utiles", help="utiles cli (python)") -@click.option("--verbose", "-v", count=True, help="Increase verbosity.") -@click.option("--quiet", "-q", count=True, help="Decrease verbosity.") -@click.version_option(version=utiles.__version__, message="%(version)s") -@click.pass_context -def cli(ctx: click.Context, verbose: int, quiet: int) -> None: - """Execute the main utiles command""" - verbosity = verbose - quiet - configure_logging(verbosity) - ctx.obj = {} - ctx.obj["verbosity"] = verbosity - - -# Commands are below. - - -# The shapes command. -@cli.command(short_help="Print the shapes of tiles as GeoJSON.") -# This input is either a filename, stdin, or a string. -@click.argument("input", default="-", required=False) -# Coordinate precision option. -@click.option( - "--precision", type=int, default=None, help="Decimal precision of coordinates." -) -# JSON formatting options. -@click.option( - "--indent", default=None, type=int, help="Indentation level for JSON output" -) -@click.option( - "--compact/--no-compact", default=False, help="Use compact separators (',', ':')." -) -# Geographic (default) or Mercator switch. -@click.option( - "--geographic", - "projected", - flag_value="geographic", - default=True, - help="Output in geographic coordinates (the default).", -) -@click.option( - "--mercator", - "projected", - flag_value="mercator", - help="Output in Web Mercator coordinates.", -) -@click.option( - "--seq", - is_flag=True, - default=False, - help="Write a RS-delimited JSON sequence (default is LF).", -) -# GeoJSON feature (default) or collection switch. Meaningful only -# when --x-json-seq is used. -@click.option( - "--feature", - "output_mode", - flag_value="feature", - default=True, - help="Output as sequence of GeoJSON features (the default).", -) -@click.option( - "--bbox", - "output_mode", - flag_value="bbox", - help="Output as sequence of GeoJSON bbox arrays.", -) -@click.option( - "--collect", - is_flag=True, - default=False, - help="Output as a GeoJSON feature collections.", -) -# Optionally write out bboxen in a form that goes -# straight into GDAL utilities like gdalwarp. -@click.option( - "--extents/--no-extents", - default=False, - help="Write shape extents as ws-separated strings (default is False).", -) -# Optionally buffer the shapes by shifting the x and y values of each -# vertex by a constant number of decimal degrees or meters (depending -# on whether --geographic or --mercator is in effect). -@click.option( - "--buffer", - type=float, - default=None, - help="Shift shape x and y values by a constant number", -) -@click.pass_context -def shapes( - _ctx: click.Context, - input: str, - precision: Optional[int] = None, - indent: Optional[int] = None, - projected: str = "geographic", - output_mode: str = "feature", - buffer: Optional[float] = None, - compact: bool = False, - seq: bool = False, - collect: bool = False, - extents: bool = False, -) -> None: - """Print tiles as GeoJSON feature collections or sequences. - - Input may be a compact newline-delimited sequences of JSON or - a pretty-printed ASCII RS-delimited sequence of JSON (like - https://tools.ietf.org/html/rfc8142 and - https://tools.ietf.org/html/rfc7159). - - Tile descriptions may be either an [x, y, z] array or a JSON - object of the form - - {"tile": [x, y, z], "properties": {"name": "foo", ...}} - - In the latter case, the properties object will be used to update - the properties object of the output feature. - - Example: - - \b - echo "[486, 332, 10]" | utiles shapes --precision 4 --bbox - [-9.1406, 53.1204, -8.7891, 53.3309] - - """ - dump_kwds: Dict[str, Union[bool, int, Tuple[str, ...]]] = {"sort_keys": True} - if indent: - dump_kwds["indent"] = indent - if compact: - dump_kwds["separators"] = (",", ":") - - src = normalize_input(input) - features = [] - col_xs = [] - col_ys = [] - - for _i, line in enumerate(iter_lines(src)): - obj = json.loads(line) - if isinstance(obj, dict): - x, y, z = obj["tile"][:3] - props = obj.get("properties") - fid = obj.get("id") - elif isinstance(obj, list): - x, y, z = obj[:3] - props = {} - fid = None - else: - msg = f"{obj}" - raise click.BadParameter(msg, param=input, param_hint="input") # type: ignore - - feature = utiles.feature( - (x, y, z), - fid=fid, - props=props, - projected=projected, - buffer=buffer, - precision=precision, - ) - bbox = feature["bbox"] - w, s, e, n = bbox - col_xs.extend([w, e]) - col_ys.extend([s, n]) - - if collect: - features.append(feature) - elif extents: - click.echo(" ".join(map(str, bbox))) - else: - if seq: - click.echo(RS) - if output_mode == "bbox": - click.echo(json.dumps(bbox, **dump_kwds)) # type: ignore - elif output_mode == "feature": - click.echo(json.dumps(feature, **dump_kwds)) # type: ignore - - if collect and features: - bbox = [min(col_xs), min(col_ys), max(col_xs), max(col_ys)] - click.echo( - json.dumps( - {"type": "FeatureCollection", "bbox": bbox, "features": features}, - **dump_kwds, # type: ignore - ) - ) - - -# The tiles command. -@cli.command( - short_help="Print tiles that overlap or contain a lng/lat point, " - "bounding box, or GeoJSON objects." -) -# Mandatory Mercator zoom level argument. -@click.argument("zoom", type=int, default=-1) -# This input is either a filename, stdin, or a string. -# Has to follow the zoom arg. -@click.argument("input", default="-", required=False) -@click.option( - "--seq/--lf", - default=False, - help="Write a RS-delimited JSON sequence (default is LF).", -) -@click.pass_context -def tiles( - _ctx: click.Context, - zoom: int = -1, - input: str = "-", - seq: bool = False, -) -> None: - """Lists Web Mercator tiles at ZOOM level intersecting - GeoJSON [west, south, east, north] bounding boxen, features, or - collections read from stdin. Output is a JSON - [x, y, z] array. - - Input may be a compact newline-delimited sequences of JSON or - a pretty-printed ASCII RS-delimited sequence of JSON (like - https://tools.ietf.org/html/rfc8142 and - https://tools.ietf.org/html/rfc7159). - - Example: - - \b - $ echo "[-105.05, 39.95, -105, 40]" | utiles tiles 12 - [852, 1550, 12] - [852, 1551, 12] - [853, 1550, 12] - [853, 1551, 12] - - """ - src = iter(normalize_input(input)) - first_line = next(src) - - # If input is RS-delimited JSON sequence. - if first_line.startswith(RS): - - def feature_gen() -> Iterable[Dict[str, Any]]: - buffer = first_line.strip(RS) - for line in src: - if line.startswith(RS): - if buffer: - yield json.loads(buffer) - buffer = line.strip(RS) - else: - buffer += line - else: - yield json.loads(buffer) - - else: - - def feature_gen() -> Iterable[Dict[str, Any]]: - yield json.loads(first_line) - for line in src: - yield json.loads(line) - - for obj in feature_gen(): - if isinstance(obj, list): - bbox = obj - if len(bbox) == 2: - bbox += bbox - elif len(bbox) != 4: - msg = f"{bbox}" - raise click.BadParameter(msg, param=input, param_hint="input") - elif isinstance(obj, dict): - if "bbox" in obj: - bbox = obj["bbox"] - else: - bbox = utiles.geojson_bounds(obj) - - west, south, east, north = bbox - epsilon = 1.0e-10 - - if east != west and north != south: - # 2D bbox - # shrink the bounds a small amount so that - # shapes/tiles round trip. - west += epsilon - south += epsilon - east -= epsilon - north -= epsilon - - for tile in utiles.tiles(west, south, east, north, [zoom], truncate=False): - vals = (tile.x, tile.y, zoom) - output = json.dumps(vals) - if seq: - click.echo(RS) - click.echo(output) - - -# The bounding-tile command. -@cli.command( - "bounding-tile", - short_help="Print the bounding tile of a lng/lat point, " - "bounding box, or GeoJSON objects.", -) -# This input is either a filename, stdin, or a string. -@click.argument("input", default="-", required=False) -@click.option( - "--seq/--lf", - default=False, - help="Write a RS-delimited JSON sequence (default is LF).", -) -@click.pass_context -def bounding_tile(_ctx: click.Context, input: str, seq: bool = False) -> None: - """Print the Web Mercator tile at ZOOM level bounding - GeoJSON [west, south, east, north] bounding boxes, features, or - collections read from stdin. - - Input may be a compact newline-delimited sequences of JSON or - a pretty-printed ASCII RS-delimited sequence of JSON (like - https://tools.ietf.org/html/rfc8142 and - https://tools.ietf.org/html/rfc7159). - - Example: - - \b - echo "[-105.05, 39.95, -105, 40]" | utiles bounding-tile - [426, 775, 11] - - """ - src = iter(normalize_input(input)) - first_line = next(src) - - # If input is RS-delimited JSON sequence. - if first_line.startswith(RS): - - def feature_gen() -> Iterable[Dict[str, Any]]: - buffer = first_line.strip(RS) - for line in src: - if line.startswith(RS): - if buffer: - yield json.loads(buffer) - buffer = line.strip(RS) - else: - buffer += line - else: - yield json.loads(buffer) - - else: - - def feature_gen() -> Iterable[Dict[str, Any]]: - yield json.loads(first_line) - for line in src: - yield json.loads(line) - - for obj in feature_gen(): - if isinstance(obj, list): - bbox = obj - if len(bbox) == 2: - bbox += bbox - elif len(bbox) != 4: - msg = f"{bbox}" - raise click.BadParameter(msg, param=input, param_hint="input") - - elif isinstance(obj, dict): - if "bbox" in obj: - bbox = obj["bbox"] - else: - bbox = utiles.geojson_bounds(obj) - - west, south, east, north = bbox - vals = utiles.bounding_tile(west, south, east, north, truncate=False) - - # print(vals) - # output = json.dumps(vals) - output = vals.json(obj=False) - - if seq: - click.echo(RS) - - click.echo(output) - - -# The children command. -@cli.command(short_help="Print the children of the tile.") -@click.argument("input", default="-", required=False) -@click.option( - "--depth", - type=int, - default=1, - help="Number of zoom levels to traverse (default is 1).", -) -@click.pass_context -def children(_ctx: click.Context, input: str, depth: int = 1) -> None: - """Takes [x, y, z] tiles as input and writes children to stdout - in the same form. - - Input may be a compact newline-delimited sequences of JSON or - a pretty-printed ASCII RS-delimited sequence of JSON (like - https://tools.ietf.org/html/rfc8142 and - https://tools.ietf.org/html/rfc7159). - - Example: - - \b - echo "[486, 332, 10]" | utiles children - [972, 664, 11] - [973, 664, 11] - [973, 665, 11] - [972, 665, 11] - - """ - src = normalize_input(input) - for line in iter_lines(src): - tiles = [json.loads(line)[:3]] - for _i in range(depth): - tiles = sum([utiles.children(t) for t in tiles], []) - for t in tiles: - click.echo(t.json(obj=False)) - - -# The parent command. -@cli.command(short_help="Print the parent tile.") -@click.argument("input", default="-", required=False) -@click.option( - "--depth", - type=int, - default=1, - help="Number of zoom levels to traverse (default is 1).", -) -@click.pass_context -def parent(_ctx: click.Context, input: str, depth: int = 1) -> None: - """Takes [x, y, z] tiles as input and writes parents to stdout - in the same form. - - Input may be a compact newline-delimited sequences of JSON or - a pretty-printed ASCII RS-delimited sequence of JSON (like - https://tools.ietf.org/html/rfc8142 and - https://tools.ietf.org/html/rfc7159). - - Example: - - \b - echo "[486, 332, 10]" | utiles parent - [243, 166, 9] - - """ - src = normalize_input(input) - for line in iter_lines(src): - parsed = json.loads(line)[:3] - tile = utiles.parse_tile_arg(parsed) - if tile[2] - depth < 0: - msg = f"Invalid parent level: {tile[2] - depth}" - raise click.UsageError(msg) - for _i in range(depth): - ptile = utiles.parent(tile) - if ptile is None: - msg = f"Invalid parent level: {tile[2] - depth}" - raise click.UsageError(msg) - tile = ptile - output = tile.json(obj=False) - click.echo(output) - - -# The neighbors command. -@cli.command(short_help="Print the neighbors of the tile.") -@click.argument("input", default="-", required=False) -@click.pass_context -def neighbors(_ctx: click.Context, input: str) -> None: - """Takes [x, y, z] tiles as input and writes adjacent - tiles on the same zoom level to stdout in the same form. - - There are no ordering guarantees for the output tiles. - - Input may be a compact newline-delimited sequences of JSON or - a pretty-printed ASCII RS-delimited sequence of JSON (like - https://tools.ietf.org/html/rfc8142 and - https://tools.ietf.org/html/rfc7159). - - Example: - - \b - echo "[486, 332, 10]" | utiles neighbors - [485, 331, 10] - [485, 332, 10] - [485, 333, 10] - [486, 331, 10] - [486, 333, 10] - [487, 331, 10] - [487, 332, 10] - [487, 333, 10] - - """ - src = normalize_input(input) - for line in iter_lines(src): - tile = json.loads(line)[:3] - tiles = utiles.neighbors(tile) - for t in tiles: - click.echo(t.json(obj=False)) - - -@cli.command(short_help="Convert to/from quadkeys.") -@click.argument("input", default="-", required=False) -@click.pass_context -def quadkey(_ctx: click.Context, input: str) -> None: - """Takes [x, y, z] tiles or quadkeys as input and writes - quadkeys or a [x, y, z] tiles to stdout, respectively. - - Input may be a compact newline-delimited sequences of JSON or - a pretty-printed ASCII RS-delimited sequence of JSON (like - https://tools.ietf.org/html/rfc8142 and - https://tools.ietf.org/html/rfc7159). - - Examples: - - \b - echo "[486, 332, 10]" | utiles quadkey - 0313102310 - \b - echo "0313102310" | utiles quadkey - [486, 332, 10] +def cli() -> None: + args = sys.argv[1:] - """ - src = normalize_input(input) + # if first arg is "utiles" then remove it + if args and (args[0] == "utiles" or args[0] == "ut"): + args = args[1:] try: - for line in iter_lines(src): - if line[0] == "[": - tile = json.loads(line)[:3] - output = utiles.quadkey(tile) - else: - tile = utiles.quadkey_to_tile(line) - output = tile.json(obj=False) - click.echo(output) - except ValueError as ve: - e = click.BadParameter( - f"{input}", param=click.Parameter("input", type=str), param_hint="input" - ) - raise e from ve + ut_cli(["ut", *args]) + except Exception as e: + logger.error(e) + raise e from e if __name__ == "__main__": diff --git a/python/utiles/rio_plugin.py b/python/utiles/rio_plugin.py index ee44d13c..85c9faa2 100644 --- a/python/utiles/rio_plugin.py +++ b/python/utiles/rio_plugin.py @@ -1,8 +1,4 @@ -import click -from utiles.cli import cli as rio_utiles +from utiles._click import ut_click as rio_ut +from utiles._click import utiles_click as rio_utiles __all__ = ("rio_ut", "rio_utiles") - -rio_ut = click.CommandCollection( - sources=[rio_utiles], name="ut", help="utiles cli (alias)" -) diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 00000000..94747be1 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,16 @@ +use pyo3::{pyfunction, Python}; +use utiles_cli::cli::cli_main; + +#[pyfunction] +pub fn ut_cli(py: Python, args: Option>) { + let argv = match args { + Some(args) => args, + None => std::env::args().collect(), + }; + cli_main( + Some(argv), + Some(&|| { + py.check_signals().unwrap(); + }), + ) +} diff --git a/src/lib.rs b/src/lib.rs index 3838303c..23680aef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,4 @@ -use std::collections::{HashMap, HashSet}; - -use utiles::bbox::BBox; - use pyo3::exceptions::{self, PyValueError}; - use pyo3::prelude::*; use pyo3::types::{PyDict, PyTuple}; use pyutiles::pybbox::PyBbox; @@ -11,13 +6,13 @@ use pyutiles::pyiters::CoordinateIterator; use pyutiles::pylnglat::PyLngLat; use pyutiles::pylnglatbbox::PyLngLatBbox; use pyutiles::pytile::PyTile; - -use utiles::zoom::ZoomOrZooms; - +use std::collections::{HashMap, HashSet}; +use utiles::bbox::BBox; use utiles::libtiletype; +use utiles::zoom::ZoomOrZooms; +mod cli; mod pyutiles; -// mod utiles; #[derive(FromPyObject)] pub struct TileTuple(u32, u32, u8); @@ -758,11 +753,18 @@ fn feature( Ok(f) } +fn lib_constants(_py: Python<'_>, m: &PyModule) -> PyResult<()> { + m.add("__version_lib__", env!("CARGO_PKG_VERSION"))?; + m.add("__build_profile__", env!("PROFILE"))?; + Ok(()) +} + /// Utiles python module #[pymodule] +#[pyo3(name = "_utiles")] fn libutiles(_py: Python<'_>, m: &PyModule) -> PyResult<()> { - m.add("__version_lib__", env!("CARGO_PKG_VERSION"))?; - m.add("__build_profile__", env!("PROFILE"))?; + // lib constants + lib_constants(_py, m)?; // mercantile functions m.add_function(wrap_pyfunction!(parse_tile_arg, m)?)?; @@ -805,17 +807,27 @@ fn libutiles(_py: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_function(wrap_pyfunction!(tiletype, m)?)?; m.add_function(wrap_pyfunction!(tiletype_str, m)?)?; m.add_function(wrap_pyfunction!(tiletype2headers, m)?)?; - m.add("TILETYPE_UNKNOWN", libtiletype::TILETYPE_UNKNOWN)?; // 0 - m.add("TILETYPE_GIF", libtiletype::TILETYPE_GIF)?; // 1 - m.add("TILETYPE_JPG", libtiletype::TILETYPE_JPG)?; // 2 - m.add("TILETYPE_PBF", libtiletype::TILETYPE_PBF)?; // 3 - m.add("TILETYPE_PNG", libtiletype::TILETYPE_PNG)?; // 4 - m.add("TILETYPE_WEBP", libtiletype::TILETYPE_WEBP)?; // 5 + m.add("TILETYPE_UNKNOWN", libtiletype::TILETYPE_UNKNOWN)?; + m.add("TILETYPE_GIF", libtiletype::TILETYPE_GIF)?; + m.add("TILETYPE_JPG", libtiletype::TILETYPE_JPG)?; + m.add("TILETYPE_JSON", libtiletype::TILETYPE_JSON)?; + m.add("TILETYPE_PBF", libtiletype::TILETYPE_PBF)?; + m.add("TILETYPE_PBFGZ", libtiletype::TILETYPE_PBFGZ)?; + m.add("TILETYPE_PNG", libtiletype::TILETYPE_PNG)?; + m.add("TILETYPE_WEBP", libtiletype::TILETYPE_WEBP)?; // m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; + + // mbtiles... + // m.add_class::()?; + // m.add_function(wrap_pyfunction!(query_db, m)?)?; + + // rust-cli + m.add_function(wrap_pyfunction!(cli::ut_cli, m)?)?; + Ok(()) } diff --git a/src/purgatory/_coords.rs b/src/purgatory/_coords.rs deleted file mode 100644 index 53808eeb..00000000 --- a/src/purgatory/_coords.rs +++ /dev/null @@ -1,167 +0,0 @@ -/// def pycoords(obj): -/// if isinstance(obj, (tuple, list)): -/// coordinates = obj -/// elif "features" in obj: -/// coordinates = [feat["geometry"]["coordinates"] for feat in obj["features"]] -/// elif "geometry" in obj: -/// coordinates = obj["geometry"]["coordinates"] -/// else: -/// coordinates = obj.get("coordinates", obj) -/// for e in coordinates: -/// if isinstance(e, (float, int)): -/// return tuple(coordinates) -/// else: -/// for f in pycoords(e): -// #[pyfunction] -// fn _coords_slow(py: Python, obj: &PyAny) -> PyResult> { -// println!("obj: {:?}", obj); -// // let is_tuple = obj.is_instance(PyTuple)?; -// let tuple_type = py.get_type::(); -// let list_type = py.get_type::(); -// let float_type = py.get_type::(); -// let int_type = py.get_type::(); -// let dict_type = py.get_type::(); -// -// let is_tuple = obj.is_instance(tuple_type)?; -// let is_list = obj.is_instance(list_type)?; -// let is_dict = obj.is_instance(dict_type)?; -// -// let mut result: Vec<(f64, f64)> = Vec::new(); -// let coordinates: &PyAny; -// if is_tuple { -// println!("is tuple"); -// // let tuple_obj = obj.into_object(py); -// // let tuple_obj = tuple_obj.downcast::()?; -// // coordinates = obj.into(); -// // coordinates = obj; -// // println!("tuple_obj: {:?}", coordinates); -// let tuple_len = obj.len(); -// if let Ok(tlen) = tuple_len { -// if tlen == 2 { -// // try to extract as coords -// return Ok(vec![( -// obj.get_item(0)?.extract::()?, -// obj.get_item(1)?.extract::()?, -// )]); -// } else if tlen == 3 { -// // try to extract as coords -// // if first value is a number assume the thing is a coord -// if obj.get_item(0)?.is_instance(float_type)? -// || obj.get_item(0)?.is_instance(int_type)? -// { -// return Ok(vec![( -// obj.get_item(0)?.extract::()?, -// obj.get_item(1)?.extract::()?, -// )]); -// } else { -// // call _coords on each item -// let mut coordsvec: Vec<(f64, f64)> = Vec::new(); -// for item in obj.iter() { -// let c = _coords_slow(py, item)?; -// coordsvec.extend(c); -// } -// return Ok(coordsvec); -// } -// // return Ok(vec![(obj.get_item(0)?.extract::()?, obj.get_item(1)?.extract::()?)]); -// } -// } -// -// // let c = obj.extract -// } else if is_list { -// println!("is list"); -// // let tuple_obj = obj.into_object(py); -// // let tuple_obj = tuple_obj.downcast::()?; -// // coordinates = obj.into(); -// coordinates = obj; -// let c = obj.downcast::()?; -// for item in c.iter() { -// if item.is_instance(float_type)? || item.is_instance(int_type)? { -// println!("item: {:?}", item); -// let c = _coords_slow(py, item)?; -// println!("c: {:?}", c); -// let value = item.extract::()?; -// result.push((value, value)); -// } else { -// println!("item: {:?}", item); -// let c = _coords_slow(py, item)?; -// println!("c: {:?}", c); -// result.extend(c); -// } -// } -// println!("tuple_obj: {:?}", coordinates); -// } else if is_dict { -// println!("is dict"); -// match obj.contains("features") { -// Ok(true) => { -// let features = obj.get_item("features"); -// -// match features { -// Ok(val) => { -// println!("val: {:?}", val); -// let c = _coords_slow(py, val)?; -// println!("c: {:?}", c); -// result.extend(c); -// } -// Err(e) => { -// println!("e: {:?}", e); -// } -// } -// } -// _ => { -// println!("is not features"); -// } -// } -// -// match obj.contains("geometry") { -// Ok(true) => { -// let features = obj.get_item("geometry"); -// -// match features { -// Ok(val) => { -// println!("val: {:?}", val); -// let c = _coords_slow(py, val)?; -// println!("c: {:?}", c); -// result.extend(c); -// } -// Err(e) => { -// println!("e: {:?}", e); -// } -// } -// } -// _ => { -// println!("is not geometry"); -// } -// } -// -// match obj.contains("coordinates") { -// Ok(true) => { -// let features = obj.get_item("geometry"); -// -// match features { -// Ok(val) => { -// println!("val: {:?}", val); -// let c = _coords_slow(py, val)?; -// println!("c: {:?}", c); -// result.extend(c); -// } -// Err(e) => { -// println!("e: {:?}", e); -// } -// } -// } -// _ => { -// println!("is not geometry"); -// } -// } -// // println!("features: {:?}", features); -// } else { -// println!("is something else"); -// // dummy obj -// // coordinates = obj.getattr("coordinates")?.into(); -// } -// -// // Err -// // raise dummy error -// // Err(PyErr::new::( "the tile argument may have 1 or 4 values. Note that zoom is a keyword-only argument"))?; -// Ok(result) -// } diff --git a/src/pyutiles/pytile.rs b/src/pyutiles/pytile.rs index fb8588ff..444bc1eb 100644 --- a/src/pyutiles/pytile.rs +++ b/src/pyutiles/pytile.rs @@ -13,7 +13,7 @@ use pyo3::types::PyType; use pyo3::exceptions::PyValueError; use pyo3::{ - exceptions, IntoPy, Py, PyAny, pyclass, PyErr, pymethods, PyObject, PyRef, + exceptions, pyclass, pymethods, IntoPy, Py, PyAny, PyErr, PyObject, PyRef, PyResult, Python, }; use serde::{Deserialize, Serialize}; @@ -477,8 +477,6 @@ impl From for Tile { #[cfg(test)] mod tests { use super::*; - use crate::utile; - #[test] fn test_pytile_macro() { let tile = pytile!(0, 0, 0); diff --git a/test-data/tile-types/tile-arr.json b/test-data/tile-types/tile-arr.json new file mode 100644 index 00000000..7d426359 --- /dev/null +++ b/test-data/tile-types/tile-arr.json @@ -0,0 +1,3 @@ +[ + "I am a tile-json-array" +] \ No newline at end of file diff --git a/test-data/tile-types/tile-obj.json b/test-data/tile-types/tile-obj.json new file mode 100644 index 00000000..6e03b53a --- /dev/null +++ b/test-data/tile-types/tile-obj.json @@ -0,0 +1,3 @@ +{ + "data": "I am a tile-object" +} diff --git a/tests/cli/test_mt.py b/tests/cli/test_mt.py new file mode 100644 index 00000000..1cd793c6 --- /dev/null +++ b/tests/cli/test_mt.py @@ -0,0 +1,435 @@ +"""Utiles rust cli tests""" +import json +import sys +from json import dumps as stringify +from subprocess import CompletedProcess, run + +import pytest + + +def _run_cli( + args: list[str] | None, + input: str | None = None, +) -> CompletedProcess[str]: + _python = sys.executable + _args = args or [] + res = run( + [_python, "-m", "utiles.cli", *_args], + input=input, + capture_output=True, + text=True, + shell=False, # noqa: S603 + ) + return res + + +def test_rust_cli_help() -> None: + res = _run_cli(["--help"]) + assert "(rust)" in res.stdout + + +class TestTiles: + def test_cli_tiles_bad_bounds(self) -> None: + """Bounds of len 3 are bad.""" + result = _run_cli(["tiles", "14"], "[-105, 39.99, -104.99]") + assert result.returncode != 0 + + def test_cli_tiles_no_bounds(self) -> None: + result = _run_cli(["tiles", "14"], "[-105, 39.99, -104.99, 40]") + assert result.returncode == 0 + assert result.stdout == "[3413, 6202, 14]\n[3413, 6203, 14]\n" + + # + def test_cli_tiles_multi_bounds(self) -> None: + """A LF-delimited sequence can be used as input.""" + result = _run_cli( + ["tiles", "14"], "[-105, 39.99, -104.99, 40]\n[-105, 39.99, -104.99, 40]" + ) + assert result.returncode == 0 + assert len(result.stdout.strip().split("\n")) == 4 + + def test_cli_tiles_multi_bounds_seq(self) -> None: + """A JSON text sequence can be used as input.""" + result = _run_cli( + ["--debug", "tiles", "14"], + "\x1e\n[-105, 39.99, -104.99, 40]\n\x1e\n[-105, 39.99, -104.99, 40]", + ) + assert result.returncode == 0 + assert len(result.stdout.strip().split("\n")) == 4 + + def test_rust_cli_tiles_seq(self) -> None: + result = _run_cli(["tiles", "14", "--seq", "[14.0859, 5.798]"]) + # runner = CliRunner() + # result = runner.invoke(cli, ["tiles", "14", "--seq", + # "[14.0859, 5.798]" + # ],) + # print(result) + assert result.returncode == 0 + assert result.stdout == "\x1e\n[8833, 7927, 14]\n" + + def test_cli_tiles_points(self) -> None: + result = _run_cli(["tiles", "14"], "[14.0859, 5.798]") + # j + # runner = CliRunner() + # result = runner.invoke(cli, ["tiles", "14"], "[14.0859, 5.798]") + assert result.returncode == 0 + assert result.stdout == "[8833, 7927, 14]\n" + + def test_cli_tiles_point_geojson(self) -> None: + result = _run_cli( + ["tiles", "14"], '{"type":"Point","coordinates":[14.0859, 5.798]}' + ) + assert result.returncode == 0 + assert result.stdout == "[8833, 7927, 14]\n" + + def test_cli_tiles_implicit_stdin(self) -> None: + # result = _run_cli(["tiles", "14"], "[14.0859, 5.798]") + # assert result.returncode == 0 + # assert result.stdout == "[8833, 7927, 14]\n" + # runner = CliRunner() + result = _run_cli(["tiles", "14"], "[-105, 39.99, -104.99, 40]") + assert result.returncode == 0 + assert result.stdout == "[3413, 6202, 14]\n[3413, 6203, 14]\n" + + def test_cli_tiles_arg(self) -> None: + result = _run_cli(["tiles", "14", "[-105, 39.99, -104.99, 40]"]) + assert result.returncode == 0 + assert result.stdout == "[3413, 6202, 14]\n[3413, 6203, 14]\n" + + def test_cli_tiles_geosjon(self) -> None: + collection = stringify( + { + "features": [ + { + "geometry": { + "coordinates": [ + [ + [-105.46875, 39.909736], + [-105.46875, 40.446947], + [-104.765625, 40.446947], + [-104.765625, 39.909736], + [-105.46875, 39.909736], + ] + ], + "type": "Polygon", + }, + "id": "(106, 193, 9)", + "properties": {"title": "XYZ tile (106, 193, 9)"}, + "type": "Feature", + } + ], + "type": "FeatureCollection", + } + ) + result = _run_cli(["tiles", "9"], collection) + assert result.returncode == 0 + assert result.stdout == "[106, 193, 9]\n[106, 194, 9]\n" + + +class TestQuadkey: + def test_cli_quadkey_from_tiles(self) -> None: + result = _run_cli(["quadkey"], "[486, 332, 10]\n[6826, 12415, 15]") + assert result.returncode == 0 + assert result.stdout == "0313102310\n023101012323232\n" + + def test_cli_quadkey_from_quadkeys(self) -> None: + result = _run_cli(["quadkey"], "0313102310\n023101012323232") + assert result.returncode == 0 + assert result.stdout == "[486, 332, 10]\n[6826, 12415, 15]\n" + + def test_cli_quadkey_from_mixed(self) -> None: + result = _run_cli(["quadkey"], "0313102310\n[6826, 12415, 15]") + assert result.returncode == 0 + assert result.stdout == "[486, 332, 10]\n023101012323232\n" + + @pytest.mark.skip(reason="not implemented") + def test_cli_quadkey_failure(self) -> None: + """Abort when an invalid quadkey is passed""" + result = _run_cli(["quadkey"], "lolwut") + assert result.returncode != 0 + assert "lolwut" in result.stdout + + +class TestBoundingTile: + def test_cli_bounding_tile_bad_bounds(self) -> None: + """Bounds of len 3 are bad.""" + result = _run_cli(["bounding-tile"], "[-105, 39.99, -104.99]") + assert result.returncode != 0 + + def test_cli_bounding_tile(self) -> None: + result = _run_cli(["bounding-tile"], "[-105, 39.99, -104.99, 40]") + assert result.returncode == 0 + assert result.stdout == "[1706, 3101, 13]\n" + + def test_cli_bounding_tile_bbox(self) -> None: + result = _run_cli(["bounding-tile"], '{"bbox": [-105, 39.99, -104.99, 40]}') + assert result.returncode == 0 + assert result.stdout == "[1706, 3101, 13]\n" + + def test_cli_bounding_tile2(self) -> None: + result = _run_cli(["bounding-tile"], "[-105, 39.99]") + assert result.returncode == 0 + + def test_cli_multi_bounding_tile(self) -> None: + """A JSON text sequence can be used as input.""" + result = _run_cli( + ["bounding-tile"], "[-105, 39.99, -104.99, 40]\n[-105, 39.99, -104.99, 40]" + ) + assert result.returncode == 0 + assert len(result.stdout.strip().split("\n")) == 2 + + def test_cli_multi_bounding_tile_seq(self) -> None: + """A JSON text sequence can be used as input.""" + result = _run_cli( + ["bounding-tile"], + "\x1e\n[-105, 39.99, -104.99, 40]\n\x1e\n[-105, 39.99, -104.99, 40]", + ) + assert result.returncode == 0 + assert len(result.stdout.strip().split("\n")) == 2 + + @pytest.mark.skip(reason="I dont think this is correct") + def test_cli_tiles_bounding_tiles_z0(self) -> None: + result = _run_cli(["bounding-tile"], "[-1, -1, 1, 1]") + assert result.returncode == 0 + assert result.stdout == "[0, 0, 0]\n" + + @pytest.mark.skip(reason="I dont think this is correct either") + def test_cli_tiles_bounding_tiles_seq(self) -> None: + result = _run_cli(["bounding-tile", "--seq"], "[-1, -1, 1, 1]") + assert result.returncode == 0 + assert result.stdout == "\x1e\n[0, 0, 0]\n" + + def test_cli_bounding_tile_geosjon(self) -> None: + collection_dict = { + "features": [ + { + "geometry": { + "coordinates": [ + [ + [-105.46875, 39.909736], + [-105.46875, 40.446947], + [-104.765625, 40.446947], + [-104.765625, 39.909736], + [-105.46875, 39.909736], + ] + ], + "type": "Polygon", + }, + "id": "(106, 193, 9)", + "properties": {"title": "XYZ tile (106, 193, 9)"}, + "type": "Feature", + } + ], + "type": "FeatureCollection", + } + collection = json.dumps(collection_dict) + result = _run_cli(["bounding-tile"], collection) + assert result.returncode == 0 + assert result.stdout == "[26, 48, 7]\n" + + +class TestNeighbors: + def test_cli_neighbors(self) -> None: + result = _run_cli(["neighbors"], "[243, 166, 9]") + assert result.returncode == 0 + + tiles_lines = result.stdout.strip().split("\n") + tiles = [tuple(json.loads(t)) for t in tiles_lines] + assert len(tiles) == 8 + + # We do not provide ordering guarantees + # tiles = set([tuple(t) for t in tiles]) + tiles_set = set(tiles) + assert (243, 166, 9) not in tiles_set, "input not in neighbors" + + assert (243 - 1, 166 - 1, 9) in tiles_set + assert (243 - 1, 166 + 0, 9) in tiles_set + assert (243 - 1, 166 + 1, 9) in tiles_set + assert (243 + 0, 166 - 1, 9) in tiles_set + assert (243 + 0, 166 + 1, 9) in tiles_set + assert (243 + 1, 166 - 1, 9) in tiles_set + assert (243 + 1, 166 + 0, 9) in tiles_set + assert (243 + 1, 166 + 1, 9) in tiles_set + + +class TestParent: + def test_cli_parent_failure(self) -> None: + """[0, 0, 0] has no parent""" + result = _run_cli(["parent"], "[0, 0, 0]") + assert result.returncode != 0 + + def test_cli_parent(self) -> None: + result = _run_cli(["parent"], "[486, 332, 10]\n[486, 332, 10]") + assert result.returncode == 0 + assert result.stdout == "[243, 166, 9]\n[243, 166, 9]\n" + + def test_cli_parent_depth(self) -> None: + result = _run_cli(["parent", "--depth", "2"], "[486, 332, 10]") + assert result.returncode == 0 + assert result.stdout == "[121, 83, 8]\n" + + def test_cli_parent_multidepth(self) -> None: + result = _run_cli(["parent", "--depth", "2"], "[486, 332, 10]\n[121, 83, 8]") + assert result.returncode == 0 + assert result.stdout == "[121, 83, 8]\n[30, 20, 6]\n" + + +class TestChildren: + def test_cli_children(self) -> None: + result = _run_cli(["children"], "[243, 166, 9]") + assert result.returncode == 0 + assert ( + result.stdout + == "[486, 332, 10]\n[487, 332, 10]\n[487, 333, 10]\n[486, 333, 10]\n" + ) + + +# =================== +# SHAPES TESTS (TODO) +# =================== + + +class TestShapes: + def test_cli_shapes_failure(self) -> None: + result = _run_cli(["shapes"], "0") + assert result.returncode != 0 + + def test_cli_shapes(self) -> None: + result = _run_cli(["shapes", "--precision", "6"], "[106, 193, 9]") + assert result.returncode == 0 + expected = { + "bbox": [-105.46875, 39.909736, -104.765625, 40.446947], + "geometry": { + "coordinates": [ + [ + [-105.46875, 39.909736], + [-105.46875, 40.446947], + [-104.765625, 40.446947], + [-104.765625, 39.909736], + [-105.46875, 39.909736], + ] + ], + "type": "Polygon", + }, + "id": "(106, 193, 9)", + "properties": {"title": "XYZ tile (106, 193, 9)"}, + "type": "Feature", + } + assert json.loads(result.stdout) == expected + + def test_cli_shapes_arg(self) -> None: + # runner = CliRunner() + # result = runner.invoke(cli, ["shapes", "[106, 193, 9]", "--precision", "6"]) + result = _run_cli(["shapes", "[106, 193, 9]", "--precision", "6"]) + assert result.returncode == 0 + result_output_json = json.loads(result.stdout) + + expected_dict = { + "bbox": [-105.46875, 39.909736, -104.765625, 40.446947], + "geometry": { + "coordinates": [ + [ + [-105.46875, 39.909736], + [-105.46875, 40.446947], + [-104.765625, 40.446947], + [-104.765625, 39.909736], + [-105.46875, 39.909736], + ] + ], + "type": "Polygon", + }, + "id": "(106, 193, 9)", + "properties": {"title": "XYZ tile (106, 193, 9)"}, + "type": "Feature", + } + + assert result_output_json == expected_dict + + def test_cli_shapes_buffer(self) -> None: + result = _run_cli( + ["shapes", "[106, 193, 9]", "--buffer", "1.0", "--precision", "6"] + ) + assert result.returncode == 0 + expected = { + "bbox": [-106.46875, 38.909736, -103.765625, 41.446947], + "geometry": { + "coordinates": [ + [ + [-106.46875, 38.909736], + [-106.46875, 41.446947], + [-103.765625, 41.446947], + [-103.765625, 38.909736], + [-106.46875, 38.909736], + ] + ], + "type": "Polygon", + }, + "id": "(106, 193, 9)", + "properties": {"title": "XYZ tile (106, 193, 9)"}, + "type": "Feature", + } + assert json.loads(result.stdout) == expected + + @pytest.mark.skip(reason="not implemented") + def test_cli_shapes_compact(self) -> None: + """Output is compact.""" + result = _run_cli(["shapes", "--compact"], "[106, 193, 9]") + assert result.returncode == 0 + assert '"type":"Feature"' in result.stdout.strip() + + @pytest.mark.skip(reason="not implemented b/c why would I/anyone ever need that...") + def test_cli_shapes_indentation(self) -> None: + """Output is indented.""" + result = _run_cli(["shapes", "--indent", "8"], "[106, 193, 9]") + assert result.returncode == 0 + assert ' "type": "Feature"' in result.stdout.strip() + + def test_cli_shapes_collect(self) -> None: + """Shapes are collected into a feature collection.""" + result = _run_cli(["shapes", "--collect", "--feature"], "[106, 193, 9]") + assert result.returncode == 0 + assert "FeatureCollection" in result.stdout + + def test_cli_shapes_extents(self) -> None: + result = _run_cli( + ["shapes", "[106, 193, 9]", "--extents", "--mercator", "--precision", "3"] + ) + assert result.returncode == 0 + assert result.stdout == "-11740727.545 4852834.052 -11662456.028 4931105.569\n" + + def test_cli_shapes_bbox(self) -> None: + """JSON text sequences of bboxes are output.""" + result = _run_cli( + [ + "shapes", + "[106, 193, 9]", + "--seq", + "--bbox", + "--mercator", + "--precision", + "3", + ], + ) + assert result.returncode == 0 + assert ( + result.stdout + == "\x1e\n[-11740727.545,4852834.052,-11662456.028,4931105.569]\n" + ) + + def test_cli_shapes_props_fid(self) -> None: + result = _run_cli( + [ + "shapes", + '{"tile": [106, 193, 9], "properties": {"title": "foo"}, "id": "42"}', + ], + ) + assert result.returncode == 0 + assert '"title":"foo"' in result.stdout + assert '"id":"42"' in result.stdout + + def test_cli_strict_overlap_contain(self) -> None: + result1 = _run_cli(["shapes"], "[2331,1185,12]") + assert result1.returncode == 0 + result2 = _run_cli(["tiles", "12"], result1.stdout) + assert result2.returncode == 0 + assert result2.stdout == "[2331, 1185, 12]\n" diff --git a/tests/mercantests/test_cli.py b/tests/mercantests/test_cli.py index 5ae15580..a000ee54 100644 --- a/tests/mercantests/test_cli.py +++ b/tests/mercantests/test_cli.py @@ -5,7 +5,7 @@ import pytest from click.testing import CliRunner -from utiles.cli import cli +from utiles._legacy.cli import cli def test_cli_shapes_failure() -> None: diff --git a/tests/test_exports.py b/tests/test_exports.py index bb5b0cda..63fe5ffd 100644 --- a/tests/test_exports.py +++ b/tests/test_exports.py @@ -1,7 +1,7 @@ from collections import Counter import utiles -from utiles import libutiles +from utiles import _utiles as libutiles def test_import() -> None: diff --git a/tests/test_mbtiles.py b/tests/test_mbtiles.py new file mode 100644 index 00000000..5f08823c --- /dev/null +++ b/tests/test_mbtiles.py @@ -0,0 +1,2 @@ +def test_todo() -> None: + assert True diff --git a/tests/test_tiletype.py b/tests/test_tiletype.py index 26ac81bb..52bd972c 100644 --- a/tests/test_tiletype.py +++ b/tests/test_tiletype.py @@ -8,7 +8,21 @@ Extensions = Union[str, bool] PWD = Path(__file__).parent -REPO_ROOT = PWD.parent + + +def _repo_root() -> Path: + _root = PWD + for _i in range(5): + _root = _root.parent + if (_root / ".github").is_dir(): + return _root + msg = "Could not find repo root" + raise RuntimeError(msg) + + +REPO_ROOT = _repo_root() + +# go up and find dir with sub dir ".github" def tiletype(buffer: bytes) -> Extensions: @@ -56,6 +70,9 @@ def tiletype(buffer: bytes) -> Extensions: # gzip: recklessly assumes contents are PBF. elif buffer[0] == 0x1F and buffer[1] == 0x8B: return "pbfgz" + # if buffer starts with '{' or '[' assume JSON + elif buffer[0] == 0x7B or buffer[0] == 0x5B: + return "json" return False @@ -77,9 +94,15 @@ def tiletype(buffer: bytes) -> Extensions: "tux_alpha.webp": "webp", "unknown.txt": False, "webp-550x368.webp": "webp", + "tile-arr.json": "json", + "tile-obj.json": "json", } +def test_found_test_files() -> None: + assert len(TEST_TILES_BYTES) == len(TEST_TILE_NAME2TYPE) + + @pytest.mark.parametrize( "tile", TEST_TILES_BYTES, diff --git a/tests/test_utiles.py b/tests/test_utiles.py index fd9c56cc..b76e6346 100644 --- a/tests/test_utiles.py +++ b/tests/test_utiles.py @@ -5,7 +5,6 @@ import pytest from pytest_benchmark.fixture import BenchmarkFixture -from typing_extensions import TypedDict import utiles from utiles import Tile @@ -27,12 +26,6 @@ def test_version() -> None: assert utiles.__version__ == pyproject_version -class TileDict(TypedDict): - x: int - y: int - z: int - - @pytest.mark.parametrize( "tile,quadkey", [