diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..161c442 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,15 @@ +{ + "name": "Rust", + "image": "mcr.microsoft.com/devcontainers/rust:1-1-bullseye", + // Features to add to the dev container. More info: https://containers.dev/features. + "features": { + "ghcr.io/devcontainers/features/node:1": { + "version": "20", + "nvmVersion": "latest" + }, + "ghcr.io/devcontainers/features/python:1": { + "installTools": true, + "version": "latest" + } + } +} \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..34b667e --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,20 @@ +name: Tests + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: cargo build + - run: cargo test + - run: cargo doc + - run: cargo clippy diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..e69220c --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1759 @@ +# 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 = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "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 = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[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 = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[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 = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets 0.48.5", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "ctor" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding 2.3.1", +] + +[[package]] +name = "futures" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-executor" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-macro" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gbfs_types" +version = "0.1.0" +dependencies = [ + "futures", + "geo-types", + "http 1.0.0", + "napi", + "napi-derive", + "pretty_assertions", + "pyo3", + "reqwest", + "serde", + "serde_json", + "serde_with", + "url 2.5.0", + "url_serde", +] + +[[package]] +name = "geo-types" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567495020b114f1ce9bed679b29975aa0bfae06ac22beacd5cfde5dabe7b05d6" +dependencies = [ + "approx", + "num-traits", + "serde", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "h2" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.11", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[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 = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.11", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.11", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "indoc" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[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.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "libloading" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[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 = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[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.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "napi" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1133249c46e92da921bafc8aba4912bf84d6c475f7625183772ed2d0844dc3a7" +dependencies = [ + "bitflags 2.4.1", + "ctor", + "napi-derive", + "napi-sys", + "once_cell", +] + +[[package]] +name = "napi-derive" +version = "2.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b5af262f1d8e660742eb722abc7113a5b3c3de4144d0ef23ede2518672ceff1" +dependencies = [ + "cfg-if", + "convert_case", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea236321b521d6926213a2021e407b0562e28a257c037a45919e414d2cdb4f8" +dependencies = [ + "convert_case", + "once_cell", + "proc-macro2", + "quote", + "regex", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2503fa6af34dc83fb74888df8b22afe933b58d37daf7d80424b1c60c68196b8b" +dependencies = [ + "libloading", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[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.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[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 0.48.5", +] + +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pyo3" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e8453b658fe480c3e70c8ed4e3d3ec33eb74988bd186561b0cc66b85c3bc4b" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "parking_lot", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "serde", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96fe70b176a89cff78f2fa7b3c930081e163d5379b4dcdf993e3ae29ca662e5" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "214929900fd25e6604661ed9cf349727c8920d47deff196c4e28165a6ef2a96b" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac53072f717aa1bfa4db832b39de8c875b7c7af4f4a6fe93cdbf9264cf8383b" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7774b5a8282bd4f25f803b1f0d945120be959a36c72e08e7cd031c792fdfd424" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[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", + "regex-syntax", +] + +[[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", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "reqwest" +version = "0.11.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http 0.2.11", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding 2.3.1", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url 2.5.0", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.1.0", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "target-lexicon" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" + +[[package]] +name = "tempfile" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "time" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[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 = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-bidi" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unindent" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" + +[[package]] +name = "url" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +dependencies = [ + "idna 0.1.5", + "matches", + "percent-encoding 1.0.1", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna 0.5.0", + "percent-encoding 2.3.1", +] + +[[package]] +name = "url_serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74e7d099f1ee52f823d4bdd60c93c3602043c728f5db3b97bdb548467f7bddea" +dependencies = [ + "serde", + "url 1.7.2", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9855085 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "gbfs_types" +version = "0.1.0" +edition = "2021" +license = "CC-BY-3.0" +description = "Types for GBFS" + +[features] +default = ["reqwest_blocking", "pyo3", "napi"] +reqwest_blocking = ["reqwest/blocking"] +pyo3 = ["dep:pyo3"] +napi = ["dep:napi", "dep:napi-derive"] + +[dev-dependencies] +pretty_assertions = "1.4.0" + +[dependencies] +futures = "0.3.29" +http = "1.0.0" +serde_json = "1.0.108" +serde_with = "3.4.0" +url = "2.5.0" +url_serde = "0.2.0" + +[dependencies.geo-types] +version = "0.7.12" +features = ["serde"] + +[dependencies.serde] +version = "1.0.193" +features = ["derive"] + +[dependencies.reqwest] +version = "0.11.22" +features = ["json"] + +[dependencies.napi] +version = "2.14.1" +default-features = false +features = ["napi6"] +optional = true + +[dependencies.napi-derive] +version = "2.14.2" +optional = true + +[dependencies.pyo3] +version = "0.20" +features = ["serde"] +optional = true diff --git a/README.md b/README.md new file mode 100644 index 0000000..9c6d915 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ + +[GBFS](https://gbfs.org/) types in Rust. + +Most fields required by the GBFS specification are marked as `Option` in this crate, as they may not always present in the data provided by the GBFS feeds. + +Some helper functions are provided to fetch the data from the GBFS feeds. + +Maintained By [Fluctuo](https://fluctuo.com) \ No newline at end of file diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..39a2b6e --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base" + ] +} diff --git a/src/3.0-RC2/files/examples/specification/gbfs.json b/src/3.0-RC2/files/examples/specification/gbfs.json new file mode 100644 index 0000000..06e387a --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/gbfs.json @@ -0,0 +1,17 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 0, + "version": "3.0-RC2", + "data": { + "feeds": [ + { + "name": "system_information", + "url": "https://www.example.com/gbfs/1/system_information" + }, + { + "name": "station_information", + "url": "https://www.example.com/gbfs/1/station_information" + } + ] + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/examples/specification/gbfs_versions.json b/src/3.0-RC2/files/examples/specification/gbfs_versions.json new file mode 100644 index 0000000..468ba7b --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/gbfs_versions.json @@ -0,0 +1,17 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 0, + "version": "3.0-RC2", + "data": { + "versions": [ + { + "version": "2.0", + "url": "https://www.example.com/gbfs/2/gbfs" + }, + { + "version": "3.0-RC2", + "url": "https://www.example.com/gbfs/3/gbfs" + } + ] + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/examples/specification/geofencing_zones.json b/src/3.0-RC2/files/examples/specification/geofencing_zones.json new file mode 100644 index 0000000..07b4d2d --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/geofencing_zones.json @@ -0,0 +1,98 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 60, + "version": "3.0-RC2", + "data": { + "geofencing_zones": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [ + -122.578067, + 45.562982 + ], + [ + -122.661838, + 45.562741 + ], + [ + -122.661151, + 45.504542 + ], + [ + -122.578926, + 45.5046625 + ], + [ + -122.578067, + 45.562982 + ] + ] + ], + [ + [ + [ + -122.650680, + 45.548197 + ], + [ + -122.650852, + 45.534731 + ], + [ + -122.630939, + 45.535212 + ], + [ + -122.630424, + 45.548197 + ], + [ + -122.650680, + 45.548197 + ] + ] + ] + ] + }, + "properties": { + "name": [ + { + "text": "NE 24th/NE Knott", + "language": "en" + } + ], + "start": "2023-07-17T13:34:13+02:00", + "end": "2024-07-18T13:34:13+02:00", + "rules": [ + { + "vehicle_type_ids": [ + "moped1", + "car1" + ], + "ride_start_allowed": false, + "ride_end_allowed": false, + "ride_through_allowed": true, + "maximum_speed_kph": 10, + "station_parking": true + } + ] + } + } + ] + }, + "global_rules": [ + { + "ride_start_allowed": false, + "ride_end_allowed": false, + "ride_through_allowed": true + } + ] + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/examples/specification/manifest.json b/src/3.0-RC2/files/examples/specification/manifest.json new file mode 100644 index 0000000..cf5622f --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/manifest.json @@ -0,0 +1,35 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 0, + "version": "3.0-RC2", + "data": { + "datasets": [ + { + "system_id": "example_berlin", + "versions": [ + { + "version": "2.0", + "url": "https://berlin.example.com/gbfs/2/gbfs" + }, + { + "version": "3.0-RC2", + "url": "https://berlin.example.com/gbfs/3/gbfs" + } + ] + }, + { + "system_id": "example_paris", + "versions": [ + { + "version": "2.0", + "url": "https://paris.example.com/gbfs/2/gbfs" + }, + { + "version": "3.0-RC2", + "url": "https://paris.example.com/gbfs/3/gbfs" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/examples/specification/station_information-1.json b/src/3.0-RC2/files/examples/specification/station_information-1.json new file mode 100644 index 0000000..eba2a6e --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/station_information-1.json @@ -0,0 +1,33 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 0, + "version": "3.0-RC2", + "data": { + "stations": [ + { + "station_id": "pga", + "name": [ + { + "text": "Parking garage A", + "language": "en" + } + ], + "lat": 12.345678, + "lon": 45.678901, + "station_opening_hours": "Su-Th 05:00-22:00; Fr-Sa 05:00-01:00", + "parking_type": "underground_parking", + "parking_hoop": false, + "contact_phone": "+33109874321", + "is_charging_station": true, + "vehicle_docks_capacity": [ + { + "vehicle_type_ids": [ + "abc123" + ], + "count": 7 + } + ] + } + ] + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/examples/specification/station_information-2.json b/src/3.0-RC2/files/examples/specification/station_information-2.json new file mode 100644 index 0000000..15a06aa --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/station_information-2.json @@ -0,0 +1,68 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 0, + "version": "3.0-RC2", + "data": { + "stations": [ + { + "station_id": "station12", + "name": [ + { + "text": "SE Belmont & SE 10th", + "language": "en" + } + ], + "lat": 45.516445, + "lon": -122.655775, + "is_valet_station": false, + "is_virtual_station": true, + "is_charging_station": false, + "station_area": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [ + -122.655775, + 45.516445 + ], + [ + -122.655705, + 45.516445 + ], + [ + -122.655705, + 45.516495 + ], + [ + -122.655775, + 45.516495 + ], + [ + -122.655775, + 45.516445 + ] + ] + ] + ] + }, + "capacity": 16, + "vehicle_types_capacity": [ + { + "vehicle_type_ids": [ + "abc123", + "def456" + ], + "count": 15 + }, + { + "vehicle_type_ids": [ + "def456" + ], + "count": 1 + } + ] + } + ] + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/examples/specification/station_information-3.json b/src/3.0-RC2/files/examples/specification/station_information-3.json new file mode 100644 index 0000000..7ea1ff9 --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/station_information-3.json @@ -0,0 +1,24 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 60, + "version": "3.0-RC2", + "data": { + "stations": [ + { + "station_id": "425", + "name": [ + { + "text": "Coppertail", + "language": "en" + } + ], + "lat": 27.956333, + "lon": -82.430436, + "rental_uris": { + "android": "https://www.example.com/app?sid=1234567890&platform=android", + "ios": "https://www.example.com/app?sid=1234567890&platform=ios" + } + } + ] + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/examples/specification/station_status.json b/src/3.0-RC2/files/examples/specification/station_status.json new file mode 100644 index 0000000..0e66945 --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/station_status.json @@ -0,0 +1,80 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 0, + "version": "3.0-RC2", + "data": { + "stations": [ + { + "station_id": "station1", + "is_installed": true, + "is_renting": true, + "is_returning": true, + "last_reported": "2023-07-17T13:34:13+02:00", + "num_docks_available": 3, + "num_docks_disabled": 1, + "vehicle_docks_available": [ + { + "vehicle_type_ids": [ + "abc123", + "def456" + ], + "count": 2 + }, + { + "vehicle_type_ids": [ + "def456" + ], + "count": 1 + } + ], + "num_vehicles_available": 1, + "num_vehicles_disabled": 2, + "vehicle_types_available": [ + { + "vehicle_type_id": "abc123", + "count": 1 + }, + { + "vehicle_type_id": "def456", + "count": 0 + } + ] + }, + { + "station_id": "station2", + "is_installed": true, + "is_renting": true, + "is_returning": true, + "last_reported": "2023-07-17T13:34:13+02:00", + "num_docks_available": 8, + "num_docks_disabled": 1, + "vehicle_docks_available": [ + { + "vehicle_type_ids": [ + "abc123" + ], + "count": 6 + }, + { + "vehicle_type_ids": [ + "def456" + ], + "count": 2 + } + ], + "num_vehicles_available": 6, + "num_vehicles_disabled": 1, + "vehicle_types_available": [ + { + "vehicle_type_id": "abc123", + "count": 2 + }, + { + "vehicle_type_id": "def456", + "count": 4 + } + ] + } + ] + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/examples/specification/system_alerts.json b/src/3.0-RC2/files/examples/specification/system_alerts.json new file mode 100644 index 0000000..0a0b659 --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/system_alerts.json @@ -0,0 +1,43 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 60, + "version": "3.0-RC2", + "data": { + "alerts": [ + { + "alert_id": "21", + "type": "station_closure", + "station_ids": [ + "123", + "456", + "789" + ], + "times": [ + { + "start": "2023-07-17T13:34:13+02:00", + "end": "2023-07-18T13:34:13+02:00" + } + ], + "url": [ + { + "text": "https://example.com/more-info", + "language": "en" + } + ], + "summary": [ + { + "text": "Disruption of Service", + "language": "en" + } + ], + "description": [ + { + "text": "The three stations on Broadway will be out of service from 12:00am Nov 3 to 3:00pm Nov 6th to accommodate road work", + "language": "en" + } + ], + "last_updated": "2023-07-17T13:34:13+02:00" + } + ] + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/examples/specification/system_information-1.json b/src/3.0-RC2/files/examples/specification/system_information-1.json new file mode 100644 index 0000000..984c9d4 --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/system_information-1.json @@ -0,0 +1,69 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 1800, + "version": "3.0-RC2", + "data": { + "system_id": "example_cityname", + "languages": [ + "en" + ], + "name": [ + { + "text": "Example Bike Rental", + "language": "en" + } + ], + "short_name": [ + { + "text": "Example Bike", + "language": "en" + } + ], + "operator": [ + { + "text": "Example Sharing, Inc", + "language": "en" + } + ], + "opening_hours": "Apr 1-Nov 3 00:00-24:00", + "start_date": "2010-06-10", + "url": "https://www.example.com", + "purchase_url": "https://www.example.com", + "phone_number": "+18005551234", + "email": "customerservice@example.com", + "feed_contact_email": "datafeed@example.com", + "timezone": "America/Chicago", + "license_url": "https://www.example.com/data-license.html", + "terms_url": [ + { + "text": "https://www.example.com/en/terms", + "language": "en" + } + ], + "terms_last_updated": "2021-06-21", + "privacy_url": [ + { + "text": "https://www.example.com/en/privacy-policy", + "language": "en" + } + ], + "privacy_last_updated": "2019-01-13", + "rental_apps": { + "android": { + "discovery_uri": "com.example.android://", + "store_uri": "https://play.google.com/store/apps/details?id=com.example.android" + }, + "ios": { + "store_uri": "https://apps.apple.com/app/apple-store/id123456789", + "discovery_uri": "com.example.ios://" + } + }, + "brand_assets": { + "brand_last_modified": "2021-06-15", + "brand_image_url": "https://www.example.com/assets/brand_image.svg", + "brand_image_url_dark": "https://www.example.com/assets/brand_image_dark.svg", + "color": "#C2D32C", + "brand_terms_url": "https://www.example.com/assets/brand.pdf" + } + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/examples/specification/system_information-2.json b/src/3.0-RC2/files/examples/specification/system_information-2.json new file mode 100644 index 0000000..03e0c49 --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/system_information-2.json @@ -0,0 +1,28 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 60, + "version": "3.0-RC2", + "data": { + "name": [ + { + "text": "Example Bike Rental", + "language": "en" + } + ], + "system_id": "example_cityname", + "timezone": "America/Chicago", + "languages": [ + "en" + ], + "rental_apps": { + "android": { + "store_uri": "https://play.google.com/store/apps/details?id=com.example.android", + "discovery_uri": "com.example.android://" + }, + "ios": { + "store_uri": "https://apps.apple.com/app/apple-store/id123456789", + "discovery_uri": "com.example.ios://" + } + } + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/examples/specification/system_pricing_plans-1.json b/src/3.0-RC2/files/examples/specification/system_pricing_plans-1.json new file mode 100644 index 0000000..9514af9 --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/system_pricing_plans-1.json @@ -0,0 +1,46 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 0, + "version": "3.0-RC2", + "data": { + "plans": [ + { + "plan_id": "plan2", + "name": [ + { + "text": "One-Way", + "language": "en" + } + ], + "currency": "USD", + "price": 2.00, + "reservation_price_per_min": 0.15, + "is_taxable": false, + "description": [ + { + "text": "Includes 10km, overage fees apply after 10km.", + "language": "en" + } + ], + "per_km_pricing": [ + { + "start": 10, + "rate": 1.00, + "interval": 1.0, + "end": 25 + }, + { + "start": 25, + "rate": 0.50, + "interval": 1.0 + }, + { + "start": 25, + "rate": 3.00, + "interval": 5.0 + } + ] + } + ] + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/examples/specification/system_pricing_plans-2.json b/src/3.0-RC2/files/examples/specification/system_pricing_plans-2.json new file mode 100644 index 0000000..ec2a8fe --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/system_pricing_plans-2.json @@ -0,0 +1,41 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 0, + "version": "3.0-RC2", + "data": { + "plans": [ + { + "plan_id": "plan3", + "name": [ + { + "text": "Simple Rate", + "language": "en" + } + ], + "currency": "CAD", + "price": 3.00, + "is_taxable": true, + "description": [ + { + "text": "$3 unlock fee, $0.25 per kilometer and 0.50 per minute.", + "language": "en" + } + ], + "per_km_pricing": [ + { + "start": 0, + "rate": 0.25, + "interval": 1.0 + } + ], + "per_min_pricing": [ + { + "start": 0, + "rate": 0.50, + "interval": 1.0 + } + ] + } + ] + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/examples/specification/system_regions.json b/src/3.0-RC2/files/examples/specification/system_regions.json new file mode 100644 index 0000000..70c32b8 --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/system_regions.json @@ -0,0 +1,45 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 86400, + "version": "3.0-RC2", + "data": { + "regions": [ + { + "name": [ + { + "text": "North", + "language": "en" + } + ], + "region_id": "3" + }, + { + "name": [ + { + "text": "East", + "language": "en" + } + ], + "region_id": "4" + }, + { + "name": [ + { + "text": "South", + "language": "en" + } + ], + "region_id": "5" + }, + { + "name": [ + { + "text": "West", + "language": "en" + } + ], + "region_id": "6" + } + ] + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/examples/specification/vehicle_status-1.json b/src/3.0-RC2/files/examples/specification/vehicle_status-1.json new file mode 100644 index 0000000..9ae5a48 --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/vehicle_status-1.json @@ -0,0 +1,32 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 0, + "version": "3.0-RC2", + "data": { + "vehicles": [ + { + "vehicle_id": "973a5c94-c288-4a2b-afa6-de8aeb6ae2e5", + "last_reported": "2023-07-17T13:34:13+02:00", + "lat": 12.345678, + "lon": 56.789012, + "is_reserved": false, + "is_disabled": false, + "vehicle_type_id": "abc123", + "rental_uris": { + "android": "https://www.example.com/app?vehicle_id=973a5c94-c288-4a2b-afa6-de8aeb6ae2e5&platform=android&", + "ios": "https://www.example.com/app?vehicle_id=973a5c94-c288-4a2b-afa6-de8aeb6ae2e5&platform=ios" + } + }, + { + "vehicle_id": "987fd100-b822-4347-86a4-b3eef8ca8b53", + "last_reported": "2023-07-17T13:34:13+02:00", + "is_reserved": false, + "is_disabled": false, + "vehicle_type_id": "def456", + "current_range_meters": 6543.0, + "station_id": "86", + "pricing_plan_id": "plan3" + } + ] + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/examples/specification/vehicle_status-2.json b/src/3.0-RC2/files/examples/specification/vehicle_status-2.json new file mode 100644 index 0000000..0d75c15 --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/vehicle_status-2.json @@ -0,0 +1,40 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 0, + "version": "3.0-RC2", + "data": { + "vehicles": [ + { + "vehicle_id": "45bd3fb7-a2d5-4def-9de1-c645844ba962", + "last_reported": "2023-07-17T13:34:13+02:00", + "lat": 12.345678, + "lon": 56.789012, + "is_reserved": false, + "is_disabled": false, + "vehicle_type_id": "abc123", + "current_range_meters": 400000.0, + "available_until": "2021-05-17T15:00:00Z", + "home_station_id": "station1", + "vehicle_equipment": [ + "child_seat_a", + "winter_tires" + ] + }, + { + "vehicle_id": "d4521def-7922-4e46-8e1d-8ac397239bd0", + "last_reported": "2023-07-17T13:34:13+02:00", + "is_reserved": false, + "is_disabled": false, + "vehicle_type_id": "def456", + "current_fuel_percent": 0.7, + "current_range_meters": 6543.0, + "station_id": "86", + "pricing_plan_id": "plan3", + "home_station_id": "146", + "vehicle_equipment": [ + "child_seat_a" + ] + } + ] + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/examples/specification/vehicle_types.json b/src/3.0-RC2/files/examples/specification/vehicle_types.json new file mode 100644 index 0000000..bcfbfb1 --- /dev/null +++ b/src/3.0-RC2/files/examples/specification/vehicle_types.json @@ -0,0 +1,141 @@ +{ + "last_updated": "2023-07-17T13:34:13+02:00", + "ttl": 0, + "version": "3.0-RC2", + "data": { + "vehicle_types": [ + { + "vehicle_type_id": "abc123", + "form_factor": "bicycle", + "propulsion_type": "human", + "name": [ + { + "text": "Example Basic Bike", + "language": "en" + } + ], + "wheel_count": 2, + "default_reserve_time": 30, + "return_constraint": "any_station", + "vehicle_assets": { + "icon_url": "https://www.example.com/assets/icon_bicycle.svg", + "icon_url_dark": "https://www.example.com/assets/icon_bicycle_dark.svg", + "icon_last_modified": "2021-06-15" + }, + "default_pricing_plan_id": "bike_plan_1", + "pricing_plan_ids": [ + "bike_plan_1", + "bike_plan_2", + "bike_plan_3" + ] + }, + { + "vehicle_type_id": "cargo123", + "form_factor": "cargo_bicycle", + "propulsion_type": "human", + "name": [ + { + "text": "Example Cargo Bike", + "language": "en" + } + ], + "description": [ + { + "text": "Extra comfortable seat with additional suspension.\n\nPlease be aware of the cargo box lock: you need to press it down before pulling it up again!", + "language": "en" + } + ], + "wheel_count": 3, + "default_reserve_time": 30, + "return_constraint": "roundtrip_station", + "vehicle_assets": { + "icon_url": "https://www.example.com/assets/icon_cargobicycle.svg", + "icon_url_dark": "https://www.example.com/assets/icon_cargobicycle_dark.svg", + "icon_last_modified": "2021-06-15" + }, + "default_pricing_plan_id": "cargo_plan_1", + "pricing_plan_ids": [ + "cargo_plan_1", + "cargo_plan_2", + "cargo_plan_3" + ] + }, + { + "vehicle_type_id": "def456", + "form_factor": "scooter_standing", + "propulsion_type": "electric", + "name": [ + { + "text": "Example E-scooter V2", + "language": "en" + } + ], + "wheel_count": 2, + "max_permitted_speed": 25, + "rated_power": 350, + "default_reserve_time": 30, + "max_range_meters": 12345.0, + "return_constraint": "free_floating", + "vehicle_assets": { + "icon_url": "https://www.example.com/assets/icon_escooter.svg", + "icon_url_dark": "https://www.example.com/assets/icon_escooter_dark.svg", + "icon_last_modified": "2021-06-15" + }, + "default_pricing_plan_id": "scooter_plan_1" + }, + { + "vehicle_type_id": "car1", + "form_factor": "car", + "rider_capacity": 5, + "cargo_volume_capacity": 200, + "propulsion_type": "combustion_diesel", + "eco_labels": [ + { + "country_code": "FR", + "eco_sticker": "critair_1" + }, + { + "country_code": "DE", + "eco_sticker": "euro_2" + } + ], + "name": [ + { + "text": "Four-door Sedan", + "language": "en" + } + ], + "wheel_count": 4, + "default_reserve_time": 0, + "max_range_meters": 523992.0, + "return_constraint": "roundtrip_station", + "vehicle_accessories": [ + "doors_4", + "automatic", + "cruise_control" + ], + "g_CO2_km": 120, + "vehicle_image": "https://www.example.com/assets/renault-clio.jpg", + "make": [ + { + "text": "Renault", + "language": "en" + } + ], + "model": [ + { + "text": "Clio", + "language": "en" + } + ], + "color": "white", + "vehicle_assets": { + "icon_url": "https://www.example.com/assets/icon_car.svg", + "icon_url_dark": "https://www.example.com/assets/icon_car_dark.svg", + "icon_last_modified": "2021-06-15" + }, + "default_pricing_plan_id": "car_plan_1" + } + ] + } +} \ No newline at end of file diff --git a/src/3.0-RC2/files/gbfs.rs b/src/3.0-RC2/files/gbfs.rs new file mode 100644 index 0000000..f1f3dc2 --- /dev/null +++ b/src/3.0-RC2/files/gbfs.rs @@ -0,0 +1,185 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +use futures::try_join; + +use super::super::GbfsObjects; + +use super::*; +use crate::v3_0_rc2::types::*; +use crate::v3_0_rc2::urls::*; + +file_struct!(GbfsFile, GbfsData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GbfsData { + /// All of the feeds that are published by this auto-discovery file. + pub feeds: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GbfsDataFeeds { + /// Key identifying the type of feed this is. The key MUST be the base file name defined in the spec for the corresponding feed type + pub name: FeedType, + /// URL for the feed. Note that the actual feed endpoints (urls) may not be defined in the `file_name.json` format. + /// For example, a valid feed endpoint could end with `station_info` instead of `station_information.json`. + pub url: URL, +} + +impl GbfsData { + fn get_feed_url(&self, name: &str) -> Option { + self.feeds + .iter() + .find(|feed| feed.name == name) + .map(|feed| feed.url.clone()) + } + + pub async fn get_objects(&self) -> Result { + let vehicle_status = self.get_vehicle_status(); + let station_information = self.get_station_information(); + let station_status = self.get_station_status(); + let system_pricing_plans = self.get_system_pricing_plans(); + let vehicle_types = self.get_vehicle_types(); + + let ( + vehicle_status, + station_information, + station_status, + system_pricing_plans, + vehicle_types, + ) = try_join!( + vehicle_status, + station_information, + station_status, + system_pricing_plans, + vehicle_types + )?; + + let vehicle_status = vehicle_status.map(|f| f.data); + let station_information = station_information.map(|f| f.data); + let station_status = station_status.map(|f| f.data); + let system_pricing_plans = system_pricing_plans.map(|f| f.data); + let vehicle_types = vehicle_types.map(|f| f.data); + + Ok(GbfsObjects { + vehicle_status, + station_information, + station_status, + system_pricing_plans, + vehicle_types, + }) + } +} + +macro_rules! add_file_type { + ($fn_url_name: ident, $url_ty: ident, $fn_name: ident, $ty: ident, $name: expr) => { + impl GbfsData { + pub fn $fn_url_name(&self) -> Option<$url_ty> { + let url = self.get_feed_url($name)?; + + Some($url_ty::new(&url)) + } + + pub async fn $fn_name(&self) -> Result, reqwest::Error> { + let url = self.$fn_url_name(); + + if let Some(url) = url { + Ok(Some(url.fetch_async().await?)) + } else { + Ok(None) + } + } + } + }; +} + +add_file_type!( + get_gbfs_versions_url, + GbfsVersionsFileUrl, + get_gbfs_versions, + GbfsVersionsFile, + "gbfs_versions" +); + +add_file_type!( + get_geofencing_zones_url, + GeofencingZonesFileUrl, + get_geofencing_zones, + GeofencingZonesFile, + "geofencing_zones" +); + +add_file_type!( + get_station_information_url, + StationInformationFileUrl, + get_station_information, + StationInformationFile, + "station_information" +); + +add_file_type!( + get_station_status_url, + StationStatusFileUrl, + get_station_status, + StationStatusFile, + "station_status" +); + +add_file_type!( + get_system_alerts_url, + SystemAlertsFileUrl, + get_system_alerts, + SystemAlertsFile, + "system_alerts" +); + +add_file_type!( + get_system_information_url, + SystemInformationFileUrl, + get_system_information, + SystemInformationFile, + "system_information" +); + +add_file_type!( + get_system_pricing_plans_url, + SystemPricingPlansFileUrl, + get_system_pricing_plans, + SystemPricingPlansFile, + "system_pricing_plans" +); + +add_file_type!( + get_system_regions_url, + SystemRegionsFileUrl, + get_system_regions, + SystemRegionsFile, + "system_regions" +); + +add_file_type!( + get_vehicle_status_url, + VehicleStatusFileUrl, + get_vehicle_status, + VehicleStatusFile, + "vehicle_status" +); + +add_file_type!( + get_vehicle_types_url, + VehicleTypesFileUrl, + get_vehicle_types, + VehicleTypesFile, + "vehicle_types" +); diff --git a/src/3.0-RC2/files/gbfs_versions.rs b/src/3.0-RC2/files/gbfs_versions.rs new file mode 100644 index 0000000..d258e13 --- /dev/null +++ b/src/3.0-RC2/files/gbfs_versions.rs @@ -0,0 +1,33 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +use crate::v3_0_rc2::types::*; +use crate::v3_0_rc2::urls::*; + +file_struct!(GbfsVersionsFile, GbfsVersionsData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GbfsVersionsData { + /// Contains one object, as defined below, for each of the available versions of a feed. + /// The array MUST be sorted by increasing MAJOR and MINOR version number. + pub versions: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GbfsVersion { + /// The semantic version of the feed in the form `X.Y`. + pub version: String, + /// URL of the corresponding `gbfs.json` endpoint. + pub url: GbfsFileUrl, +} diff --git a/src/3.0-RC2/files/geofencing_zones.rs b/src/3.0-RC2/files/geofencing_zones.rs new file mode 100644 index 0000000..b61f7c8 --- /dev/null +++ b/src/3.0-RC2/files/geofencing_zones.rs @@ -0,0 +1,99 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +file_struct!(GeofencingZonesFile, GeofencingZonesData); + +use crate::v3_0_rc2::types::*; + +#[cfg(doc)] +use crate::v3_0_rc2::files::station_information; +#[cfg(doc)] +use crate::v3_0_rc2::files::geofencing_zones; + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GeofencingZonesData { + pub geofencing_zones: GeofencingZone, + /// restrictions that apply globally in all areas as the default restrictions, except where overridden with an explicit geofencing zone + pub global_rules: Option>, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// geofenced zone and its associated rules and attributes +pub struct GeofencingZone { + /// `FeatureCollection` (as per IETF RFC 7946). + pub r#type: String, + pub features: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Feature { + /// `Feature` (as per IETF RFC 7946). + pub r#type: String, + /// A polygon that describes where rides may or may not be able to start, end, go through, or have other limitations or affordances. + /// Rules may only apply to the interior of a polygon. + /// All geofencing zones contained in this list are public (meaning they can be displayed on a map for public use). + pub geometry: geo_json::MultiPolygon, + /// Properties describing travel allowances and limitations. + pub properties: Properties, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Properties { + /// Public name of the geofencing zone. + pub name: Vec, + /// Start time of the geofencing zone. If the geofencing zone is always active, this can be omitted. + pub start: Option, + /// End time of the geofencing zone. If the geofencing zone is always active, this can be omitted. + pub end: Option, + /// Restrictions that apply within the area of the polygon. + pub rules: Option>, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Geofencing Rule Precedence +/// +/// Geofencing Rule objects are specified within arrays for the rules and [global_rules](GeofencingZonesData::global_rules) fields of [geofencing_zones] to allow for different restrictions for different vehicle types. +/// When multiple rules in the same array apply to a particular vehicle type, per the semantics of the vehicle_type_ids field, then the earlier rule (in order of the JSON file) takes precedence for that vehicle type. +/// +/// When multiple overlapping polygons define rules that apply to a particular vehicle type, +/// then the rules from the earlier polygon (in order of the JSON file) takes precedence for that vehicle type in the overlapping area. +/// Polygons with inactive time ranges should be excluded from consideration when considering precedence. +/// +/// When a polygon and the [global_rules](GeofencingZonesData::global_rules) field define rules that apply to a particular vehicle type, then the rules from the polygon take precedence for that vehicle type in the area of the polygon. +pub struct Rule { + /// Array of IDs of vehicle types for which any restrictions SHOULD be applied. + /// If vehicle type IDs are not specified, then restrictions apply to all vehicle types. + pub vehicle_type_ids: Option>, + /// Is the ride allowed to start in this zone? + pub ride_start_allowed: bool, + /// Is the ride allowed to end in this zone? + pub ride_end_allowed: bool, + /// Is the ride allowed to travel through this zone? + pub ride_through_allowed: bool, + /// What is the maximum speed allowed, in kilometers per hour? + /// + /// If there is no maximum speed to observe, this can be omitted. + pub maximum_speed_kph: Option, + /// Can vehicles only be parked at stations defined in [station_information] within this geofence zone? + pub station_parking: Option, +} diff --git a/src/3.0-RC2/files/manifest.rs b/src/3.0-RC2/files/manifest.rs new file mode 100644 index 0000000..08c8944 --- /dev/null +++ b/src/3.0-RC2/files/manifest.rs @@ -0,0 +1,33 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use crate::v3_0_rc2::types::*; + +use serde::{Deserialize, Serialize}; + +use crate::v3_0_rc2::files::gbfs_versions::GbfsVersion; + +file_struct!(ManifestFile, ManifestData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ManifestData { + pub datasets: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct System { + /// The `system_id` from `system_information.json` for the corresponding data set(s). + pub system_id: SystemID, + /// Contains one object, as defined below, for each of the available versions of a feed. + /// The array MUST be sorted by increasing MAJOR and MINOR version number. + pub versions: Vec, +} diff --git a/src/3.0-RC2/files/mod.rs b/src/3.0-RC2/files/mod.rs new file mode 100644 index 0000000..ad18574 --- /dev/null +++ b/src/3.0-RC2/files/mod.rs @@ -0,0 +1,169 @@ +macro_rules! file_struct { + ( $ty: ident, $data: ty ) => { + #[cfg_attr(feature = "napi", napi(object))] + #[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] + #[serde_with::skip_serializing_none] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct $ty { + /// Indicates the last time data in the feed was updated. This timestamp represents the publisher's knowledge of the current state of the system at this point in time. + pub last_updated: Timestamp, + /// Number of seconds before the data in the feed will be updated again (0 if the data should always be refreshed). + pub ttl: u32, + /// GBFS version number to which the feed conforms, according to the versioning framework. + pub version: String, + /// Response data. + pub data: $data, + } + }; +} + +pub mod gbfs; +pub mod gbfs_versions; +pub mod geofencing_zones; +pub mod manifest; +pub mod station_information; +pub mod station_status; +pub mod system_alerts; +pub mod system_information; +pub mod system_pricing_plans; +pub mod system_regions; +pub mod vehicle_status; +pub mod vehicle_types; + +pub use self::gbfs::{GbfsData, GbfsFile}; +pub use self::gbfs_versions::{GbfsVersionsData, GbfsVersionsFile}; +pub use self::geofencing_zones::{GeofencingZonesData, GeofencingZonesFile}; +pub use self::manifest::{ManifestData, ManifestFile}; +pub use self::station_information::{StationInformationData, StationInformationFile}; +pub use self::station_status::{StationStatusData, StationStatusFile}; +pub use self::system_alerts::{SystemAlertsData, SystemAlertsFile}; +pub use self::system_information::{SystemInformationData, SystemInformationFile}; +pub use self::system_pricing_plans::{SystemPricingPlansData, SystemPricingPlansFile}; +pub use self::system_regions::{SystemRegionsData, SystemRegionsFile}; +pub use self::vehicle_status::{VehicleStatusData, VehicleStatusFile}; +pub use self::vehicle_types::{VehicleTypesData, VehicleTypesFile}; + +#[cfg(test)] +mod tests { + use pretty_assertions::assert_eq; + + fn test_file(example: &str) { + let json_value: serde_json::Value = serde_json::from_str(example).unwrap(); + + let file: T = serde_json::from_str(example).unwrap(); + + let json_value2: serde_json::Value = + serde_json::from_str(&serde_json::to_string(&file).unwrap()).unwrap(); + + assert_eq!(json_value, json_value2); + } + + #[test] + fn gbfs() { + let gbfs = include_str!("./examples/specification/gbfs.json"); + + test_file::(gbfs); + } + + #[test] + fn manifest() { + let manifest = include_str!("./examples/specification/manifest.json"); + + test_file::(manifest); + } + + #[test] + fn gbfs_versions() { + let gbfs_versions = include_str!("./examples/specification/gbfs_versions.json"); + + test_file::(gbfs_versions); + } + + #[test] + fn system_information() { + let system_information = include_str!("./examples/specification/system_information-1.json"); + + test_file::(system_information); + + let system_information = include_str!("./examples/specification/system_information-2.json"); + + test_file::(system_information); + } + + #[test] + fn vehicle_types() { + let vehicle_types = include_str!("./examples/specification/vehicle_types.json"); + + test_file::(vehicle_types); + } + + #[test] + fn station_information() { + let station_information = + include_str!("./examples/specification/station_information-1.json"); + + test_file::(station_information); + + let station_information = + include_str!("./examples/specification/station_information-2.json"); + + test_file::(station_information); + + let station_information = + include_str!("./examples/specification/station_information-3.json"); + + test_file::(station_information); + } + + #[test] + fn station_status() { + let station_status = include_str!("./examples/specification/station_status.json"); + + test_file::(station_status); + } + + #[test] + fn vehicle_status() { + let vehicle_status = include_str!("./examples/specification/vehicle_status-1.json"); + + test_file::(vehicle_status); + + let vehicle_status = include_str!("./examples/specification/vehicle_status-2.json"); + + test_file::(vehicle_status); + } + + #[test] + fn system_regions() { + let system_regions = include_str!("./examples/specification/system_regions.json"); + + test_file::(system_regions); + } + + #[test] + fn system_pricing_plans() { + let system_pricing_plans = + include_str!("./examples/specification/system_pricing_plans-1.json"); + + test_file::(system_pricing_plans); + + let system_pricing_plans = + include_str!("./examples/specification/system_pricing_plans-2.json"); + + test_file::(system_pricing_plans); + } + + #[test] + fn system_alerts() { + let system_alerts = include_str!("./examples/specification/system_alerts.json"); + + test_file::(system_alerts); + } + + #[test] + fn geofencing_zones() { + let geofencing_zones = include_str!("./examples/specification/geofencing_zones.json"); + + test_file::(geofencing_zones); + } +} diff --git a/src/3.0-RC2/files/station_information.rs b/src/3.0-RC2/files/station_information.rs new file mode 100644 index 0000000..9211ded --- /dev/null +++ b/src/3.0-RC2/files/station_information.rs @@ -0,0 +1,132 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +use crate::v3_0_rc2::types::*; + +file_struct!(StationInformationFile, StationInformationData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Contains one object per station in the system as defined below. +pub struct StationInformationData { + pub stations: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Station { + /// Identifier of a station. + pub station_id: StationID, + /// The public name of the station for display in maps, digital signage, and other text applications. + /// Names SHOULD reflect the station location through the use of a cross street or local landmark. + /// Abbreviations SHOULD NOT be used for names and other text (for example, "St." for "Street") unless a location is called by its abbreviated name (for example, “JFK Airport”). + /// + /// Examples: + /// + /// - Broadway and East 22nd Street + /// - Convention Center + /// - Central Park South + pub name: Option>, + /// Short name or other type of identifier. + pub short_name: Option>, + /// Latitude of the station in decimal degrees. + /// This field SHOULD have a precision of 6 decimal places (0.000001). + pub lat: Latitude, + /// Longitude of the station in decimal degrees. + /// This field SHOULD have a precision of 6 decimal places (0.000001). + pub lon: Longitude, + /// Address (street number and name) where station is located. + /// This MUST be a valid address, not a free-form text description. + /// + /// Example: 1234 Main Street + pub address: Option, + /// Cross street or landmark where the station is located. + pub cross_street: Option, + /// Identifier of the region where station is located. + pub region_id: Option, + /// Postal code where station is located. + pub post_code: Option, + /// Hours of operation for the station. + /// If `station_opening_hours` is defined it overrides any `opening_hours` defined in `system_information.json` for the station for which it is defined. + pub station_opening_hours: Option, + /// Payment methods accepted at this station. + pub rental_methods: Option>, + /// Is this station a location with or without smart dock technology? + /// + /// - `true`: The station is a location without smart docking infrastructure. the station may be defined by a point (lat/lon) and/or station_area (below). + /// - `false`: The station consists of smart docking infrastructure (docks). + pub is_virtual_station: Option, + /// A GeoJSON MultiPolygon that describes the area of a virtual station. + /// + /// If `station_area` is supplied, then the record describes a virtual station. + /// + /// If `lat`/`lon` and `station_area` are both defined, the `lat`/`lon` is the significant coordinate of the station (for example, parking facility or valet drop-off and pick up point). + /// The station_area takes precedence over any `ride_start_allowed` and `ride_end_allowed` rules in overlapping `geofencing_zones`. + pub station_area: Option, + /// Type of parking station. + pub parking_type: Option, + /// Are parking hoops present at this station? + pub parking_hoop: Option, + /// Contact phone of the station. + pub contact_phone: Option, + /// Number of total docking points installed at this station, both available and unavailable, regardless of what vehicle types are allowed at each dock. + pub capacity: Option, + /// Used to model the parking capacity of virtual stations (defined using the is_virtual_station field) for each vehicle type that can be returned to this station. + /// The total number of vehicles from each of these objects SHOULD add up to match the value specified in the `capacity` field. + pub vehicle_types_capacity: Option>, + /// These objects are used to model the total docking capacity of a station, both available and unavailable, for each type of vehicle that may dock at this station. + /// The total number of docks from each of these objects SHOULD add up to match the value specified in the `capacity` field. + pub vehicle_docks_capacity: Option>, + /// Are valet services provided at this station? + pub is_valet_station: Option, + /// Does the station support charging of electric vehicles? + pub is_charging_station: Option, + /// Contains rental URIs for Android, iOS, and web + pub rental_uris: Option, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct VehicleTypesCapacity { + /// Vehicles types that may park at the virtual station. + pub vehicle_type_ids: Vec, + /// If the virtual station is defined by [station_area](Station::station_area), this is the number that can park within the station area. + /// + /// If [lat](Station::lat)/[lon](Station::lon) is defined, this is the number that can park at those coordinates. + pub count: u32, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct VehicleDocksCapacity { + /// Vehicles types that able to use a particular type of dock at the station. + pub vehicle_type_ids: Vec, + // number representing the total number of docks at the station, both available and unavailable, that may accept that vehicle types + pub count: u32, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RentalUris { + /// URI that can be passed to an Android app with an `android.intent.action.VIEW` Android intent to support Android Deep Links (). + pub android: Option, + /// URI that can be used on iOS to launch the rental app for this station. + pub ios: Option, + /// URL that can be used by a web browser to show more information about renting a vehicle at this station. + pub web: Option, +} diff --git a/src/3.0-RC2/files/station_status.rs b/src/3.0-RC2/files/station_status.rs new file mode 100644 index 0000000..e59f633 --- /dev/null +++ b/src/3.0-RC2/files/station_status.rs @@ -0,0 +1,81 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +use crate::v3_0_rc2::types::*; + +file_struct!(StationStatusFile, StationStatusData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Contains one object per station in the system as defined below. +pub struct StationStatusData { + pub stations: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Station { + /// Identifier of a station. + pub station_id: StationID, + /// Number of functional vehicles physically at the station that may be offered for rental. To know if the vehicles are available for rental, see [is_renting](Station::is_renting). + pub num_vehicles_available: u32, + /// Used to model the total number of each defined vehicle type available at a station. + /// The total number of vehicles from each of these objects SHOULD add up to match the value specified in the [num_vehicles_available](Station::num_vehicles_available) field. + pub vehicle_types_available: Option>, + /// Number of disabled vehicles of any type at the station. + pub num_vehicles_disabled: Option, + /// Number of functional docks physically at the station that are able to accept vehicles for return. To know if the docks are accepting vehicle returns, see [is_returning](Station::is_returning). + pub num_docks_available: Option, + /// Used to model the number of docks available for certain vehicle types. + pub vehicle_docks_available: Option>, + /// Number of disabled dock points at the station. + pub num_docks_disabled: Option, + /// Is the station currently on the street? + /// + /// In seasonal systems where equipment is removed during winter, boolean SHOULD be set to `false` during the off season. + /// May also be set to `false` to indicate planned (future) stations which have not yet been installed. + pub is_installed: bool, + /// Is the station currently renting vehicles? + /// + /// If the station is temporarily taken out of service and not allowing rentals, this field MUST be set to `false`. + /// + /// If a station becomes inaccessible to users due to road construction or other factors this field SHOULD be set to `false`. + /// Field SHOULD be set to `false` during hours or days when the system is not offering vehicles for rent. + pub is_renting: bool, + /// Is the station accepting vehicle returns? + /// + /// If the station is temporarily taken out of service and not allowing vehicle returns, this field MUST be set to `false`. + /// If a station becomes inaccessible to users due to road construction or other factors, this field SHOULD be set to `false`. + pub is_returning: bool, + /// The last time this station reported its status to the operator's backend. + pub last_reported: Option, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct VehicleTypeAvailable { + pub vehicle_type_id: VehicleTypeID, + /// A number representing the total number of available vehicles of this type at this station. + pub count: u32, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct VehicleDockAvailable { + pub vehicle_type_ids: Vec, + /// A number representing the number of docks available for those vehicle types at this station. + pub count: u32, +} diff --git a/src/3.0-RC2/files/system_alerts.rs b/src/3.0-RC2/files/system_alerts.rs new file mode 100644 index 0000000..1d4d611 --- /dev/null +++ b/src/3.0-RC2/files/system_alerts.rs @@ -0,0 +1,55 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +use crate::v3_0_rc2::types::*; + +file_struct!(SystemAlertsFile, SystemAlertsData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SystemAlertsData { + pub alerts: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SystemAlert { + /// Identifier for this alert. + pub alert_id: AlertID, + pub r#type: String, + /// Indicating when the alert is in effect (for example, when the system or station is actually closed, or when a station is scheduled to be moved). + pub times: Vec, + /// If this is an alert that affects one or more stations, include their ID(s). Otherwise omit this field. + /// If both [station_ids][SystemAlert::station_ids] and [region_ids][SystemAlert::region_ids] are omitted, this alert affects the entire system. + pub station_ids: Option>, + /// If this system has regions, and if this alert only affects certain regions, include their ID(s). + pub region_ids: Option>, + /// URL where the customer can learn more information about this alert. + pub url: Option>, + /// A short summary of this alert to be displayed to the customer. + pub summary: Option>, + /// Detailed description of the alert. + pub description: Option>, + /// Indicates the last time the info for the alert was updated. + pub last_updated: Option, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AlertTime { + /// Start time of the alert. + pub start: Timestamp, + /// End time of the alert. If there is currently no end time planned for the alert, this can be omitted. + pub end: Option, +} diff --git a/src/3.0-RC2/files/system_information.rs b/src/3.0-RC2/files/system_information.rs new file mode 100644 index 0000000..5164e62 --- /dev/null +++ b/src/3.0-RC2/files/system_information.rs @@ -0,0 +1,144 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +use crate::v3_0_rc2::types::*; +use crate::v3_0_rc2::urls::*; + +file_struct!(SystemInformationFile, SystemInformationData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SystemInformationData { + /// This is a globally unique identifier for the vehicle share system. + /// Each distinct system or geographic area in which vehicles are operated MUST have its own unique system_id. + /// It is up to the publisher of the feed to guarantee uniqueness and MUST be checked against existing system_id fields in `systems.csv` to ensure this. + /// This value is intended to remain the same over the life of the system. + /// + /// System IDs SHOULD be recognizable as belonging to a particular system as opposed to random strings - for example, bcycle_austin or biketown_pdx. + pub system_id: SystemID, + /// List of languages used in translated strings. + pub languages: Option>, + /// Name of the system to be displayed to customers. + pub name: Option>, + /// Hours and dates of operation for the system in [OSM opening_hours](https://wiki.openstreetmap.org/wiki/Key:opening_hours) format. + pub opening_hours: Option, + /// Abbreviation for a system. + pub short_name: Option>, + /// Name of the system operator. + pub operator: Option>, + /// The URL of the vehicle share system. + pub url: Option, + /// URL where a customer can purchase a membership. + pub purchase_url: Option, + /// Date that the system began operations. + pub start_date: Option, + /// Date after which this data source will no longer be available to consuming applications. + /// + /// This OPTIONAL field SHOULD be used to notify 3rd party data consumers when a service is planning a permanent (non-seasonal) shutdown. + /// Publishers SHOULD include this date in their feeds as soon as they know of an impending shutdown. + /// Publishers SHOULD continue to publish feeds for 30 days following a permanent shutdown after which they SHOULD return a helpful http status code and text, for example `410 service no longer available ...` for all feed endpoint URLs. + pub termination_date: Option, + /// This OPTIONAL field SHOULD contain a single voice telephone number for the specified system’s customer service department. + pub phone_number: Option, + pub email: Option, + /// This OPTIONAL field SHOULD contain a single contact email address actively monitored by the operator’s customer service department. + /// This email address SHOULD be a direct contact point where riders can reach a customer service representative. + pub feed_contact_email: Option, + /// This field MUST contain a single contact email for feed consumers to report issues with the feed. + /// This email address SHOULD point to a stable email address, that does not correspond to an individual but rather the team or company that manages GBFS feeds. + pub manifest_url: Option, + /// The time zone where the system is located. + pub timezone: Option, + /// An identifier for a standard license from the SPDX License List. Provide `license_id` rather than `license_url` if the license is included in the SPDX License List. + pub license_id: Option, + /// A fully qualified URL of a page that defines the license terms for the GBFS data for this system. + pub license_url: Option, + /// If the feed license requires attribution, name of the organization to which attribution should be provided. + pub attribution_organization_name: Option>, + /// URL of the organization to which attribution should be provided. + pub attribution_url: Option, + pub brand_assets: Option, + /// A fully qualified URL pointing to the terms of service (also often called "terms of use" or "terms and conditions") for the service. + pub terms_url: Option>, + /// The date that the terms of service provided at `terms_url` were last updated. + pub terms_last_updated: Option, + /// A fully qualified URL pointing to the privacy policy for the service. + pub privacy_url: Option>, + /// The date that the privacy policy provided at `privacy_url` was last updated. + pub privacy_last_updated: Option, + /// Contains rental app information for android and ios. + pub rental_apps: Option, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct BrandAssets { + /// Date that indicates the last time any included brand assets were updated or modified. + pub brand_last_modified: Option, + /// A fully qualified URL pointing to the location of a page that defines the license terms of brand icons, colors, or other trademark information. + /// This field MUST NOT take the place of license_url or license_id. + pub brand_terms_url: Option, + /// A fully qualified URL pointing to the location of a graphic file representing the brand for the service. + /// File MUST be in SVG V1.1 format and MUST be either square or round. + pub brand_image_url: URL, + /// A fully qualified URL pointing to the location of a graphic file representing the brand for the service for use in dark mode applications. + /// File MUST be in SVG V1.1 format and MUST be either square or round. + pub brand_image_url_dark: Option, + /// Color used to represent the brand for the service expressed as a 6 digit hexadecimal color code in the form #000000. + pub color: Option, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RentalApps { + /// Contains rental app download and app discovery information for the Android platform in the `store_uri` and `discovery_uri` fields. + pub android: Option, + /// Contains rental information for the iOS platform in the store_uri and discovery_uri fields. + pub ios: Option, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AndroidRentalApp { + /// URI where the rental Android app can be downloaded from. Typically this will be a URI to an app store, such as Google Play. + /// If the URI points to an app store, the URI SHOULD follow Android best practices so the viewing app can directly open + /// + /// Example value: `https://play.google.com/store/apps/details?id=com.example.android` + pub store_uri: URI, + /// URI that can be used to discover if the rental Android app is installed on the device (for example, using `PackageManager.queryIntentActivities()`). + /// This intent is used by viewing apps to prioritize rental apps for a particular user based on whether they already have a particular rental app installed. + /// + /// Example value: `com.example.android://` + pub discovery_uri: URI, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct IosRentalApp { + /// URI where the rental iOS app can be downloaded from. + /// Typically this will be a URI to an app store, such as the Apple App Store. + /// If the URI points to an app store, the URI SHOULD follow iOS best practices so the viewing app can directly open the URI to the native app store app instead of a website. + /// + /// Example value: `https://apps.apple.com/app/apple-store/id123456789` + pub store_uri: URI, + /// URI that can be used to discover if the rental iOS app is installed on the device (for example, using `UIApplication canOpenURL:`). + /// This intent is used by viewing apps to prioritize rental apps for a particular user based on whether they already have a particular rental app installed. + /// + /// Example value: `com.example.ios://` + pub discovery_uri: URI, +} diff --git a/src/3.0-RC2/files/system_pricing_plans.rs b/src/3.0-RC2/files/system_pricing_plans.rs new file mode 100644 index 0000000..7d26411 --- /dev/null +++ b/src/3.0-RC2/files/system_pricing_plans.rs @@ -0,0 +1,79 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +use crate::v3_0_rc2::types::*; + +file_struct!(SystemPricingPlansFile, SystemPricingPlansData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Contains one object per vehicle that is currently deployed in the field. +pub struct SystemPricingPlansData { + pub plans: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SystemPricingPlan { + /// Identifier for a pricing plan in the system. + pub plan_id: PricingPlanID, + /// URL where the customer can learn more about this pricing plan. + pub url: Option, + /// Name of this pricing plan. + pub name: Option>, + /// Currency used to pay the fare. + pub currency: Currency, + /// Fare price, in the unit specified by currency. + /// + /// In case of non-rate price, this field is the total price. + /// In case of rate price, this field is the base price that is charged only once per trip (typically the price for unlocking) in addition to per_km_pricing and/or per_min_pricing. + pub price: Option, + /// The cost, described as per minute rate, to reserve the vehicle prior to beginning a rental. + /// This amount is charged for each minute of the vehicle reservation until the rental is initiated, or until the number of minutes defined in vehicle_types.json#default_reserve_time elapses, whichever comes first. + /// This field MUST NOT be combined in a single pricing plan with reservation_price_flat_rate. + pub reservation_price_per_min: Option, + /// The cost, described as a flat rate, to reserve the vehicle prior to beginning a rental. + /// This amount is charged once to reserve the vehicle for the duration of the time defined by vehicle_types.json#default_reserve_time. + /// This field MUST NOT be combined in a single pricing plan with reservation_price_per_min. + pub reservation_price_flat_rate: Option, + /// Will additional tax be added to the base price? + pub is_taxable: Option, + /// Customer-readable description of the pricing plan. + /// This SHOULD include the duration, price, conditions, etc. that the publisher would like users to see. + pub description: Option>, + /// When the price is a function of distance traveled, displayed in kilometers. + pub per_km_pricing: Option>, + /// When the price is a function of time traveled, displayed in minutes. + pub per_min_pricing: Option>, + /// Is there currently an increase in price in response to increased demand in this pricing plan? If this field is empty, it means there is no surge pricing in effect. + pub surge_pricing: Option, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct PricingRate { + /// The unit at which this segment rate starts being charged (inclusive). + pub start: i32, + /// Rate that is charged for each unit interval after the start. + /// Can be a negative number, which indicates that the traveler will receive a discount. + pub rate: f64, + /// Interval at which the rate of this segment is either reapplied indefinitely, or if defined, up until (but not including) `end` unit. + /// + /// An interval of 0 indicates the rate is only charged once. + pub interval: NonNegativeFloat, + /// The unit at which the rate will no longer apply (exclusive) for example, if end is `20` the rate no longer applies at `20.00`. + /// + /// If this field is empty, the price issued for this segment is charged until the trip ends, in addition to the cost of any subsequent segments. + pub end: Option, +} diff --git a/src/3.0-RC2/files/system_regions.rs b/src/3.0-RC2/files/system_regions.rs new file mode 100644 index 0000000..ebd69eb --- /dev/null +++ b/src/3.0-RC2/files/system_regions.rs @@ -0,0 +1,30 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +use crate::v3_0_rc2::types::*; + +file_struct!(SystemRegionsFile, SystemRegionsData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SystemRegionsData { + pub regions: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Region { + /// Identifier for the region. + pub region_id: RegionID, + /// Public name for this region. + pub name: Vec, +} diff --git a/src/3.0-RC2/files/vehicle_status.rs b/src/3.0-RC2/files/vehicle_status.rs new file mode 100644 index 0000000..89cbd9d --- /dev/null +++ b/src/3.0-RC2/files/vehicle_status.rs @@ -0,0 +1,77 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +use crate::v3_0_rc2::types::*; + +file_struct!(VehicleStatusFile, VehicleStatusData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Contains one object per vehicle that is currently deployed in the field. +pub struct VehicleStatusData { + /// Array that contains one object per vehicle that is currently deployed in the field and not part of an active rental. + pub vehicles: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Vehicle that is currently deployed in the field +pub struct Vehicle { + /// Identifier of a vehicle. + pub vehicle_id: VehicleID, + /// Latitude of the vehicle in decimal degrees. + pub lat: Option, + /// Longitude of the vehicle in decimal degrees. + pub lon: Option, + /// Is the vehicle currently reserved? + pub is_reserved: bool, + /// Is the vehicle currently disabled? + pub is_disabled: bool, + /// Rental URIs for Android, iOS, and web + pub rental_uris: Option, + /// The [VehicleType](crate::v3_0_rc2::files::vehicle_types::VehicleType) of this vehicle. + pub vehicle_type_id: Option, + /// The last time this vehicle reported its status to the operator's backend. + pub last_reported: Option, + /// This value represents the furthest distance in meters that the vehicle can travel with the vehicle's current charge or fuel (without recharging or refueling). + /// Note that in the case of carsharing, the given range is indicative and can be different from the one displayed on the vehicle's dashboard. + pub current_range_meters: Option, + /// This value represents the current percentage, expressed from 0 to 1, of fuel or battery power remaining in the vehicle. + pub current_fuel_percent: Option, + /// If the vehicle is currently at a [Station](crate::v3_0_rc2::files::station_information::Station). + pub station_id: Option, + /// The [Station](crate::v3_0_rc2::files::station_information::Station) where this vehicle must be returned to. + pub home_station_id: Option, + /// The [PricingPlan](crate::v3_0_rc2::files::system_pricing_plans::SystemPricingPlan) this vehicle is eligible for. + /// + /// If this field is defined it supersedes [default_pricing_plan_id](crate::v3_0_rc2::files::system_information::SystemInformation::default_pricing_plan_id). + pub pricing_plan_id: Option, + /// List of vehicle equipment provided by the operator in addition to the accessories already provided in the vehicle (field vehicle_accessories of vehicle_types.json) but subject to more frequent updates. + pub vehicle_equipment: Option>, + /// The date and time when any rental of the vehicle must be completed. + /// The vehicle must be returned and made available for the next user by this time. + /// If this field is empty, it indicates that the vehicle is available indefinitely. + pub available_until: Option, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct RentalUris { + /// URI that can be passed to an Android app with an `android.intent.action.VIEW` Android intent to support Android Deep Links (https://developer.android.com/training/app-links/deep-linking). + pub android: Option, + /// URI that can be used on iOS to launch the rental app for this vehicle. + pub ios: Option, + /// URL that can be used by a web browser to show more information about renting a vehicle at this vehicle. + pub web: Option, +} diff --git a/src/3.0-RC2/files/vehicle_types.rs b/src/3.0-RC2/files/vehicle_types.rs new file mode 100644 index 0000000..b974154 --- /dev/null +++ b/src/3.0-RC2/files/vehicle_types.rs @@ -0,0 +1,103 @@ +#![allow(non_snake_case)] + +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +use crate::v3_0_rc2::types::*; + +file_struct!(VehicleTypesFile, VehicleTypesData); + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Contains one object per vehicle that is currently deployed in the field. +pub struct VehicleTypesData { + pub vehicle_types: Vec, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +/// Vehicle that is currently deployed in the field +pub struct VehicleType { + /// Unique identifier of a vehicle type. + pub vehicle_type_id: VehicleTypeID, + /// The vehicle's general form factor. + pub form_factor: Option, + /// The number of riders (driver included) the vehicle can legally accommodate. + pub rider_capacity: Option, + /// Cargo volume available in the vehicle, expressed in liters. For cars, it corresponds to the space between the boot floor, including the storage under the hatch, to the rear shelf in the trunk. + pub cargo_volume_capacity: Option, + /// The capacity of the vehicle cargo space (excluding passengers), expressed in kilograms. + pub cargo_load_capacity: Option, + /// The primary propulsion type of the vehicle. + pub propulsion_type: Option, + /// Vehicle air quality certificate. Official anti-pollution certificate, based on the information on the vehicle's registration certificate, attesting to its level of pollutant emissions based on a defined standard. In Europe, for example, it is the European emission standard. The aim of this measure is to encourage the use of the least polluting vehicles by allowing them to drive during pollution peaks or in low emission zones. + pub eco_labels: Option>, + /// This represents the furthest distance in meters that the vehicle can travel without recharging or refueling when it has the maximum amount of energy potential (for example, a full battery or full tank of gas). + pub max_range_meters: Option, + /// The public name of this vehicle type. + pub name: Option>, + /// Description of accessories available in the vehicle. These accessories are part of the vehicle and are not supposed to change frequently. + pub vehicle_accessories: Option>, + /// Maximum quantity of CO2, in grams, emitted per kilometer, according to the [WLTP](https://en.wikipedia.org/wiki/Worldwide_Harmonised_Light_Vehicles_Test_Procedure). + pub g_CO2_km: Option, + /// URL to an image that would assist the user in identifying the vehicle (for example, an image of the vehicle or a logo). + pub vehicle_image: Option, + /// The name of the vehicle manufacturer. + pub make: Option>, + /// The name of the vehicle model. + pub model: Option>, + /// The color of the vehicle. + pub color: Option, + /// Customer-readable description of the vehicle type outlining special features or how-tos. + pub description: Option>, + /// Number of wheels this vehicle type has. + pub wheel_count: Option, + /// The maximum speed in kilometers per hour this vehicle is permitted to reach in accordance with local permit and regulations. + pub max_permitted_speed: Option, + /// The rated power of the motor for this vehicle type in watts. + pub rated_power: Option, + /// Maximum time in minutes that a vehicle can be reserved before a rental begins. + /// If default_reserve_time is set to 0, the vehicle type cannot be reserved. + pub default_reserve_time: Option, + /// The conditions for returning the vehicle at the end of the rental. + pub return_constraint: Option, + pub vehicle_assets: Option, + /// A plan_id, as defined in system_pricing_plans.json, that identifies a default pricing plan for this vehicle to be used by trip planning applications for purposes of calculating the cost of a single trip using this vehicle type. + /// This default pricing plan is superseded by `pricing_plan_id` when `pricing_plan_id` is defined in `vehicle_status.json`. + pub default_pricing_plan_id: Option, + /// All pricing plan IDs that are applied to this vehicle type. + pub pricing_plan_ids: Option>, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct EcoLabel { + /// Country where the `eco_sticker` applies. + pub country_code: CountryCode, + /// Name of the eco label. + pub eco_sticker: String, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct VehicleAsset { + /// A fully qualified URL pointing to the location of a graphic icon file that MAY be used to represent this vehicle type on maps and in other applications. + pub icon_url: String, + /// A fully qualified URL pointing to the location of a graphic icon file to be used to represent this vehicle type when in dark mode on maps and in other applications. + pub icon_url_dark: Option, + /// Date that indicates the last time any included vehicle icon images were modified or updated. + pub icon_last_modified: Option, +} diff --git a/src/3.0-RC2/mod.rs b/src/3.0-RC2/mod.rs new file mode 100644 index 0000000..e5baf26 --- /dev/null +++ b/src/3.0-RC2/mod.rs @@ -0,0 +1,23 @@ +pub mod files; +pub mod types; +pub mod urls; + +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GbfsObjects { + pub vehicle_status: Option, + pub station_status: Option, + pub station_information: Option, + pub vehicle_types: Option, + pub system_pricing_plans: Option, +} diff --git a/src/3.0-RC2/types.rs b/src/3.0-RC2/types.rs new file mode 100644 index 0000000..4d09008 --- /dev/null +++ b/src/3.0-RC2/types.rs @@ -0,0 +1,202 @@ +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +#[cfg(doc)] +use crate::v3_0_rc2::files::gbfs::GbfsFile; +#[cfg(doc)] +use crate::v3_0_rc2::files::gbfs_versions::GbfsVersionsFile; +#[cfg(doc)] +use crate::v3_0_rc2::files::geofencing_zones::GeofencingZonesFile; +#[cfg(doc)] +use crate::v3_0_rc2::files::station_information::StationInformationFile; +#[cfg(doc)] +use crate::v3_0_rc2::files::station_status::StationStatusFile; +#[cfg(doc)] +use crate::v3_0_rc2::files::system_alerts::SystemAlertsFile; +#[cfg(doc)] +use crate::v3_0_rc2::files::system_information::SystemInformationFile; +#[cfg(doc)] +use crate::v3_0_rc2::files::system_pricing_plans::SystemPricingPlansFile; +#[cfg(doc)] +use crate::v3_0_rc2::files::system_regions::SystemRegionsFile; +#[cfg(doc)] +use crate::v3_0_rc2::files::vehicle_status::VehicleStatusFile; +#[cfg(doc)] +use crate::v3_0_rc2::files::vehicle_types::VehicleTypesFile; + +#[cfg(doc)] +use crate::v3_0_rc2::files::manifest::System; +#[cfg(doc)] +use crate::v3_0_rc2::files::station_information::Station; +#[cfg(doc)] +use crate::v3_0_rc2::files::system_alerts::SystemAlert; +#[cfg(doc)] +use crate::v3_0_rc2::files::system_information::SystemInformationData; +#[cfg(doc)] +use crate::v3_0_rc2::files::system_pricing_plans::SystemPricingPlan; +#[cfg(doc)] +use crate::v3_0_rc2::files::system_regions::Region; +#[cfg(doc)] +use crate::v3_0_rc2::files::vehicle_status::Vehicle; +#[cfg(doc)] +use crate::v3_0_rc2::files::vehicle_types::VehicleType; + +/// Should be represented as a string that identifies that particular entity. An ID: +/// - MUST be unique within like fields (for example, `station_id` MUST be unique among stations). +/// - Does not have to be globally unique, unless otherwise specified. +/// - MUST be in the ASCII printable character range, space excluded (`0x21` to `0x7E`). +/// - SHOULD be restricted to `A-Z`, `a-z`, `0-9` and `.@:/_-` +/// - MUST be persistent for a given entity (station, plan, etc). An exception is floating bike bike_id, which MUST NOT be persistent for privacy reasons (see free_bike_status.json). (as of v2.0) +pub type ID = String; + +// A date in the ISO 8601 Complete Date Extended Format: YYYY-MM-DD . Example: `2019-09-13` for September 13th, 2019. +pub type Date = String; + +// Datetime (added in v2.3)- Combination of a date and a time following ISO 8601 notation. Attributes : year, month, day, hour, minute, second, and timezone. +pub type Datetime = String; + +/// Service time in the HH:MM:SS format for the time zone indicated in `system_information.json` (00:00:00 - 47:59:59). Time can stretch up to one additional day in the future to accommodate situations where, for example, a system was open from 11:30pm - 11pm the next day (23:30:00-47:00:00). +pub type Time = String; + +/// An email address. Example: `example@example.com` +pub type Email = String; + +/// Phone number in E.164 format. The phone number MUST start with a "+". The characters following the "+" MUST be integers and MUST NOT contain any hyphens, spaces or parentheses. +pub type PhoneNumber = String; + +/// A fully qualified URL that includes `http://` or `https://`. Any special characters in the URL MUST be correctly escaped. See the following for a description of how to create fully qualified URL values. +pub type URL = String; // url::URL; + +/// A fully qualified URI that includes the scheme (for example, com.example.android://). Any special characters in the URI MUST be correctly escaped. See the following for a description of how to create fully qualified URI values. Note that URIs MAY be URLs. +pub type URI = String; // http::Uri; + +/// Country code following the ISO 3166-1 alpha-2 notation. +pub type CountryCode = String; + +/// An IETF BCP 47 language code. For an introduction to IETF BCP 47, refer to and . Examples: `en` for English, `en-US` +pub type Language = String; + +/// WGS84 latitude in decimal degrees. The value MUST be greater than or equal to -90.0 and less than or equal to 90.0. Example: `41.890169` for the Colosseum in Rome. +pub type Latitude = f64; + +/// WGS84 longitude in decimal degrees. The value MUST be greater than or equal to -180.0 and less than or equal to 180.0. Example: `12.492269` for the Colosseum in Rome. +pub type Longitude = f64; + +// A Geometry Object as described by the IETF RFC . +//pub type MultiPolygon = geo_types::geometry::MultiPolygon; + +/// Timestamp fields MUST be represented as strings in RFC3339 format, for example 2023-07-17T13:34:13+02:00. +pub type Timestamp = String; + +/// TZ timezone from the . Timezone names never contain the space character but MAY contain an underscore. Refer to for a list of valid values. Example: `Asia/Tokyo`, `America/Los_Angeles` or `Africa/Cairo`. +pub type Timezone = String; + +/// Float (added in v2.1) - A 32-bit floating point number. +pub type Float = f64; + +/// A 32-bit floating point number greater than or equal to 0. +pub type NonNegativeFloat = f64; + +/// An integer greater than or equal to 0. +pub type NonNegativeInteger = u32; + +/// An identifier for a standard license from the [SPDX License List](https://spdx.org/licenses/). +pub type LicenseID = String; + +/// A 6 digit hexadecimal color code in the form #000000. +pub type Color = String; + +/// ISO 4217 code: (for example, CAD for Canadian dollars, EUR for euros, or JPY for Japanese yen.) +pub type Currency = String; + +/// Unique identifier of a vehicle type. +/// View [VehicleType] for more information. +pub type VehicleTypeID = ID; +/// Unique identifier of a vehicle. +/// View [Vehicle] for more information. +pub type VehicleID = ID; +/// Unique identifier of a station. +/// View [Station] for more information. +pub type StationID = ID; +/// Unique identifier of a region. +/// View [Region] for more information. +pub type RegionID = ID; +/// Unique identifier of a system. +/// View [SystemInformationData] and [System] for more information. +pub type SystemID = ID; +/// Unique identifier of a pricing plan. +/// View [SystemPricingPlan] for more information. +pub type PricingPlanID = ID; +/// Unique identifier of a system alert. +/// View [SystemAlert] for more information. +pub type AlertID = ID; + +/// Type of a GBFS feed. +/// Current values are : +/// - `gbfs` for [GbfsFile], +/// - `gbfs_versions` for [GbfsVersionsFile], +/// - `system_information` for [SystemInformationFile], +/// - `vehicle_types` for [VehicleTypesFile], +/// - `station_information` for [StationInformationFile], +/// - `station_status` for [StationStatusFile], +/// - `vehicle_status` for [VehicleStatusFile], +/// - `system_regions` for [SystemRegionsFile], +/// - `system_pricing_plans` for [SystemPricingPlansFile], +/// - `system_alerts` for [SystemAlertsFile], +/// - `geofencing_zones` for [GeofencingZonesFile] +pub type FeedType = String; + +/// Opening hours in the [OSM opening_hours](https://wiki.openstreetmap.org/wiki/Key:opening_hours). +pub type OSMOpeningHours = String; + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct LocalizedString { + /// The translated text. + pub text: String, + /// The language code. + pub language: Language, +} + +#[cfg_attr(feature = "napi", napi(object))] +#[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] +#[serde_with::skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct LocalizedUrl { + pub text: URL, + /// The language code. + pub language: Language, +} + +pub mod geo_json { + use serde::{Deserialize, Serialize}; + + #[cfg(feature = "pyo3")] + use pyo3::prelude::*; + + #[cfg(feature = "napi")] + use napi_derive::napi; + + pub mod coordinates { + pub type Point = Vec; + pub type LineString = Vec; + pub type Polygon = Vec; + pub type MultiPolygon = Vec; + } + + #[cfg_attr(feature = "napi", napi(object))] + #[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] + #[serde_with::skip_serializing_none] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct MultiPolygon { + pub r#type: String, + pub coordinates: coordinates::MultiPolygon, + } +} diff --git a/src/3.0-RC2/urls.rs b/src/3.0-RC2/urls.rs new file mode 100644 index 0000000..433aabf --- /dev/null +++ b/src/3.0-RC2/urls.rs @@ -0,0 +1,82 @@ +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "napi")] +use napi_derive::napi; + +use serde::{Deserialize, Serialize}; +use url; + +use super::files::*; + +macro_rules! file_url { + ( $ty: ident, $body: ident ) => { + #[cfg_attr(feature = "napi", napi(object))] + #[cfg_attr(feature = "pyo3", pyclass(get_all, set_all))] + #[derive(Debug, Clone)] + pub struct $ty { + pub value: String, + } + + impl $ty { + pub fn new(value: &str) -> Self { + Self { + value: value.to_string(), + } + } + + pub fn get(&self) -> url::Url { + self.value.parse().unwrap() + } + + #[cfg(feature = "reqwest_blocking")] + pub fn fetch(&self) -> Result<$body, reqwest::Error> { + reqwest::blocking::get(self.get())?.json() + } + + pub async fn fetch_async(&self) -> Result<$body, reqwest::Error> { + reqwest::get(self.get()).await?.json().await + } + } + + impl From for $ty { + fn from(value: String) -> Self { + $ty { value } + } + } + + impl<'de> Deserialize<'de> for $ty { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let val: String = Deserialize::deserialize(deserializer)?; + + val.try_into().map_err(serde::de::Error::custom) + } + } + + impl Serialize for $ty { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.value.serialize(serializer) + } + } + }; +} + +file_url!(ManifestFileUrl, ManifestFile); +file_url!(GbfsFileUrl, GbfsFile); + +file_url!(GbfsVersionsFileUrl, GbfsVersionsFile); +file_url!(GeofencingZonesFileUrl, GeofencingZonesFile); +file_url!(StationInformationFileUrl, StationInformationFile); +file_url!(StationStatusFileUrl, StationStatusFile); +file_url!(SystemAlertsFileUrl, SystemAlertsFile); +file_url!(SystemInformationFileUrl, SystemInformationFile); +file_url!(SystemPricingPlansFileUrl, SystemPricingPlansFile); +file_url!(SystemRegionsFileUrl, SystemRegionsFile); +file_url!(VehicleStatusFileUrl, VehicleStatusFile); +file_url!(VehicleTypesFileUrl, VehicleTypesFile); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..c932d16 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2 @@ +#[path = "3.0-RC2/mod.rs"] +pub mod v3_0_rc2;