From 62269f42a53f8b9337e16e3948ac44e15a09d99d Mon Sep 17 00:00:00 2001 From: OMGeeky Date: Wed, 12 Apr 2023 13:15:41 +0200 Subject: [PATCH] Pretty good working --- .gitignore | 3 + Cargo.lock | 1552 +++++++++++++++++ Cargo.toml | 16 + google_bigquery_v2_derive/.gitignore | 3 + google_bigquery_v2_derive/Cargo.toml | 16 + google_bigquery_v2_derive/src/lib.rs | 376 ++++ src/client.rs | 87 + src/data/bigquery_builder.rs | 293 ++++ src/data/bigquery_table.rs | 57 + src/data/mod.rs | 6 + .../convert_bigquery_params.rs | 107 ++ .../convert_type_to_big_query_type.rs | 42 + src/data/param_conversion/mod.rs | 42 + src/lib.rs | 4 + src/prelude.rs | 12 + src/utils/mod.rs | 1 + tests/tests.rs | 108 ++ 17 files changed, 2725 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 google_bigquery_v2_derive/.gitignore create mode 100644 google_bigquery_v2_derive/Cargo.toml create mode 100644 google_bigquery_v2_derive/src/lib.rs create mode 100644 src/client.rs create mode 100644 src/data/bigquery_builder.rs create mode 100644 src/data/bigquery_table.rs create mode 100644 src/data/mod.rs create mode 100644 src/data/param_conversion/convert_bigquery_params.rs create mode 100644 src/data/param_conversion/convert_type_to_big_query_type.rs create mode 100644 src/data/param_conversion/mod.rs create mode 100644 src/lib.rs create mode 100644 src/prelude.rs create mode 100644 src/utils/mod.rs create mode 100644 tests/tests.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..117278f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +/.idea +/auth diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..32c182b --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1552 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[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 = "anyhow" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" + +[[package]] +name = "async-trait" +version = "0.1.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.14", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[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.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "serde", + "time 0.1.45", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cxx" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.14", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.14", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding 2.2.0", +] + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.14", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "google-apis-common" +version = "5.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf0d0bf11c1a1c1c304a7379fa173659bda32fdd2f042c5af5a066f547cb2fef" +dependencies = [ + "base64 0.13.1", + "chrono", + "http", + "hyper", + "itertools", + "mime", + "serde", + "serde_json", + "serde_with", + "tokio", + "tower-service", + "url 1.7.2", + "yup-oauth2", +] + +[[package]] +name = "google-bigquery2" +version = "5.0.2+20230114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc3eb8b026c61ff94b9b1badbdcc64e1fd330c0ee8d981aa180853a3d402e1a3" +dependencies = [ + "anyhow", + "google-apis-common", + "http", + "hyper", + "hyper-rustls", + "itertools", + "mime", + "serde", + "serde_json", + "tokio", + "tower-service", + "url 1.7.2", +] + +[[package]] +name = "google_bigquery_v2" +version = "0.1.0" +dependencies = [ + "chrono", + "env_logger", + "google-bigquery2", + "google_bigquery_v2_derive", + "log", + "nameof", + "serde_json", + "tokio", +] + +[[package]] +name = "google_bigquery_v2_derive" +version = "0.0.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.14", +] + +[[package]] +name = "h2" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[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.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +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", + "serde", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi", + "io-lifetimes", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.45.0", +] + +[[package]] +name = "nameof" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce8b389a86cabeb0d8b33a61e60f3cbb4de38914342fe274e69111f3b5d9c44" + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[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.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustix" +version = "0.37.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64 0.21.0", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "scratch" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.14", +] + +[[package]] +name = "serde_json" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331bb8c3bf9b92457ab7abecf07078c13f7d270ba490103e84e8b014490cd0b0" +dependencies = [ + "base64 0.13.1", + "chrono", + "hex", + "indexmap", + "serde", + "serde_json", + "serde_with_macros", + "time 0.3.20", +] + +[[package]] +name = "serde_with_macros" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859011bddcc11f289f07f467cc1fe01c7a941daa4d8f6c40d4d1c92eb6d9319c" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +dependencies = [ + "itoa", + "libc", + "num_threads", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +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.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.45.0", +] + +[[package]] +name = "tokio-macros" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.14", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-util" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +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.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[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-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[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.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna 0.3.0", + "percent-encoding 2.2.0", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[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.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[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.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "yup-oauth2" +version = "8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1da09943413fec574fbc624011874b716e4b446f83d43f8248a5903e7ffc5e52" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.13.1", + "futures", + "http", + "hyper", + "hyper-rustls", + "itertools", + "log", + "percent-encoding 2.2.0", + "rustls", + "rustls-pemfile", + "seahash", + "serde", + "serde_json", + "time 0.3.20", + "tokio", + "tower-service", + "url 2.3.1", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7e512ba --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "google_bigquery_v2" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +google-bigquery2 = "5.0.2" +serde_json = "1.0.95" +tokio = "1.0.2" +google_bigquery_v2_derive = { version = "0.0.0", path = "./google_bigquery_v2_derive" } +chrono = "0.4.24" +log = "0.4.17" +nameof = "1.2.2" +env_logger = "0.10.0" \ No newline at end of file diff --git a/google_bigquery_v2_derive/.gitignore b/google_bigquery_v2_derive/.gitignore new file mode 100644 index 0000000..6936990 --- /dev/null +++ b/google_bigquery_v2_derive/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock diff --git a/google_bigquery_v2_derive/Cargo.toml b/google_bigquery_v2_derive/Cargo.toml new file mode 100644 index 0000000..1f84a7b --- /dev/null +++ b/google_bigquery_v2_derive/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "google_bigquery_v2_derive" +version = "0.0.0" +authors = ["OMGeeky "] +description = "A `cargo generate` template for quick-starting a procedural macro crate" +keywords = ["template", "proc_macro", "procmacro"] +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +quote = "1" +proc-macro2 = "1.0" +syn = "2.0" + diff --git a/google_bigquery_v2_derive/src/lib.rs b/google_bigquery_v2_derive/src/lib.rs new file mode 100644 index 0000000..bed46d9 --- /dev/null +++ b/google_bigquery_v2_derive/src/lib.rs @@ -0,0 +1,376 @@ +#[allow(unused)] +extern crate proc_macro; + +use proc_macro2::TokenStream; +use syn::DeriveInput; + +struct Field { + field_ident: proc_macro2::Ident, + db_name: std::string::String, + local_name: std::string::String, + ty: syn::Type, + required: bool, +} + +struct Attribute { + name: std::string::String, + value: std::string::String, +} + +//region Derive macro 'BigDataTableDerive' + +#[proc_macro_derive(BigDataTableDerive, attributes(db_name, required, client, primary_key))] +pub fn big_query_table_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let ast = syn::parse(input).unwrap(); + let tokens = impl_big_query_table_derive(&ast); + tokens.into() +} + +fn impl_big_query_table_derive(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { + let pk_field = get_pk_field(&ast); + let client_field = get_client_field(&ast); + implement_big_query_table_base(&ast, &pk_field, &client_field) +} + +fn implement_big_query_table_base( + ast: &DeriveInput, + pk_field: &Field, + client_field: &Field, +) -> proc_macro2::TokenStream { + let table_ident = &ast.ident; + let table_name = get_table_name(&ast); + let impl_get_client = implement_get_client(&client_field); + let impl_set_client = implement_set_client(&client_field); + let impl_get_pk_field_name = implement_get_pk_field_name(&pk_field); + let impl_get_pk_db_name = implement_get_pk_db_name(&pk_field); + let impl_get_pk_value = implement_get_pk_value(&pk_field); + let impl_get_query_fields = implement_get_query_fields(&ast); + let impl_get_table_name = implement_impl_get_table_name(&table_name); + let impl_set_field_value = implement_set_field_value(&ast); + let impl_from__query_result_row = implement_from__query_result_row(&ast); + quote::quote! { + impl<'a> BigQueryTableBase<'a> for #table_ident<'a> { + #impl_get_client + #impl_set_client + #impl_get_pk_field_name + #impl_get_pk_db_name + #impl_get_pk_value + #impl_get_query_fields + #impl_get_table_name + #impl_set_field_value + #impl_from__query_result_row + } + } +} + + +//region method implementations + +fn implement_get_client(client_field: &Field) -> TokenStream { + let client_ident = client_field.field_ident.clone(); + quote::quote! { + fn get_client(&self) -> &'a BigqueryClient { + log::trace!("get_client() self={:?}", self); + &self.#client_ident + } + } +} + +fn implement_set_client(client_field: &Field) -> TokenStream { + let client_ident = client_field.field_ident.clone(); + quote::quote! { + fn set_client(&mut self, client: &'a BigqueryClient) { + log::trace!("set_client() self={:?}", self); + self.#client_ident = client; + } + } +} + +fn implement_get_pk_field_name(pk_field: &Field) -> TokenStream { + let pk_local_name = pk_field.local_name.clone(); + quote::quote! { + fn get_pk_field_name() -> String { + log::trace!("get_pk_field_name()"); + String::from(#pk_local_name) + } + } +} + +fn implement_get_pk_db_name(pk_field: &Field) -> TokenStream { + let pk_db_name = pk_field.db_name.clone(); + quote::quote! { + fn get_pk_db_name() -> String { + log::trace!("get_pk_db_name()"); + String::from(#pk_db_name) + } + } +} + +fn implement_get_pk_value(pk_field: &Field) -> TokenStream { + let pk_ident = &pk_field.field_ident; + quote::quote! { + fn get_pk_value(&self) -> &dyn google_bigquery_v2::data::param_conversion::BigDataValueType { + log::trace!("get_pk_value() self={:?}", self); + &self.#pk_ident + } + } +} + +fn implement_get_query_fields(ast: &DeriveInput) -> TokenStream { + fn implement_map_insert(f: Field) -> TokenStream { + let local_name = f.local_name; + let db_name = f.db_name; + quote::quote! { + map.insert(String::from(#local_name),String::from(#db_name)); + } + } + let fields = get_fields(&ast.data); + let pk_field = get_pk_field(&ast); + let client_ident = get_client_field(&ast).field_ident; + let fields: Vec = fields + .into_iter() + .filter(|f| { + f.field_ident != client_ident && f.field_ident != pk_field.field_ident + }) + .map(implement_map_insert) + .collect(); + + let pk_insert = implement_map_insert(pk_field); + + quote::quote! { + fn get_query_fields(include_pk: bool) -> std::collections::HashMap { + log::trace!("get_query_fields() include_pk={}", include_pk); + let mut map = std::collections::HashMap::new(); + if(include_pk) { + #pk_insert + } + #(#fields)* + map + } + } +} + +fn implement_impl_get_table_name(table_name: &String) -> TokenStream { + quote::quote! { + fn get_table_name() -> String { + log::trace!("get_table_name()"); + String::from(#table_name) + } + } +} + +fn implement_set_field_value(ast: &DeriveInput) -> TokenStream { + fn write_set_field_value(f: Field) -> TokenStream { + let field_ident = f.field_ident; + let local_name = f.local_name; + let field_type = f.ty; + quote::quote! { + #local_name => self.#field_ident = #field_type::from_param(value)?, + } + } + let client_ident = get_client_field(&ast).field_ident; + let fields = get_fields(&ast.data); + let fields: Vec = fields + .into_iter() + .filter(|f| { + f.field_ident != client_ident + }) + .map(write_set_field_value) + .collect(); + + quote::quote! { + fn set_field_value(&mut self, field_name: &str, value: &serde_json::Value) -> Result<()>{ + log::trace!("set_field_value() self={:?} field_name={} value={:?}", self, field_name, value); + use google_bigquery_v2::data::param_conversion::ConvertBigQueryParams; + match field_name { + #(#fields)* + _ => return Err(google_bigquery_v2::data::param_conversion::ConversionError::new(format!("Field '{}' not found", field_name)).into()) + } + Ok(()) + } + } +} + +fn implement_from__query_result_row(ast: &DeriveInput) -> TokenStream { + fn set_field_value(f: Field) -> TokenStream { + let field_ident = f.field_ident; + let local_name = f.local_name; + let field_type = f.ty; + let db_name = f.db_name; + quote::quote! { + #field_ident: #field_type::from_param(&row[#db_name])?, + } + } + let client_ident = get_client_field(&ast).field_ident; + let fields = get_fields(&ast.data); + let fields: Vec = fields + .into_iter() + .filter(|f| { + f.field_ident != client_ident + }) + .map(set_field_value) + .collect(); + quote::quote! { + fn new_from_query_result_row( + client: &'a BigqueryClient, + row: &std::collections::HashMap, + ) -> Result + where Self: Sized { + log::trace!("new_from_query_result_row() client={:?} row={:?}", client, row); + use google_bigquery_v2::data::param_conversion::ConvertBigQueryParams; + let result = Self{ + #client_ident: client, + #(#fields)* + }; + Ok(result) + } + } +} +//endregion + +//endregion + +//region Helper functions + +fn get_table_name(ast: &DeriveInput) -> String { + for attr in get_struct_attributes(ast) { + if attr.name.eq("db_name") { + let tokens = &attr.value; + return tokens.to_string(); + } + } + ast.ident.to_string() +} + +fn get_pk_field(ast: &syn::DeriveInput) -> Field { + let mut pk_fields = get_fields_with_attribute(&ast.data, "primary_key"); + if pk_fields.len() != 1 { + panic!("Exactly one primary key field must be specified"); + } + let pk = pk_fields.remove(0); + pk +} + +fn get_client_field(ast: &syn::DeriveInput) -> Field { + //region client + let mut client_fields = get_fields_with_attribute(&ast.data, "client"); + if client_fields.len() != 1 { + panic!("Exactly one client field must be specified"); + } + let client = client_fields.remove(0); + //endregion + client +} + +fn get_struct_attributes(ast: &syn::DeriveInput) -> Vec { + let attrs = &ast.attrs; + let mut res = vec![]; + for attr in attrs { + if attr.path().is_ident("db_name") { + let args: syn::LitStr = attr.parse_args().expect("Failed to parse target name"); + let args = args.value(); + res.push(Attribute { + name: "db_name".to_string(), + value: args, + }); + } + } + res +} + +fn get_fields(data: &syn::Data) -> Vec { + let mut res = vec![]; + + match data { + syn::Data::Struct(ref data_struct) => match data_struct.fields { + syn::Fields::Named(ref fields_named) => { + for field in fields_named.named.iter() { + if let Some(parsed_field) = parse_local_field(&field, false) { + res.push(parsed_field); + } + } + } + _ => (), + }, + _ => panic!("Must be a struct!"), + }; + + return res; +} + +fn parse_local_field(field: &syn::Field, include_ignored: bool) -> Option { + match &field.ident { + Some(ident) => { + let mut name = None; + let mut required = false; + let attrs = &field.attrs; + for attribute in attrs { + if attribute.path().is_ident("db_ignore") && !include_ignored { + return None; //skip this field completely + } + if attribute.path().is_ident("db_name") { + let args: syn::LitStr = + attribute.parse_args().expect("Failed to parse target name"); + let args = args.value(); + name = Some(args); + } + if attribute.path().is_ident("required") { + required = true; + } + } + + let local_name = ident.to_string(); + let name = match name { + None => local_name.clone(), + Some(n) => n, + }; + let parsed_field = Field { + field_ident: ident.clone(), + local_name, + db_name: name, + ty: field.ty.clone(), + required, + }; + return Some(parsed_field); + } + _ => None, + } +} + +fn get_fields_with_attribute(data: &syn::Data, attribute_name: &str) -> Vec { + let mut res = vec![]; + match data { + // Only process structs + syn::Data::Struct(ref data_struct) => { + // Check the kind of fields the struct contains + match data_struct.fields { + // Structs with named fields + syn::Fields::Named(ref fields_named) => { + // Iterate over the fields + for field in fields_named.named.iter() { + if let Some(_) = &field.ident { + // Get attributes #[..] on each field + for attr in field.attrs.iter() { + // Parse the attribute + if attr.path().is_ident(attribute_name) { + let parsed_field = parse_local_field(&field, true).unwrap(); + res.push(parsed_field); + } + } + } + } + } + + // Struct with unnamed fields + _ => (), + } + } + + // Panic when we don't have a struct + _ => panic!("Must be a struct"), + } + + return res; +} + +//endregion diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..96c2e42 --- /dev/null +++ b/src/client.rs @@ -0,0 +1,87 @@ +use std::error::Error; +use std::fmt::Debug; + +use google_bigquery2::hyper::client::HttpConnector; +use google_bigquery2::hyper_rustls::HttpsConnector; +use google_bigquery2::Bigquery; +use google_bigquery2::{hyper, hyper_rustls, oauth2}; + + +pub struct BigqueryClient { + client: Bigquery>, + project_id: String, + dataset_id: String, +} + +impl BigqueryClient { + pub fn empty() -> BigqueryClient { + todo!() + } +} + +impl BigqueryClient { + pub async fn new>( + project_id: S, + dataset_id: S, + service_account_path: Option, + ) -> Result> { + let client = get_internal_client(service_account_path).await?; + Ok(BigqueryClient { + client, + project_id: project_id.into(), + dataset_id: dataset_id.into(), + }) + } + + pub fn get_client(&self) -> &Bigquery> { + &self.client + } + pub fn get_project_id(&self) -> &str { + &self.project_id + } + pub fn get_dataset_id(&self) -> &str { + &self.dataset_id + } +} + +impl Debug for BigqueryClient { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("BigqueryClient") + .field("project_id", &self.project_id) + .field("dataset_id", &self.dataset_id) + .finish() + } +} + +async fn get_internal_client>( + service_account_path: Option, +) -> Result>, Box> { + let hyper_client = hyper::Client::builder().build( + hyper_rustls::HttpsConnectorBuilder::new() + .with_native_roots() + .https_or_http() + .enable_http1() + .enable_http2() + .build(), + ); + let service_account_path = match service_account_path { + None => "auth/service_account2.json".to_string(), + Some(s) => s.into(), + }; + let secret = oauth2::read_service_account_key(&service_account_path) + .await + .expect( + format!( + "Failed to read service account key from file. {}", + service_account_path + ) + .as_str(), + ); + let auth = oauth2::ServiceAccountAuthenticator::builder(secret) + .build() + .await + .expect("Failed to authenticate with service account key."); + let client: Bigquery> = Bigquery::new(hyper_client, auth); + + Ok(client) +} diff --git a/src/data/bigquery_builder.rs b/src/data/bigquery_builder.rs new file mode 100644 index 0000000..54840f4 --- /dev/null +++ b/src/data/bigquery_builder.rs @@ -0,0 +1,293 @@ +use std::collections::HashMap; +use std::fmt::{Debug, Display, Formatter}; +use std::marker::PhantomData; + +use google_bigquery2::api::{ + QueryParameter, QueryParameterType, QueryParameterValue, QueryRequest, +}; +use log::{debug, log, trace}; +use serde_json::Value; + +use crate::client::BigqueryClient; +use crate::data::BigQueryTable; +use crate::data::param_conversion::BigDataValueType; +use crate::prelude::*; + +//region OrderDirection + +#[derive(Debug, Clone)] +pub enum OrderDirection { + Ascending, + Descending, +} + +impl OrderDirection { + pub(crate) fn to_query_str(&self) -> String { + match self { + OrderDirection::Ascending => String::from("ASC"), + OrderDirection::Descending => String::from("DESC"), + } + } +} + +//endregion + +//region BigQueryBuilder + +#[derive(Debug, Clone)] +pub struct BigQueryBuilder<'a, Table> { + client: Option<&'a BigqueryClient>, + required_params: Vec, + selected_fields: Option>, + wheres: Vec, + limit: Option, + order_bys: Vec<(String, OrderDirection)>, + + _table_type_marker: PhantomData>, +} + +impl<'a, Table> BigQueryBuilder<'a, Table> + where + Table: BigQueryTable<'a>, +{ + //region build methods + + pub async fn run(self) -> Result> { + trace!("BigQueryBuilder::run()"); + //TODO: maybe return an iterator instead of a vector. + // this would allow for lazy loading of the data. + // it would also make it possible that additional + // data is loaded (if the set limit is higher than + // the number of rows returned) + let client = self.client.unwrap(); + let fields = self.get_sorted_selected_fields(); + let req = self.build_query_request(); + + debug!("req: {:?}", req); + let (res, query_res) = client + .get_client() + .jobs() + .query(req, client.get_project_id()) + .doit() + .await?; + + if res.status() != 200 { + return Err(format!("Wrong status code returned! ({})", res.status()).into()); + } + + let query_res = query_res.rows.unwrap(); + println!("query_res: {:?}", query_res); + let mut result: Vec = Vec::new(); + for row in query_res { + let row = row.f.unwrap(); + let mut row_data: HashMap = HashMap::new(); + for (i, field) in row.into_iter().enumerate() { + let field = field.v.unwrap_or(Value::Null); + println!("{}: {}", fields[i], field); + row_data.insert(fields[i].clone(), field); + } + let data = Table::new_from_query_result_row(client, &row_data)?; + result.push(data); + } + + return Ok(result); + } + + pub fn build_query_request(self) -> QueryRequest { + QueryRequest { + query: Some(self.build_query_string()), + query_parameters: Some(self.required_params), + use_legacy_sql: Some(false), + /*TODO: is this line needed?: use_legacy_sql: Some(false),*/ + ..Default::default() + } + } + pub fn build_query_string(&self) -> String { + let where_clause = if self.wheres.is_empty() { + String::new() + } else { + format!("WHERE {}", self.wheres.join(" AND ")) + }; + let order_by_clause = if self.order_bys.is_empty() { + String::new() + } else { + format!("ORDER BY {}", self.order_bys + .iter() + .map(|(key, dir)| format!("{} {}", key, dir.to_query_str())) + .collect::>() + .join(", ")) + }; + format!( + "SELECT {} FROM {} {} {} LIMIT {}", + self.get_sorted_selected_fields() + .join(", "), + Table::get_table_identifier_from_client(self.client.unwrap()), + where_clause, + order_by_clause, + self.limit.unwrap_or(1000) + ) + } + + //endregion + + + //region add content + + fn set_select_fields(self, fields: Vec) -> Result { + //TODO: this method probably does not work since the logic does + // not work if (at least the required) fields are not selected + // since the parser will not be able to create the struct instance. + let selected_fields = self.selected_fields; + let mut selected_fields = match selected_fields { + Some(selected_fields) => selected_fields, + None => Vec::new(), + }; + + for field in fields { + let field_name = Table::get_field_db_name(&field) + .map_err(|e| format!("Error while selecting field '{}': {}", field, e))?; + selected_fields.push(field_name); + } + + Ok(Self { + selected_fields: Some(selected_fields), + ..self + }) + } + + pub fn add_where_eq(self, column: &str, value: Option<&T>) -> Result + where + T: BigDataValueType + Debug, + { + let column = Table::get_field_db_name(column)?; + let mut wheres = self.wheres; + + if let Some(value) = value { + let param_name = format!("__PARAM_{}", self.required_params.len()); + + let param = get_parameter(value, ¶m_name); + + let mut required_params = self.required_params; + required_params.push(param); + + wheres.push(format!("{} = @{}", column, param_name)); + + return Ok(Self { + wheres, + required_params, + ..self + }); + } + + wheres.push(format!("{} is NULL", column)); + Ok(Self { wheres, ..self }) + } + + pub fn add_order_by(self, column: &str, direction: OrderDirection) -> Self { + let column = Table::get_field_db_name(column).unwrap(); + let mut order_bys = self.order_bys; + order_bys.push((column.to_string(), direction)); + Self { order_bys, ..self } + } + //endregion + + fn get_sorted_selected_fields(&self) -> Vec { + trace!("get_sorted_selected_fields()"); + let mut fields: Vec = match &self.selected_fields { + Some(fields) => fields.clone(), + None => { + Table::get_query_fields(true) + .into_iter() + .map(|f| f.1)//get the db name + .collect() + } + }; + println!("fields: {:?}", fields); + fields.sort(); + fields + } +} +//region implement some convenience traits for BigQueryBuilder + +impl<'a, Table> Default for BigQueryBuilder<'a, Table> { + fn default() -> Self { + Self { + client: None, + required_params: vec![], + selected_fields: None, + wheres: vec![], + limit: None, + order_bys: vec![], + _table_type_marker: PhantomData, + } + } +} + +impl<'a, Table: BigQueryTable<'a>> Display for BigQueryBuilder<'a, Table> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!( + "BigQueryBuilder: {}\t\ + wheres: 'where {}'\t\ + order by's: 'order by {}'\t\ + limit: {:?}\t\ + params: {:?}", + Table::get_table_name(), + self.wheres.join(" AND "), + self.order_bys + .iter() + .map(|(key, dir)| format!("{} {}", key, dir.to_query_str())) + .collect::>() + .join(", "), + self.limit, + self.required_params + )) + } +} + +//endregion + +//endregion + +fn get_parameter(value: &T, param_name: &String) -> QueryParameter + where + T: BigDataValueType + Debug, +{ + let param_value = serde_json::from_value(value.to_param()).unwrap(); + let param_value = QueryParameterValue { + value: Some(param_value), + ..Default::default() + }; + + let param_type = T::convert_type_to_bigquery_type(); + let param_type = QueryParameterType { + type_: Some(param_type), + ..Default::default() + }; + + let param = QueryParameter { + parameter_type: Some(param_type), + parameter_value: Some(param_value), + name: Some(param_name.clone()), + }; + param +} + +//region BigQueryBuilderAvailable + +pub trait BigQueryBuilderAvailable<'a, Table> { + fn query(client: &'a BigqueryClient) -> BigQueryBuilder<'a, Table>; +} + +impl<'a, Table> BigQueryBuilderAvailable<'a, Table> for Table + where + Table: BigQueryTable<'a>, +{ + fn query(client: &'a BigqueryClient) -> BigQueryBuilder<'a, Table> { + BigQueryBuilder { + client: Some(client), + ..Default::default() + } + } +} + +//endregion diff --git a/src/data/bigquery_table.rs b/src/data/bigquery_table.rs new file mode 100644 index 0000000..55e23ed --- /dev/null +++ b/src/data/bigquery_table.rs @@ -0,0 +1,57 @@ +use std::collections::HashMap; + +use log::trace; +use serde_json::Value; + +use crate::client::BigqueryClient; +use crate::data::param_conversion::{BigDataValueType, ConversionError}; +use crate::data::param_conversion::ConvertBigQueryParams; +use crate::prelude::*; + + +pub trait BigQueryTableBase<'a> { + fn get_table_name() -> String; + fn get_client(&self) -> &'a BigqueryClient; + fn set_client(&mut self, client: &'a BigqueryClient); + fn get_pk_field_name() -> String; + fn get_pk_db_name() -> String; + fn get_pk_value(&self) -> &dyn BigDataValueType; + fn get_query_fields(include_pk: bool) -> HashMap; + fn set_field_value(&mut self, field_name: &str, value: &Value) -> Result<()>; + + fn new_from_query_result_row( + client: &'a BigqueryClient, + row: &HashMap, + ) -> Result + where + Self: Sized; +} + +pub trait BigQueryTable<'a>: BigQueryTableBase<'a> { + fn get_field_db_name(field_name: &str) -> Result { + trace!("get_field_db_name({})", field_name); + let query_fields = Self::get_query_fields(true); + let db_name = query_fields.get(field_name); + match db_name { + None => Err(format!("Field {} not found.", field_name).into()), + Some(s) => Ok(s.to_string()), + } + } + + fn get_table_identifier(&self) -> String { + trace!("get_table_identifier()"); + Self::get_table_identifier_from_client(self.get_client()) + } + + fn get_table_identifier_from_client(client: &'a BigqueryClient) -> String { + trace!("get_table_identifier_from_client({:?})", client); + format!( + "`{}.{}.{}`", + client.get_project_id(), + client.get_dataset_id(), + Self::get_table_name() + ) + } +} + +impl<'a, T> BigQueryTable<'a> for T where T: BigQueryTableBase<'a> {} diff --git a/src/data/mod.rs b/src/data/mod.rs new file mode 100644 index 0000000..7c7a96b --- /dev/null +++ b/src/data/mod.rs @@ -0,0 +1,6 @@ +pub use bigquery_table::*; +// pub use bigquery_builder::*; + +mod bigquery_table; +pub mod bigquery_builder; +pub mod param_conversion; \ No newline at end of file diff --git a/src/data/param_conversion/convert_bigquery_params.rs b/src/data/param_conversion/convert_bigquery_params.rs new file mode 100644 index 0000000..054f3c3 --- /dev/null +++ b/src/data/param_conversion/convert_bigquery_params.rs @@ -0,0 +1,107 @@ +use std::fmt::Debug; + +use chrono::{NaiveDateTime, Utc}; +use log::trace; +use serde_json::Value; + +use crate::prelude::*; + +pub trait ConvertBigQueryParams { + fn from_param(value: &Value) -> Result where Self: Sized; + fn to_param(&self) -> Value; +} + +impl ConvertBigQueryParams for i64 { + fn from_param(value: &Value) -> Result { + let string:String = serde_json::from_value(value.clone())?; + Ok(string.parse()?) + } + fn to_param(&self) -> Value { + serde_json::to_value(self).unwrap() + } +} + +impl ConvertBigQueryParams for i32 { + fn from_param(value: &Value) -> Result { + let string:String = serde_json::from_value(value.clone())?; + Ok(string.parse()?) + } + fn to_param(&self) -> Value { + serde_json::to_value(self).unwrap() + } +} + +impl ConvertBigQueryParams for bool { + fn from_param(value: &Value) -> Result { + let value: String = serde_json::from_value(value.clone())?; + match value.as_str() { + "TRUE" => Ok(true), + "true" => Ok(true), + "FALSE" => Ok(false), + "false" => Ok(false), + invalid => Err(format!("Invalid value for bool: '{}'", invalid).into()), + } + } + fn to_param(&self) -> Value { + match self { + true => serde_json::to_value("TRUE").unwrap(), + false => serde_json::to_value("FALSE").unwrap(), + } + } +} + +impl ConvertBigQueryParams for String { + fn from_param(value: &Value) -> Result { + let string:String = serde_json::from_value(value.clone())?; + Ok(string.parse()?) + } + fn to_param(&self) -> Value { + serde_json::to_value(self).unwrap() + } +} + +impl ConvertBigQueryParams for f64 { + fn from_param(value: &Value) -> Result { + Ok(serde_json::from_value(value.clone())?) + } + fn to_param(&self) -> Value { + serde_json::to_value(self).unwrap() + } +} + +impl ConvertBigQueryParams for chrono::DateTime { + fn from_param(value: &Value) -> Result { + trace!("ConvertValueToBigqueryParamValue::from_param DateTime -> in: {:?}", value); + let value: String = serde_json::from_value(value.clone())?; + let value = value.replace("T", " ").replace("Z", ""); + let value = NaiveDateTime::parse_from_str(&value, "%Y-%m-%d %H:%M:%S")?; + let time = chrono::DateTime::::from_utc(value, Utc); + trace!("ConvertValueToBigqueryParamValue::from_param DateTime -> out: {:?}", time); + Ok(time) + } + fn to_param(&self) -> Value { + trace!("ConvertValueToBigqueryParamValue::to_param DateTime -> in: {:?}", self); + let value: String = self.to_rfc3339_opts(chrono::SecondsFormat::Secs, true); + let value: String = value.replace("Z", "").replace("T", " "); + trace!("ConvertValueToBigqueryParamValue::to_param DateTime -> out: {:?}", value); + serde_json::to_value(value).unwrap() + } +} + +impl ConvertBigQueryParams for Option { + fn from_param(value: &Value) -> Result where Self: Sized { + trace!("ConvertValueToBigqueryParamValue::from_param Option: {:?}", value); + match value { + Value::Null => Ok(None), + _ => Ok(Some(T::from_param(value)?)), + } + } + + fn to_param(&self) -> Value { + trace!("ConvertValueToBigqueryParamValue::to_param Option: {:?}", self); + match self { + Some(value) => value.to_param(), + None => Value::Null, + } + } +} diff --git a/src/data/param_conversion/convert_type_to_big_query_type.rs b/src/data/param_conversion/convert_type_to_big_query_type.rs new file mode 100644 index 0000000..bc7f4d4 --- /dev/null +++ b/src/data/param_conversion/convert_type_to_big_query_type.rs @@ -0,0 +1,42 @@ +use std::fmt::Display; + +pub trait ConvertTypeToBigQueryType { + fn convert_type_to_bigquery_type() -> String where Self: Sized; +} + +impl ConvertTypeToBigQueryType for bool { + fn convert_type_to_bigquery_type() -> String { + "BOOL".to_string() + } +} + +impl ConvertTypeToBigQueryType for i32 { + fn convert_type_to_bigquery_type() -> String { + "INT64".to_string() + } +} + +impl ConvertTypeToBigQueryType for i64 { + fn convert_type_to_bigquery_type() -> String { + "INT64".to_string() + } +} + +impl ConvertTypeToBigQueryType for String { + fn convert_type_to_bigquery_type() -> String { + "STRING".to_string() + } +} + +impl ConvertTypeToBigQueryType for &str { + fn convert_type_to_bigquery_type() -> String { + "STRING".to_string() + } +} + +impl ConvertTypeToBigQueryType for chrono::DateTime + where T: chrono::TimeZone + Display + Send + Sync + 'static { + fn convert_type_to_bigquery_type() -> String { + "DATETIME".to_string() + } +} diff --git a/src/data/param_conversion/mod.rs b/src/data/param_conversion/mod.rs new file mode 100644 index 0000000..6511999 --- /dev/null +++ b/src/data/param_conversion/mod.rs @@ -0,0 +1,42 @@ +use std::error::Error; +use std::fmt::{Debug, Display, Formatter}; + +pub use convert_bigquery_params::ConvertBigQueryParams; +pub use convert_type_to_big_query_type::ConvertTypeToBigQueryType; + +mod convert_bigquery_params; +mod convert_type_to_big_query_type; + +pub trait BigDataValueType: ConvertTypeToBigQueryType + ConvertBigQueryParams + Debug {} + +impl BigDataValueType for T {} + +//region ConversionError +#[derive(Debug)] +pub struct ConversionError { + pub message: String, +} + +impl Display for ConversionError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("{}", self.message)) + } +} + +impl Error for ConversionError {} + +impl From<&str> for ConversionError { + fn from(message: &str) -> Self { + ConversionError::new(message) + } +} + +impl ConversionError { + pub fn new(message: impl Into) -> Self { + ConversionError { + message: message.into(), + } + } +} + +//endregion \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..0e055f3 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,4 @@ +pub mod client; +pub mod data; +pub mod utils; +pub mod prelude; diff --git a/src/prelude.rs b/src/prelude.rs new file mode 100644 index 0000000..e582d97 --- /dev/null +++ b/src/prelude.rs @@ -0,0 +1,12 @@ +pub use google_bigquery_v2_derive::BigDataTableDerive; + +pub use crate::client::BigqueryClient; +pub use crate::data::{bigquery_builder::{ + BigQueryBuilder, + BigQueryBuilderAvailable, + OrderDirection, +}, + BigQueryTable, + BigQueryTableBase}; + +pub type Result = std::result::Result>; diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1 @@ + diff --git a/tests/tests.rs b/tests/tests.rs new file mode 100644 index 0000000..6d3fc1b --- /dev/null +++ b/tests/tests.rs @@ -0,0 +1,108 @@ +use log::{debug, info, LevelFilter}; +use nameof::name_of; + +use google_bigquery_v2::prelude::*; + +#[derive(BigDataTableDerive, Debug, Clone)] +#[db_name("Infos")] +pub struct DbInfos<'a> { + #[client] + client: &'a BigqueryClient, + #[primary_key] + #[db_name("Id")] + row_id: i64, + info1: Option::, + #[db_name("info")] + info2: Option::, + info3: Option::, + info4i: Option::, + #[db_name("yes")] + info4b: Option::, +} + +#[tokio::test] +async fn test_get_table_name() { + init_logger(LevelFilter::Debug); + let pk = DbInfos::get_table_name(); + println!("table name: {}", pk); + assert_eq!("Infos", pk, "table name is not correct") +} + +#[tokio::test] +async fn test_get_query_fields() { + init_logger(LevelFilter::Debug); + let fields = DbInfos::get_query_fields(true); + println!("fields: {:?}", fields); + assert_eq!(6, fields.len(), "fields length is not correct"); + assert_eq!("Id", fields.get("row_id").unwrap(), ); + assert_eq!("info1", fields.get("info1").unwrap(), ); + assert_eq!("info", fields.get("info2").unwrap()); + assert_eq!("info3", fields.get("info3").unwrap()); + assert_eq!("info4i", fields.get("info4i").unwrap()); + assert_eq!("yes", fields.get("info4b").unwrap()); +} + +#[tokio::test] +async fn test_query_builder_1() { + init_logger(LevelFilter::Debug); + let client = get_test_client().await; + let query_builder: BigQueryBuilder = DbInfos::query(&client); + let query_builder: BigQueryBuilder = query_builder + .add_where_eq::(name_of!(info1 in DbInfos), None) + .unwrap() + .add_where_eq(name_of!(info3 in DbInfos), Some(&"cc".to_string())) + .unwrap() + .add_order_by(name_of!(info2 in DbInfos), OrderDirection::Ascending); + let query_string = query_builder.clone().build_query_string(); + let expected_query_string = String::from( + "SELECT Id, info, info1, info3, info4i, yes \ + FROM `testrustproject-372221.test1.Infos` \ + WHERE info1 is NULL AND info3 = @__PARAM_0 \ + ORDER BY info ASC LIMIT 1000", + ); + println!("query : {}", query_string); + println!("expected: {}", expected_query_string); + println!("request: {:?}", query_builder.clone().build_query_request()); + + assert_eq!(query_string, expected_query_string); + assert_eq!( + query_builder + .clone() + .build_query_request() + .query_parameters + .unwrap() + .len(), + 1 + ); + let res = query_builder.clone().run().await.unwrap(); + println!("res: {:?}", res); +} + +async fn get_test_client() -> BigqueryClient { + BigqueryClient::new("testrustproject-372221", "test1", None) + .await + .unwrap() +} + +#[tokio::test] +async fn simple_query() { + init_logger(LevelFilter::Info); + let client = get_test_client().await; + let q = DbInfos::query(&client) + .add_order_by(name_of!(row_id in DbInfos), OrderDirection::Descending) + .run().await.unwrap(); + let mut last_num = 999999999999999999; + for line in q { + info!("line: {:?}", line); + debug!("row_id > last: {} <= {}",line.row_id, last_num); + assert!(line.row_id <= last_num); + last_num = line.row_id; + } +} + +fn init_logger(level: LevelFilter) { + let _ = env_logger::builder() + .is_test(true) + .filter_level(level) + .try_init(); +}