diff --git a/.hooks/commit-msg b/.hooks/commit-msg new file mode 100755 index 0000000..284ee8d --- /dev/null +++ b/.hooks/commit-msg @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +# This hook checks if the commit message ends with an issue number, and if not, +# tries to derive that number from the branch name + +branch=`git rev-parse --abbrev-ref HEAD` + +# This check doesn't need to run when commiting to develop/master +[[ "$branch" =~ ^master|develop$ ]] && exit 0 + +issue_num=`echo "$branch" | grep -Po '^[0-9]+(?=-)'` + +# Check if issue number is already present +if ! grep -q '([0-9]\+)$' "$1"; then + # Error out if we can't derive issue number + [[ -z "$issue_num" ]] && { + >&2 echo "Couldn't derive issue number from branch. Please add one manually."; + exit 1; + } + + # Append issue number, and remove all comments + echo "[#$issue_num]" "$(cat "$1")" > "$1" +fi diff --git a/.hooks/pre-commit b/.hooks/pre-commit new file mode 100755 index 0000000..a2dd254 --- /dev/null +++ b/.hooks/pre-commit @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +# This hook lints the code, and if we're on develop or master, also forces the tests to pass. +cargo fmt -- --check + +branch=`git rev-parse --abbrev-ref HEAD` + +# TODO should we add release branches here as well? +if [[ "$branch" =~ ^master|develop$ ]]; then + make test > /dev/null 2>&1 || { + >&2 echo "Tests failed. check 'make test' for more info."; + exit 1; + } +fi diff --git a/Cargo.lock b/Cargo.lock index d7b6fcf..9c0d5ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,6 +56,15 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + [[package]] name = "atty" version = "0.2.14" @@ -124,9 +133,9 @@ checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" [[package]] name = "byteorder" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" @@ -152,6 +161,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time 0.1.44", + "winapi 0.3.9", +] + [[package]] name = "cipher" version = "0.2.5" @@ -180,7 +202,7 @@ dependencies = [ "percent-encoding 2.1.0", "rand", "sha2", - "time 0.1.43", + "time 0.1.44", ] [[package]] @@ -190,8 +212,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" dependencies = [ "percent-encoding 2.1.0", - "time 0.2.25", - "version_check 0.9.2", + "time 0.2.26", + "version_check 0.9.3", ] [[package]] @@ -206,7 +228,7 @@ dependencies = [ "publicsuffix", "serde", "serde_json", - "time 0.2.25", + "time 0.2.26", "url 2.2.1", ] @@ -317,6 +339,8 @@ dependencies = [ name = "fej" version = "0.0.1" dependencies = [ + "chrono", + "regex", "reqwest", "rocket", "rocket_contrib", @@ -456,7 +480,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" dependencies = [ "typenum", - "version_check 0.9.2", + "version_check 0.9.3", ] [[package]] @@ -553,12 +577,13 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2861bd27ee074e5ee891e8b539837a9430012e249d7f0ca2d795650f579c1994" +checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737" dependencies = [ "bytes", "http", + "pin-project-lite", ] [[package]] @@ -585,7 +610,7 @@ dependencies = [ "log 0.3.9", "mime 0.2.6", "num_cpus", - "time 0.1.43", + "time 0.1.44", "traitobject", "typeable", "unicase", @@ -653,9 +678,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" +checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" dependencies = [ "autocfg", "hashbrown", @@ -704,9 +729,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "js-sys" -version = "0.3.48" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc9f84f9b115ce7843d60706df1422a916680bfdfcbdb0447c5614ff9d7e4d78" +checksum = "dc15e39392125075f60c95ba416f5381ff6c3a948ff02ab12464715adf56c821" dependencies = [ "wasm-bindgen", ] @@ -741,9 +766,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.88" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a" +checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" [[package]] name = "log" @@ -811,13 +836,13 @@ dependencies = [ [[package]] name = "mio" -version = "0.7.9" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5dede4e2065b3842b8b0af444119f3aa331cc7cc2dd20388bfb0f5d5a38823a" +checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" dependencies = [ "libc", "log 0.4.14", - "miow 0.3.6", + "miow 0.3.7", "ntapi", "winapi 0.3.9", ] @@ -848,11 +873,10 @@ dependencies = [ [[package]] name = "miow" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" dependencies = [ - "socket2", "winapi 0.3.9", ] @@ -912,6 +936,25 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -922,6 +965,12 @@ dependencies = [ "libc", ] +[[package]] +name = "once_cell" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -930,15 +979,15 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.32" +version = "0.10.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70" +checksum = "a61075b62a23fef5a29815de7536d940aa35ce96d18ce0cc5076272db678a577" dependencies = [ "bitflags", "cfg-if 1.0.0", "foreign-types", - "lazy_static", "libc", + "once_cell", "openssl-sys", ] @@ -950,9 +999,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.60" +version = "0.9.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6" +checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f" dependencies = [ "autocfg", "cc", @@ -979,7 +1028,7 @@ dependencies = [ "proc-macro2 0.4.30", "quote 0.6.13", "syn 0.15.44", - "version_check 0.9.2", + "version_check 0.9.3", "yansi", ] @@ -1012,7 +1061,7 @@ checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.63", + "syn 1.0.64", ] [[package]] @@ -1151,6 +1200,23 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" + [[package]] name = "remove_dir_all" version = "0.5.3" @@ -1188,7 +1254,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "time 0.2.25", + "time 0.2.26", "tokio", "tokio-native-tls", "url 2.2.1", @@ -1213,9 +1279,9 @@ dependencies = [ "rocket_codegen", "rocket_http", "state", - "time 0.1.43", + "time 0.1.44", "toml", - "version_check 0.9.2", + "version_check 0.9.3", "yansi", ] @@ -1230,7 +1296,7 @@ dependencies = [ "indexmap", "quote 0.6.13", "rocket_http", - "version_check 0.9.2", + "version_check 0.9.3", "yansi", ] @@ -1260,7 +1326,7 @@ dependencies = [ "percent-encoding 1.0.1", "smallvec", "state", - "time 0.1.43", + "time 0.1.44", "unicode-xid 0.1.0", ] @@ -1344,22 +1410,22 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.124" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.63", + "syn 1.0.64", ] [[package]] @@ -1429,11 +1495,11 @@ dependencies = [ [[package]] name = "standback" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2beb4d1860a61f571530b3f855a1b538d0200f7871c63331ecd6f17b1f014f8" +checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" dependencies = [ - "version_check 0.9.2", + "version_check 0.9.3", ] [[package]] @@ -1466,7 +1532,7 @@ dependencies = [ "quote 1.0.9", "serde", "serde_derive", - "syn 1.0.63", + "syn 1.0.64", ] [[package]] @@ -1482,7 +1548,7 @@ dependencies = [ "serde_derive", "serde_json", "sha1", - "syn 1.0.63", + "syn 1.0.64", ] [[package]] @@ -1510,9 +1576,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd9bc7ccc2688b3344c2f48b9b546648b25ce0b20fc717ee7fa7981a8ca9717" +checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", @@ -1535,26 +1601,27 @@ dependencies = [ [[package]] name = "time" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", + "wasi", "winapi 0.3.9", ] [[package]] name = "time" -version = "0.2.25" +version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1195b046942c221454c2539395f85413b33383a067449d78aab2b7b052a142f7" +checksum = "08a8cbfbf47955132d0202d1662f49b2423ae35862aee471f3ba4b133358f372" dependencies = [ "const_fn", "libc", "standback", "stdweb", "time-macros", - "version_check 0.9.2", + "version_check 0.9.3", "winapi 0.3.9", ] @@ -1578,7 +1645,7 @@ dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", "standback", - "syn 1.0.63", + "syn 1.0.64", ] [[package]] @@ -1598,15 +1665,15 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d56477f6ed99e10225f38f9f75f872f29b8b8bd8c0b946f63345bb144e9eeda" +checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722" dependencies = [ "autocfg", "bytes", "libc", "memchr", - "mio 0.7.9", + "mio 0.7.11", "num_cpus", "pin-project-lite", ] @@ -1623,9 +1690,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec31e5cc6b46e653cf57762f36f71d5e6386391d88a72fd6db4508f8f676fb29" +checksum = "5143d049e85af7fbc36f5454d990e62c2df705b3589f123b71f441b6b59f443f" dependencies = [ "bytes", "futures-core", @@ -1690,9 +1757,9 @@ checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" [[package]] name = "typenum" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" [[package]] name = "unicase" @@ -1780,15 +1847,15 @@ checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" [[package]] name = "version_check" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "walkdir" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", "winapi 0.3.9", @@ -1807,15 +1874,15 @@ dependencies = [ [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "wasm-bindgen" -version = "0.2.71" +version = "0.2.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee1280240b7c461d6a0071313e08f34a60b0365f14260362e5a2b17d1d31aa7" +checksum = "8fe8f61dba8e5d645a4d8132dc7a0a66861ed5e1045d2c0ed940fab33bac0fbe" dependencies = [ "cfg-if 1.0.0", "serde", @@ -1825,24 +1892,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.71" +version = "0.2.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7d8b6942b8bb3a9b0e73fc79b98095a27de6fa247615e59d096754a3bc2aa8" +checksum = "046ceba58ff062da072c7cb4ba5b22a37f00a302483f7e2a6cdc18fedbdc1fd3" dependencies = [ "bumpalo", "lazy_static", "log 0.4.14", "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.63", + "syn 1.0.64", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e67a5806118af01f0d9045915676b22aaebecf4178ae7021bc171dab0b897ab" +checksum = "73157efb9af26fb564bb59a009afd1c7c334a44db171d280690d0c3faaec3468" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -1852,9 +1919,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.71" +version = "0.2.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ac38da8ef716661f0f36c0d8320b89028efe10c7c0afde65baffb496ce0d3b" +checksum = "0ef9aa01d36cda046f797c57959ff5f3c615c9cc63997a8d545831ec7976819b" dependencies = [ "quote 1.0.9", "wasm-bindgen-macro-support", @@ -1862,28 +1929,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.71" +version = "0.2.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e" +checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d" dependencies = [ "proc-macro2 1.0.24", "quote 1.0.9", - "syn 1.0.63", + "syn 1.0.64", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.71" +version = "0.2.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d6f8ec44822dd71f5f221a5847fb34acd9060535c1211b70a05844c0f6383b1" +checksum = "b7148f4696fb4960a346eaa60bbfb42a1ac4ebba21f750f75fc1375b098d5ffa" [[package]] name = "web-sys" -version = "0.3.48" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec600b26223b2948cedfde2a0aa6756dcf1fef616f43d7b3097aaf53a6c4d92b" +checksum = "59fe19d70f5dacc03f6e46777213facae5ac3801575d56ca6cbd4c93dcd12310" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index a34a58e..6969693 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,8 @@ edition = "2018" [dependencies] rocket = "0.4.7" serde = "1.0.124" +chrono = "0.4.19" +regex = "1.4.5" [dependencies.reqwest] version = "0.11.2" diff --git a/Makefile b/Makefile index 4017a77..a1baf59 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ push: # Run run: - @ cargo run + @ RUST_BACKTRACE=1 cargo run .PHONY: run @@ -32,6 +32,14 @@ test: @ cargo test .PHONY: test +format: + @ cargo fmt +.PHONY: format + +lint: + @ cargo fmt -- --check +.PHONY: lint + # Documentation docs: diff --git a/src/hello/mod.rs b/src/hello/mod.rs index c15060b..608d234 100644 --- a/src/hello/mod.rs +++ b/src/hello/mod.rs @@ -1,11 +1,8 @@ -#[cfg(test)] mod tests; +#[cfg(test)] +mod tests; pub fn routes() -> Vec { - routes![ - world, - hello, - name_age - ] + routes![world, hello, name_age] } #[get("/world")] diff --git a/src/hello/tests.rs b/src/hello/tests.rs index d4f7096..e19264d 100644 --- a/src/hello/tests.rs +++ b/src/hello/tests.rs @@ -1,5 +1,5 @@ -use rocket::local::Client; use rocket::http::Status; +use rocket::local::Client; fn rocket() -> rocket::Rocket { rocket::ignite().mount("/", super::routes()) diff --git a/src/ivago/README.md b/src/ivago/README.md new file mode 100644 index 0000000..c69c1b4 --- /dev/null +++ b/src/ivago/README.md @@ -0,0 +1,5 @@ +# Ivago + +This part of the API is a wrapper around the Ivago website (Ivago being the +company that collects the trash in my city). Their city isn't exactly RESTful, +so this endpoint simply wraps it in a RESTful wrapper. diff --git a/src/ivago/controller/mod.rs b/src/ivago/controller/mod.rs index 01f00a4..8e3cb62 100644 --- a/src/ivago/controller/mod.rs +++ b/src/ivago/controller/mod.rs @@ -1,6 +1,7 @@ +mod pickup_times; mod search; -pub use search::{Street, search_streets}; - +pub use pickup_times::{get_pickup_times, BasicDate, PickupTime}; +pub use search::{search_streets, Street}; ///// Return the known pickup times for the given street and/or city ///// @@ -28,7 +29,7 @@ pub use search::{Street, search_streets}; // let params = [ // ("_format", "json"), // ("type", ""), - + // ] //r2 = s.get("https://www.ivago.be/nl/particulier/garbage/pick-up/pickups?", diff --git a/src/ivago/controller/pickup_times.rs b/src/ivago/controller/pickup_times.rs index 98b8e64..58d3f29 100644 --- a/src/ivago/controller/pickup_times.rs +++ b/src/ivago/controller/pickup_times.rs @@ -1,19 +1,154 @@ +use super::search::Street; +use chrono::{FixedOffset, TimeZone}; +use regex::Regex; +use reqwest::blocking as reqwest; +use rocket::http::RawStr; +use rocket::request::FromFormValue; +use serde::ser::{SerializeStruct, Serializer}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::convert::{From, TryFrom}; +use std::error::Error; + const BASE_URL: &str = "https://www.ivago.be/nl/particulier/afval/ophaling"; +const CAL_URL: &str = "https://www.ivago.be/nl/particulier/garbage/pick-up/pickups"; - -/// Represents a timezoneless date -pub struct Date { - day: u8, - month: u8, +/// Represents a very simple Timezoneless date. Considering the timezone will +/// always be CEST (aka Belgium's timezone), this is good enough. +pub struct BasicDate { year: u32, + month: u8, + day: u8, } +impl<'v> FromFormValue<'v> for BasicDate { + type Error = &'v RawStr; + + fn from_form_value(form_value: &'v RawStr) -> Result { + // Beautiful how this exact example is in the docs + match BasicDate::try_from(form_value.as_str()) { + Err(_) => Err(form_value), + // Here, we can assume these parses will work, because the regex + // didn't fail + Ok(date) => Ok(date), + } + } +} + +impl ToString for BasicDate { + fn to_string(&self) -> String { + format!("{}-{}-{}", self.year, self.month, self.day) + } +} + +impl TryFrom<&str> for BasicDate { + type Error = (); + + fn try_from(s: &str) -> Result { + let re = Regex::new(r"^(\d{4})-(\d{2})-(\d{2})$").unwrap(); + + match re.captures(s) { + None => Err(()), + // Here, we can assume these parses will work, because the regex + // didn't fail + Some(caps) => Ok(BasicDate { + year: caps.get(1).unwrap().as_str().parse().unwrap(), + month: caps.get(2).unwrap().as_str().parse().unwrap(), + day: caps.get(3).unwrap().as_str().parse().unwrap(), + }), + } + } +} + +impl Serialize for BasicDate { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl From for i64 { + fn from(date: BasicDate) -> i64 { + // Timezone of Brussels is UTC + 2 hours in the western hemisphere + FixedOffset::west(7_200) + .ymd(date.year as i32, date.month as u32, date.day as u32) + .and_hms(0, 0, 0) + .timestamp() + } +} + +impl Serialize for PickupTime { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut s = serializer.serialize_struct("PickupTime", 4)?; + s.serialize_field("date", &self.date)?; + s.serialize_field("label", &self.label)?; + s.serialize_field("classes", &self.classes)?; + s.serialize_field("url", &self.url)?; + + s.end() + } +} /// Represents a pickup time instance. All fields are a direct map of the /// original API pub struct PickupTime { - date: Date, + date: BasicDate, label: String, classes: Vec, - url: String + url: String, +} + +pub fn get_pickup_times( + street: Street, + number: u32, + start_date: BasicDate, + end_date: BasicDate, +) -> Result, Box> { + let client = reqwest::Client::builder().cookie_store(true).build()?; + + // This populates the cookies with the necessary values + client + .post(BASE_URL) + .form(&[ + ("garbage_type", ""), + ("ivago_street", &String::from(street)), + ("number", &number.to_string()), + ("form_id", "garbage_address_form"), + ]) + .send()?; + + let response = client + .get(CAL_URL) + .query(&[ + ("_format", "json"), + ("type", ""), + ("start", &i64::from(start_date).to_string()), + ("end", &i64::from(end_date).to_string()), + ]) + .send()?; + let data: Vec> = response.json()?; + + let mut output: Vec = Vec::new(); + + for map in data.iter() { + output.push(PickupTime { + // TODO should I check here if the parsing worked? + date: BasicDate::try_from(map.get("date").unwrap().as_str()).unwrap(), + label: map.get("label").unwrap().to_string(), + classes: map + .get("classes") + .unwrap() + .split_whitespace() + .map(|x| String::from(x)) + .collect(), + url: map.get("url").unwrap().to_string(), + }) + } + + Ok(output) } diff --git a/src/ivago/controller/search.rs b/src/ivago/controller/search.rs index d0282de..be81254 100644 --- a/src/ivago/controller/search.rs +++ b/src/ivago/controller/search.rs @@ -1,13 +1,14 @@ +use regex::Regex; use reqwest::blocking as reqwest; +use rocket::http::RawStr; +use rocket::request::FromFormValue; +use serde::ser::{Serialize, SerializeStruct, Serializer}; use std::collections::HashMap; use std::convert::TryFrom; -use serde::ser::{Serialize, Serializer, SerializeStruct}; use std::error::Error; - /// Endpoint for the search feature -const SEARCH_URL: &str ="https://www.ivago.be/nl/particulier/autocomplete/garbage/streets"; - +const SEARCH_URL: &str = "https://www.ivago.be/nl/particulier/autocomplete/garbage/streets"; impl From for String { fn from(street: Street) -> String { @@ -15,36 +16,49 @@ impl From for String { } } - impl Serialize for Street { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut s = serializer.serialize_struct("Street", 2)?; - s.serialize_field("name", &self.name)?; - s.serialize_field("city", &self.city)?; - s.end() - } + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut s = serializer.serialize_struct("Street", 2)?; + s.serialize_field("name", &self.name)?; + s.serialize_field("city", &self.city)?; + s.end() + } } - impl TryFrom for Street { type Error = (); fn try_from(value: String) -> Result { if let Some(index) = value.find('(') { Ok(Street { - name: (value[0 .. index - 1].trim()).to_string(), - city: (value[index + 1 .. value.len() - 1].trim()).to_string(), + name: (value[0..index - 1].trim()).to_string(), + city: (value[index + 1..value.len() - 1].trim()).to_string(), }) - - }else { + } else { Err(()) } } } +impl<'v> FromFormValue<'v> for Street { + type Error = &'v RawStr; + + fn from_form_value(form_value: &'v RawStr) -> Result { + // This regex is pretty loose tbh, but not sure how I can make it more + // strict right now + let re = Regex::new(r"^(.+) \((.+)\)$").unwrap(); + match re.captures(&form_value.url_decode_lossy()) { + None => Err(form_value), + Some(caps) => Ok(Street { + name: String::from(caps.get(1).unwrap().as_str()), + city: String::from(caps.get(2).unwrap().as_str()), + }), + } + } +} /// Represents a street pub struct Street { @@ -52,7 +66,6 @@ pub struct Street { pub city: String, } - /// Searches the Ivago API for streets in the given city /// /// # Arguments @@ -62,9 +75,7 @@ pub struct Street { // TODO find out how to do this async pub fn search_streets(street_name: &String) -> Result, Box> { let client = reqwest::Client::new(); - let response = client.get(SEARCH_URL) - .query(&[("q", street_name)]) - .send()?; + let response = client.get(SEARCH_URL).query(&[("q", street_name)]).send()?; let data: Vec> = response.json()?; let mut output: Vec = Vec::new(); diff --git a/src/ivago/mod.rs b/src/ivago/mod.rs index ad71ca9..33ab64e 100644 --- a/src/ivago/mod.rs +++ b/src/ivago/mod.rs @@ -1,24 +1,33 @@ -#[cfg(test)] mod tests; mod controller; +#[cfg(test)] +mod tests; +use rocket::http::Status; use rocket_contrib::json::Json; pub fn routes() -> Vec { - routes![ - search_streets_json, - ] + routes![route_search_streets, route_get_pickup_times] } // URL: https://www.ivago.be/nl/particulier/autocomplete/garbage/streets?q=Lange -// TODO make this async -// TODO change this so it can return errors instead of empty json #[get("/search?")] -pub fn search_streets_json(street: String) -> Json> { +pub fn route_search_streets(street: String) -> Result>, Status> { match controller::search_streets(&street) { - Ok(streets) => Json(streets), - Err(err) => { - println!("{:?}", err); - Json(Vec::new()) - }, + Ok(streets) => Ok(Json(streets)), + Err(_) => Err(Status::InternalServerError), + } +} + +#[get("/?&&&")] +pub fn route_get_pickup_times( + street: controller::Street, + number: u32, + start_date: controller::BasicDate, + end_date: controller::BasicDate, +) -> Result>, Status> { + match controller::get_pickup_times(street, number, start_date, end_date) { + // TODO provide more meaningful status codes here + Err(_) => Err(Status::InternalServerError), + Ok(times) => Ok(Json(times)), } } diff --git a/src/ivago/tests.rs b/src/ivago/tests.rs index e69de29..8b13789 100644 --- a/src/ivago/tests.rs +++ b/src/ivago/tests.rs @@ -0,0 +1 @@ + diff --git a/src/main.rs b/src/main.rs index 43348ab..8a5ea07 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ #![feature(proc_macro_hygiene, decl_macro)] -#[macro_use] extern crate rocket; +#[macro_use] +extern crate rocket; // Route modules mod hello;