DEVELOPMENT ENVIRONMENT

~liljamo/ulairi

e78be1f39347874c13c8ff1c08025ec375b5e928 — Jonni Liljamo 1 year, 11 months ago
I lost the old commit history...
38 files changed, 6614 insertions(+), 0 deletions(-)

A README.md
A ulairi-api/Cargo.lock
A ulairi-api/Cargo.toml
A ulairi-api/Dockerfile.debian
A ulairi-api/README.md
A ulairi-api/diesel.toml
A ulairi-api/migrations/00000000000000_diesel_initial_setup/down.sql
A ulairi-api/migrations/00000000000000_diesel_initial_setup/up.sql
A ulairi-api/migrations/2022-04-27-190554_users/down.sql
A ulairi-api/migrations/2022-04-27-190554_users/up.sql
A ulairi-api/migrations/2022-04-28-071220_roles/down.sql
A ulairi-api/migrations/2022-04-28-071220_roles/up.sql
A ulairi-api/migrations/2022-04-28-071249_users_roles/down.sql
A ulairi-api/migrations/2022-04-28-071249_users_roles/up.sql
A ulairi-api/migrations/2022-04-29-115235_basic_roles/down.sql
A ulairi-api/migrations/2022-04-29-115235_basic_roles/up.sql
A ulairi-api/migrations/2022-05-01-115036_hour_entries/down.sql
A ulairi-api/migrations/2022-05-01-115036_hour_entries/up.sql
A ulairi-api/scripts/deploy.sh
A ulairi-api/src/db.rs
A ulairi-api/src/hour_entries/mod.rs
A ulairi-api/src/hour_entries/model.rs
A ulairi-api/src/main.rs
A ulairi-api/src/roles/mod.rs
A ulairi-api/src/roles/model.rs
A ulairi-api/src/schema.rs
A ulairi-api/src/users/auth.rs
A ulairi-api/src/users/mod.rs
A ulairi-api/src/users/model.rs
A ulairi-client/Cargo.lock
A ulairi-client/Cargo.toml
A ulairi-client/Projektisuunnitelma.odt
A ulairi-client/README.md
A ulairi-client/src/app.rs
A ulairi-client/src/app/api.rs
A ulairi-client/src/app/widget/mod.rs
A ulairi-client/src/app/widget/password.rs
A ulairi-client/src/main.rs
A  => README.md +6 -0
@@ 1,6 @@
# Úlairi

A simple time-tracking API and client.

See the READMEs in ulairi-api/ and ulairi-client/ for their setups.


A  => ulairi-api/Cargo.lock +1678 -0
@@ 1,1678 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "aead"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331"
dependencies = [
 "generic-array",
]

[[package]]
name = "aes"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561"
dependencies = [
 "aes-soft",
 "aesni",
 "cipher",
]

[[package]]
name = "aes-gcm"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da"
dependencies = [
 "aead",
 "aes",
 "cipher",
 "ctr",
 "ghash",
 "subtle",
]

[[package]]
name = "aes-soft"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072"
dependencies = [
 "cipher",
 "opaque-debug",
]

[[package]]
name = "aesni"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce"
dependencies = [
 "cipher",
 "opaque-debug",
]

[[package]]
name = "argon2"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a27e27b63e4a34caee411ade944981136fdfa535522dc9944d6700196cbd899f"
dependencies = [
 "base64ct",
 "blake2",
 "password-hash",
]

[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
 "hermit-abi",
 "libc",
 "winapi 0.3.9",
]

[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"

[[package]]
name = "base64"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
dependencies = [
 "byteorder",
 "safemem",
]

[[package]]
name = "base64"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"

[[package]]
name = "base64ct"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179"

[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"

[[package]]
name = "blake2"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388"
dependencies = [
 "digest 0.10.3",
]

[[package]]
name = "block-buffer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
 "generic-array",
]

[[package]]
name = "block-buffer"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
dependencies = [
 "generic-array",
]

[[package]]
name = "buf_redux"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f"
dependencies = [
 "memchr",
 "safemem",
]

[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"

[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"

[[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.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
 "libc",
 "num-integer",
 "num-traits",
 "serde",
 "time",
 "winapi 0.3.9",
]

[[package]]
name = "cipher"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
dependencies = [
 "generic-array",
]

[[package]]
name = "cookie"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80f6044740a4a516b8aac14c140cdf35c1a640b1bd6b98b6224e49143b2f1566"
dependencies = [
 "aes-gcm",
 "base64 0.13.0",
 "hkdf",
 "hmac",
 "percent-encoding 2.1.0",
 "rand 0.8.5",
 "sha2",
 "time",
]

[[package]]
name = "cpufeatures"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
dependencies = [
 "libc",
]

[[package]]
name = "cpuid-bool"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba"

[[package]]
name = "crypto-common"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
dependencies = [
 "generic-array",
 "typenum",
]

[[package]]
name = "crypto-mac"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a"
dependencies = [
 "generic-array",
 "subtle",
]

[[package]]
name = "ctr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f"
dependencies = [
 "cipher",
]

[[package]]
name = "devise"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd716c4a507adc5a2aa7c2a372d06c7497727e0892b243d3036bc7478a13e526"
dependencies = [
 "devise_codegen",
 "devise_core",
]

[[package]]
name = "devise_codegen"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea7b8290d118127c08e3669da20b331bed56b09f20be5945b7da6c116d8fab53"
dependencies = [
 "devise_core",
 "quote 0.6.13",
]

[[package]]
name = "devise_core"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1053e9d5d5aade9bcedb5ab53b78df2b56ff9408a3138ce77eaaef87f932373"
dependencies = [
 "bitflags",
 "proc-macro2 0.4.30",
 "quote 0.6.13",
 "syn 0.15.44",
]

[[package]]
name = "diesel"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d"
dependencies = [
 "bitflags",
 "byteorder",
 "chrono",
 "diesel_derives",
 "pq-sys",
]

[[package]]
name = "diesel_derives"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3"
dependencies = [
 "proc-macro2 1.0.39",
 "quote 1.0.18",
 "syn 1.0.95",
]

[[package]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
 "generic-array",
]

[[package]]
name = "digest"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [
 "block-buffer 0.10.2",
 "crypto-common",
 "subtle",
]

[[package]]
name = "dotenv"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"

[[package]]
name = "dotenv_codegen"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56966279c10e4f8ee8c22123a15ed74e7c8150b658b26c619c53f4a56eb4a8aa"
dependencies = [
 "dotenv_codegen_implementation",
 "proc-macro-hack",
]

[[package]]
name = "dotenv_codegen_implementation"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e737a3522cd45f6adc19b644ce43ef53e1e9045f2d2de425c1f468abd4cf33"
dependencies = [
 "dotenv",
 "proc-macro-hack",
 "proc-macro2 1.0.39",
 "quote 1.0.18",
 "syn 1.0.95",
]

[[package]]
name = "fastrand"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
dependencies = [
 "instant",
]

[[package]]
name = "filetime"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c"
dependencies = [
 "cfg-if 1.0.0",
 "libc",
 "redox_syscall",
 "winapi 0.3.9",
]

[[package]]
name = "fsevent"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6"
dependencies = [
 "bitflags",
 "fsevent-sys",
]

[[package]]
name = "fsevent-sys"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0"
dependencies = [
 "libc",
]

[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"

[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
dependencies = [
 "bitflags",
 "fuchsia-zircon-sys",
]

[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"

[[package]]
name = "gcc"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"

[[package]]
name = "generic-array"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
dependencies = [
 "typenum",
 "version_check 0.9.4",
]

[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
 "cfg-if 1.0.0",
 "libc",
 "wasi 0.9.0+wasi-snapshot-preview1",
]

[[package]]
name = "getrandom"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
dependencies = [
 "cfg-if 1.0.0",
 "libc",
 "wasi 0.10.0+wasi-snapshot-preview1",
]

[[package]]
name = "ghash"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375"
dependencies = [
 "opaque-debug",
 "polyval",
]

[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"

[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"

[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
 "libc",
]

[[package]]
name = "hkdf"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f"
dependencies = [
 "digest 0.9.0",
 "hmac",
]

[[package]]
name = "hmac"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15"
dependencies = [
 "crypto-mac",
 "digest 0.9.0",
]

[[package]]
name = "httparse"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c"

[[package]]
name = "hyper"
version = "0.10.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273"
dependencies = [
 "base64 0.9.3",
 "httparse",
 "language-tags",
 "log 0.3.9",
 "mime 0.2.6",
 "num_cpus",
 "time",
 "traitobject",
 "typeable",
 "unicase 1.4.2",
 "url",
]

[[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 = "indexmap"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
dependencies = [
 "autocfg",
 "hashbrown",
]

[[package]]
name = "inotify"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f"
dependencies = [
 "bitflags",
 "inotify-sys",
 "libc",
]

[[package]]
name = "inotify-sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
dependencies = [
 "libc",
]

[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
 "cfg-if 1.0.0",
]

[[package]]
name = "iovec"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
dependencies = [
 "libc",
]

[[package]]
name = "itoa"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"

[[package]]
name = "jwt"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caa2b51232f4dba9bcbdc082f4ea5bee58d5c2866770b4dc80c868d09bd82569"
dependencies = [
 "rust-crypto",
 "rustc-serialize",
]

[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
dependencies = [
 "winapi 0.2.8",
 "winapi-build",
]

[[package]]
name = "language-tags"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"

[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"

[[package]]
name = "libc"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"

[[package]]
name = "lock_api"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
dependencies = [
 "autocfg",
 "scopeguard",
]

[[package]]
name = "log"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
dependencies = [
 "log 0.4.17",
]

[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
 "cfg-if 1.0.0",
]

[[package]]
name = "matches"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"

[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"

[[package]]
name = "mime"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
dependencies = [
 "log 0.3.9",
]

[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"

[[package]]
name = "mime_guess"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
dependencies = [
 "mime 0.3.16",
 "unicase 2.6.0",
]

[[package]]
name = "mio"
version = "0.6.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
dependencies = [
 "cfg-if 0.1.10",
 "fuchsia-zircon",
 "fuchsia-zircon-sys",
 "iovec",
 "kernel32-sys",
 "libc",
 "log 0.4.17",
 "miow",
 "net2",
 "slab",
 "winapi 0.2.8",
]

[[package]]
name = "mio-extras"
version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
dependencies = [
 "lazycell",
 "log 0.4.17",
 "mio",
 "slab",
]

[[package]]
name = "miow"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
dependencies = [
 "kernel32-sys",
 "net2",
 "winapi 0.2.8",
 "ws2_32-sys",
]

[[package]]
name = "multipart"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d050aeedc89243f5347c3e237e3e13dc76fbe4ae3742a57b94dc14f69acf76d4"
dependencies = [
 "buf_redux",
 "httparse",
 "log 0.4.17",
 "mime 0.3.16",
 "mime_guess",
 "quick-error",
 "rand 0.7.3",
 "safemem",
 "tempfile",
 "twoway",
]

[[package]]
name = "net2"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
dependencies = [
 "cfg-if 0.1.10",
 "libc",
 "winapi 0.3.9",
]

[[package]]
name = "notify"
version = "4.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257"
dependencies = [
 "bitflags",
 "filetime",
 "fsevent",
 "fsevent-sys",
 "inotify",
 "libc",
 "mio",
 "mio-extras",
 "walkdir",
 "winapi 0.3.9",
]

[[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_cpus"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
 "hermit-abi",
 "libc",
]

[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"

[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
 "instant",
 "lock_api",
 "parking_lot_core 0.8.5",
]

[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
 "lock_api",
 "parking_lot_core 0.9.3",
]

[[package]]
name = "parking_lot_core"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [
 "cfg-if 1.0.0",
 "instant",
 "libc",
 "redox_syscall",
 "smallvec",
 "winapi 0.3.9",
]

[[package]]
name = "parking_lot_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
dependencies = [
 "cfg-if 1.0.0",
 "libc",
 "redox_syscall",
 "smallvec",
 "windows-sys",
]

[[package]]
name = "password-hash"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e029e94abc8fb0065241c308f1ac6bc8d20f450e8f7c5f0b25cd9b8d526ba294"
dependencies = [
 "base64ct",
 "rand_core 0.6.3",
 "subtle",
]

[[package]]
name = "pear"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32dfa7458144c6af7f9ce6a137ef975466aa68ffa44d4d816ee5934018ba960a"
dependencies = [
 "pear_codegen",
]

[[package]]
name = "pear_codegen"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0288ba5d581afbc93e2bbd931c1013584c15ecf46b1cdb927edc7abddbc8ca6"
dependencies = [
 "proc-macro2 0.4.30",
 "quote 0.6.13",
 "syn 0.15.44",
 "version_check 0.9.4",
 "yansi",
]

[[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.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"

[[package]]
name = "polyval"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd"
dependencies = [
 "cpuid-bool",
 "opaque-debug",
 "universal-hash",
]

[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"

[[package]]
name = "pq-sys"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ac25eee5a0582f45a67e837e350d784e7003bd29a5f460796772061ca49ffda"
dependencies = [
 "vcpkg",
]

[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"

[[package]]
name = "proc-macro2"
version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
dependencies = [
 "unicode-xid",
]

[[package]]
name = "proc-macro2"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
dependencies = [
 "unicode-ident",
]

[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"

[[package]]
name = "quote"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
dependencies = [
 "proc-macro2 0.4.30",
]

[[package]]
name = "quote"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
 "proc-macro2 1.0.39",
]

[[package]]
name = "r2d2"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f"
dependencies = [
 "log 0.4.17",
 "parking_lot 0.11.2",
 "scheduled-thread-pool",
]

[[package]]
name = "r2d2-diesel"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9c29bad92da76d02bc2c020452ebc3a3fe6fa74cfab91e711c43116e4fb1a3"
dependencies = [
 "diesel",
 "r2d2",
]

[[package]]
name = "rand"
version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
dependencies = [
 "libc",
 "rand 0.4.6",
]

[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
 "fuchsia-cprng",
 "libc",
 "rand_core 0.3.1",
 "rdrand",
 "winapi 0.3.9",
]

[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
 "getrandom 0.1.16",
 "libc",
 "rand_chacha 0.2.2",
 "rand_core 0.5.1",
 "rand_hc",
]

[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
 "libc",
 "rand_chacha 0.3.1",
 "rand_core 0.6.3",
]

[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
 "ppv-lite86",
 "rand_core 0.5.1",
]

[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
 "ppv-lite86",
 "rand_core 0.6.3",
]

[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
 "rand_core 0.4.2",
]

[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"

[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
 "getrandom 0.1.16",
]

[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
 "getrandom 0.2.6",
]

[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
 "rand_core 0.5.1",
]

[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
 "rand_core 0.3.1",
]

[[package]]
name = "redox_syscall"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
dependencies = [
 "bitflags",
]

[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
 "winapi 0.3.9",
]

[[package]]
name = "rocket"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83b9d9dc08c5dcc1d8126a9dd615545e6a358f8c13c883c8dfed8c0376fa355e"
dependencies = [
 "atty",
 "base64 0.13.0",
 "log 0.4.17",
 "memchr",
 "num_cpus",
 "pear",
 "rocket_codegen",
 "rocket_http",
 "state",
 "time",
 "toml",
 "version_check 0.9.4",
 "yansi",
]

[[package]]
name = "rocket-multipart-form-data"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73d29a55443ad045eea2121f6047f25dc2f10eda1adc13ed0f00c12af182831f"
dependencies = [
 "mime 0.3.16",
 "multipart",
 "rocket",
]

[[package]]
name = "rocket_codegen"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2810037b5820098af97bd4fdd309e76a8101ceb178147de775c835a2537284fe"
dependencies = [
 "devise",
 "glob",
 "indexmap",
 "quote 0.6.13",
 "rocket_http",
 "version_check 0.9.4",
 "yansi",
]

[[package]]
name = "rocket_contrib"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e20efbc6a211cb3df5375accf532d4186f224b623f39eca650b19b96240c596b"
dependencies = [
 "log 0.4.17",
 "notify",
 "rocket",
 "serde",
 "serde_json",
]

[[package]]
name = "rocket_http"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf9cbd128e1f321a2d0bebd2b7cf0aafd89ca43edf69e49b56a5c46e48eb19f"
dependencies = [
 "cookie",
 "hyper",
 "indexmap",
 "pear",
 "percent-encoding 1.0.1",
 "smallvec",
 "state",
 "time",
 "unicode-xid",
]

[[package]]
name = "rust-crypto"
version = "0.2.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
dependencies = [
 "gcc",
 "libc",
 "rand 0.3.23",
 "rustc-serialize",
 "time",
]

[[package]]
name = "rustc-serialize"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"

[[package]]
name = "ryu"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"

[[package]]
name = "safemem"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"

[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
 "winapi-util",
]

[[package]]
name = "scheduled-thread-pool"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "977a7519bff143a44f842fd07e80ad1329295bd71686457f18e496736f4bf9bf"
dependencies = [
 "parking_lot 0.12.1",
]

[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"

[[package]]
name = "serde"
version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
dependencies = [
 "serde_derive",
]

[[package]]
name = "serde_derive"
version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
dependencies = [
 "proc-macro2 1.0.39",
 "quote 1.0.18",
 "syn 1.0.95",
]

[[package]]
name = "serde_json"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
dependencies = [
 "itoa",
 "ryu",
 "serde",
]

[[package]]
name = "sha2"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
dependencies = [
 "block-buffer 0.9.0",
 "cfg-if 1.0.0",
 "cpufeatures",
 "digest 0.9.0",
 "opaque-debug",
]

[[package]]
name = "slab"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"

[[package]]
name = "smallvec"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"

[[package]]
name = "state"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3015a7d0a5fd5105c91c3710d42f9ccf0abfb287d62206484dcc67f9569a6483"

[[package]]
name = "subtle"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"

[[package]]
name = "syn"
version = "0.15.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
dependencies = [
 "proc-macro2 0.4.30",
 "quote 0.6.13",
 "unicode-xid",
]

[[package]]
name = "syn"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
dependencies = [
 "proc-macro2 1.0.39",
 "quote 1.0.18",
 "unicode-ident",
]

[[package]]
name = "tempfile"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
 "cfg-if 1.0.0",
 "fastrand",
 "libc",
 "redox_syscall",
 "remove_dir_all",
 "winapi 0.3.9",
]

[[package]]
name = "time"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
 "libc",
 "wasi 0.10.0+wasi-snapshot-preview1",
 "winapi 0.3.9",
]

[[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.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"

[[package]]
name = "toml"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
dependencies = [
 "serde",
]

[[package]]
name = "traitobject"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"

[[package]]
name = "twoway"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1"
dependencies = [
 "memchr",
]

[[package]]
name = "typeable"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"

[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"

[[package]]
name = "ulairi-api"
version = "0.1.0"
dependencies = [
 "argon2",
 "chrono",
 "diesel",
 "dotenv",
 "dotenv_codegen",
 "jwt",
 "r2d2",
 "r2d2-diesel",
 "rand_core 0.6.3",
 "rocket",
 "rocket-multipart-form-data",
 "rocket_codegen",
 "rocket_contrib",
 "rust-crypto",
 "rustc-serialize",
 "serde",
]

[[package]]
name = "unicase"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
dependencies = [
 "version_check 0.1.5",
]

[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
 "version_check 0.9.4",
]

[[package]]
name = "unicode-bidi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"

[[package]]
name = "unicode-ident"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"

[[package]]
name = "unicode-normalization"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
dependencies = [
 "tinyvec",
]

[[package]]
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"

[[package]]
name = "universal-hash"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
dependencies = [
 "generic-array",
 "subtle",
]

[[package]]
name = "url"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
dependencies = [
 "idna",
 "matches",
 "percent-encoding 1.0.1",
]

[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"

[[package]]
name = "version_check"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"

[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"

[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
dependencies = [
 "same-file",
 "winapi 0.3.9",
 "winapi-util",
]

[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"

[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"

[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"

[[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-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"

[[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 0.3.9",
]

[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

[[package]]
name = "windows-sys"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
 "windows_aarch64_msvc",
 "windows_i686_gnu",
 "windows_i686_msvc",
 "windows_x86_64_gnu",
 "windows_x86_64_msvc",
]

[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"

[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"

[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"

[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"

[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"

[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
dependencies = [
 "winapi 0.2.8",
 "winapi-build",
]

[[package]]
name = "yansi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"

A  => ulairi-api/Cargo.toml +26 -0
@@ 1,26 @@
[package]
name = "ulairi-api"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

# TODO: Update dependencies to latest versions
[dependencies]
rocket = "0.4.5"
rocket_codegen = "0.4.5"
rocket_contrib = "0.4.5"
diesel = { version = "1.4.5", features = ["postgres", "chrono"] }
chrono = { version = "0.4.19", features = ["serde"] }
dotenv = "0.15.0"
dotenv_codegen = "0.15.0"
rocket-multipart-form-data = "0.9.5"
serde = { version = "1.0", features = ["derive"] }
r2d2-diesel = "1.0.0"
r2d2 = "0.8.3"
jwt = "0.4.0"
rust-crypto = "0.2"
rustc-serialize = "0.3"

argon2 = "0.4.0"
rand_core = { version = "0.6.0", features = ["std"] }

A  => ulairi-api/Dockerfile.debian +45 -0
@@ 1,45 @@
## Buiilder
FROM rust:latest AS builder

RUN rustup default nightly
RUN update-ca-certificates

# Create appuser
ENV USER=ulairi
ENV UID=10001

RUN adduser \
  --disabled-password \
  --gecos "" \
  --home "/nonexistent" \
  --shell "/sbin/nologin" \
  --no-create-home \
  --uid "${UID}" \
  "${USER}"

WORKDIR /ulairi-api

COPY ./ .

RUN cargo build --release

## Final image
FROM debian:buster-slim

RUN apt update
RUN apt install -y libpq5 libpq-dev

# Import from builder
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /etc/group /etc/group

WORKDIR /ulairi-api

# Copy our build
COPY --from=builder /ulairi-api/target/release/ulairi-api ./

# Use an unprivileged user
USER ulairi:ulairi

CMD ["/ulairi-api/ulairi-api"]


A  => ulairi-api/README.md +12 -0
@@ 1,12 @@
# Template

Install diesel:
 * cargo install diesel_cli --no-default-features --features "postgres"

Run migrations:
 * diesel migration run

Edit .env:
 * JWT_KEY = tr -dc A-Za-z0-9 </dev/urandom | head -c 32 ; echo ''
 * DATABASE_URL = postgres connection string, ideally running on localhost!


A  => ulairi-api/diesel.toml +5 -0
@@ 1,5 @@
# For documentation on how to configure this file,
# see diesel.rs/guides/configuring-diesel-cli

[print_schema]
file = "src/schema.rs"

A  => ulairi-api/migrations/00000000000000_diesel_initial_setup/down.sql +6 -0
@@ 1,6 @@
-- This file was automatically created by Diesel to setup helper functions
-- and other internal bookkeeping. This file is safe to edit, any future
-- changes will be added to existing projects as new migrations.

DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
DROP FUNCTION IF EXISTS diesel_set_updated_at();

A  => ulairi-api/migrations/00000000000000_diesel_initial_setup/up.sql +36 -0
@@ 1,36 @@
-- This file was automatically created by Diesel to setup helper functions
-- and other internal bookkeeping. This file is safe to edit, any future
-- changes will be added to existing projects as new migrations.




-- Sets up a trigger for the given table to automatically set a column called
-- `updated_at` whenever the row is modified (unless `updated_at` was included
-- in the modified columns)
--
-- # Example
--
-- ```sql
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
--
-- SELECT diesel_manage_updated_at('users');
-- ```
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
BEGIN
    EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
                    FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
BEGIN
    IF (
        NEW IS DISTINCT FROM OLD AND
        NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
    ) THEN
        NEW.updated_at := current_timestamp;
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

A  => ulairi-api/migrations/2022-04-27-190554_users/down.sql +3 -0
@@ 1,3 @@
-- This file should undo anything in `up.sql`
DROP TABLE users;


A  => ulairi-api/migrations/2022-04-27-190554_users/up.sql +8 -0
@@ 1,8 @@
-- Your SQL goes here
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  username VARCHAR(128) UNIQUE NOT NULL,
  email VARCHAR(128) UNIQUE NOT NULL,
  password VARCHAR(256) NOT NULL
);


A  => ulairi-api/migrations/2022-04-28-071220_roles/down.sql +3 -0
@@ 1,3 @@
-- This file should undo anything in `up.sql`
DROP TABLE roles;


A  => ulairi-api/migrations/2022-04-28-071220_roles/up.sql +6 -0
@@ 1,6 @@
-- Your SQL goes here
CREATE TABLE roles (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255) UNIQUE NOT NULL
);


A  => ulairi-api/migrations/2022-04-28-071249_users_roles/down.sql +3 -0
@@ 1,3 @@
-- This file should undo anything in `up.sql`
DROP TABLE users_roles;


A  => ulairi-api/migrations/2022-04-28-071249_users_roles/up.sql +8 -0
@@ 1,8 @@
-- Your SQL goes here
-- Normally wouldn't need a primary key, but diesel requires it.
CREATE TABLE users_roles (
  id SERIAL PRIMARY KEY,
  user_id INTEGER REFERENCES users(id) NOT NULL,
  role_id INTEGER REFERENCES roles(id) NOT NULL
);


A  => ulairi-api/migrations/2022-04-29-115235_basic_roles/down.sql +5 -0
@@ 1,5 @@
-- This file should undo anything in `up.sql`
DELETE FROM roles WHERE name = 'admin';
DELETE FROM users WHERE name = 'teamleader';
DELETE FROM roles WHERE name = 'user';


A  => ulairi-api/migrations/2022-04-29-115235_basic_roles/up.sql +6 -0
@@ 1,6 @@
-- Your SQL goes here
INSERT INTO roles (name) VALUES ('admin');
INSERT INTO roles (name) VALUES ('teamleader');
INSERT INTO roles (name) VALUES ('user');



A  => ulairi-api/migrations/2022-05-01-115036_hour_entries/down.sql +3 -0
@@ 1,3 @@
-- This file should undo anything in `up.sql`
DROP TABLE hour_entries;


A  => ulairi-api/migrations/2022-05-01-115036_hour_entries/up.sql +9 -0
@@ 1,9 @@
-- Your SQL goes here
CREATE TABLE hour_entries (
  id SERIAL PRIMARY KEY,
  user_id INTEGER REFERENCES users(id) NOT NULL,
  hours INTEGER NOT NULL,
  date_worked DATE NOT NULL,
  date_entered DATE NOT NULL
);


A  => ulairi-api/scripts/deploy.sh +8 -0
@@ 1,8 @@
#!/bin/sh

# Build the image
docker build -t ulairi-api:debian -f Dockerfile.debian .

# Run the image
docker run -ti ulairi-api:debian


A  => ulairi-api/src/db.rs +41 -0
@@ 1,41 @@
use diesel::pg::PgConnection;
use r2d2;
use r2d2_diesel::ConnectionManager;
use rocket::http::Status;
use rocket::request::{self, FromRequest};
use rocket::{Outcome, Request, State};
use std::ops::Deref;

type Pool = r2d2::Pool<ConnectionManager<PgConnection>>;

pub fn init_pool() -> Pool {
    let manager = ConnectionManager::<PgConnection>::new(database_url());
    Pool::new(manager).expect("db pool")
}

fn database_url() -> String {
    // Read the DATABASE_URL environment variable from the .env file
    dotenv!("DATABASE_URL").to_string()
}

pub struct DbConn(pub r2d2::PooledConnection<ConnectionManager<PgConnection>>);

impl<'a, 'r> FromRequest<'a, 'r> for DbConn {
    type Error = ();

    fn from_request(request: &'a Request<'r>) -> request::Outcome<DbConn, Self::Error> {
        let pool = request.guard::<State<Pool>>()?;
        match pool.get() {
            Ok(conn) => Outcome::Success(DbConn(conn)),
            Err(_) => Outcome::Failure((Status::ServiceUnavailable, ())),
        }
    }
}

impl Deref for DbConn {
    type Target = PgConnection;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

A  => ulairi-api/src/hour_entries/mod.rs +137 -0
@@ 1,137 @@
pub mod model;

use self::model::HourEntry;
use rocket::{self, http::Status};
use rocket_contrib::json::{Json, JsonValue};
use serde::{Deserialize, Serialize};

use crate::users::auth::ApiKey;
use crate::users::model::User;

use crate::db;

#[derive(Serialize, Deserialize)]
struct RequestDetails {
    email: String,
}

#[post("/all", format = "application/json", data = "<request_details>")]
fn all_entries(
    request_details: Json<RequestDetails>,
    key: ApiKey,
    connection: db::DbConn,
) -> Result<Json<JsonValue>, Status> {
    let email = request_details.email.to_string();
    match User::get_by_email(email.clone(), &connection) {
        Some(user) => {
            // Check if the key is owned by the email given
            if key.0 == email {
                let entries = HourEntry::get_all(user.id, &connection);
                match entries {
                    Ok(entries) => Ok(Json(json!(entries))),
                    Err(_) => Err(Status::InternalServerError),
                }
            } else {
                // Key is not owned by the email given
                Err(Status::Unauthorized)
            }
        }
        // User not found
        None => Err(Status::NotFound),
    }
}

#[derive(Serialize, Deserialize)]
struct InsertDetails {
    email: String,
    hours: i32,
    date_worked: String,
}

#[post("/insert", format = "application/json", data = "<insert_details>")]
fn insert_entry(
    insert_details: Json<InsertDetails>,
    key: ApiKey,
    connection: db::DbConn,
) -> Result<Json<JsonValue>, Status> {
    let email = insert_details.email.to_string();
    let hours = insert_details.hours;
    let date_worked = insert_details.date_worked.to_string();

    match User::get_by_email(email.clone(), &connection) {
        Some(user) => {
            if key.0 == email {
                let entry = HourEntry::create(user.id, hours, date_worked, &connection);

                // Return entry
                match entry {
                    Ok(entry) => Ok(Json(json!(entry))),
                    Err(_) => Err(Status::InternalServerError),
                }
            } else {
                Err(Status::Unauthorized)
            }
        }
        None => Err(Status::NotFound),
    }
}

#[derive(Serialize, Deserialize)]
struct DeleteDetails {
    email: String,
    id: i32,
}

#[post("/delete", format = "application/json", data = "<delete_details>")]
fn delete_entry(
    delete_details: Json<DeleteDetails>,
    key: ApiKey,
    connection: db::DbConn,
) -> Json<JsonValue> {
    let email = delete_details.email.to_string();
    let id = delete_details.id;

    match User::get_by_email(email.clone(), &connection) {
        Some(_) => {
            if key.0 == email {
                match HourEntry::get_by_id(id, &connection) {
                    Ok(_) => match HourEntry::delete(id, &connection) {
                        Ok(_) => Json(json!(
                        {
                            "success": false,
                            "message": "Entry deletion failed"
                        })),
                        Err(_) => Json(json!(
                        {
                            "success": true,
                            "message": "Entry deleted succesfully"
                        })),
                    },
                    Err(_) => Json(json!(
                    {
                        "success": false,
                        "message": "Entry not found"
                    })),
                }
            } else {
                Json(json!(
                {
                    "success": false,
                    "message": "You are not authorized to delete this entry"
                }))
            }
        }
        None => Json(json!(
        {
            "success": false,
            "message": "User not found"
        })),
    }
}

pub fn mount(rocket: rocket::Rocket) -> rocket::Rocket {
    rocket.mount(
        "/api/hours",
        routes![all_entries, insert_entry, delete_entry],
    )
}

A  => ulairi-api/src/hour_entries/model.rs +81 -0
@@ 1,81 @@
use crate::schema::hour_entries;

use diesel;
use diesel::pg::PgConnection;
use diesel::prelude::*;

use serde::{Deserialize, Serialize};

use chrono::NaiveDate;

#[derive(Serialize, Deserialize, Queryable, AsChangeset)]
#[table_name = "hour_entries"]
pub struct HourEntry {
    pub id: i32,
    pub user_id: i32,
    pub hours: i32,
    pub date_worked: NaiveDate,
    pub date_entered: NaiveDate,
}

#[derive(Serialize, Deserialize, Insertable)]
#[table_name = "hour_entries"]
pub struct InsertableHourEntry {
    pub user_id: i32,
    pub hours: i32,
    pub date_worked: NaiveDate,
    pub date_entered: NaiveDate,
}

impl HourEntry {
    pub fn create(
        user_id: i32,
        hours: i32,
        date_worked: String,
        connection: &PgConnection,
    ) -> QueryResult<HourEntry> {
        let date_worked = NaiveDate::parse_from_str(&date_worked, "%Y-%m-%d").unwrap();
        let date_entered = chrono::Utc::now().naive_utc().date();

        // Create a new hour entry
        let hour_entry = InsertableHourEntry {
            user_id,
            hours,
            date_worked,
            date_entered,
        };

        // Insert the new hour entry
        diesel::insert_into(hour_entries::table)
            .values(hour_entry)
            .execute(connection)?;

        // Return the new hour entry
        hour_entries::table
            .order(hour_entries::id.desc())
            .first(connection)
    }

    pub fn delete(id: i32, connection: &PgConnection) -> QueryResult<HourEntry> {
        // Delete the hour entry
        diesel::delete(hour_entries::table.find(id)).execute(connection)?;

        // Find entry with id, returns an error if not found, which we want
        hour_entries::table
            .filter(hour_entries::id.eq(id))
            .first(connection)
    }

    pub fn get_all(user_id: i32, connection: &PgConnection) -> QueryResult<Vec<HourEntry>> {
        hour_entries::table
            .filter(hour_entries::user_id.eq(user_id))
            .order(hour_entries::date_worked.desc())
            .load(connection)
    }

    pub fn get_by_id(id: i32, connection: &PgConnection) -> QueryResult<HourEntry> {
        hour_entries::table
            .filter(hour_entries::id.eq(id))
            .first(connection)
    }
}

A  => ulairi-api/src/main.rs +30 -0
@@ 1,30 @@
#![feature(decl_macro)]

#[macro_use]
extern crate diesel;
#[macro_use]
extern crate rocket;
#[macro_use]
extern crate rocket_contrib;
#[macro_use]
extern crate dotenv_codegen;

mod db;

mod hour_entries;
mod roles;
mod users;

pub mod schema;

fn main() {
    println!("Starting server...");

    let mut rocket = rocket::ignite().manage(db::init_pool());

    rocket = users::mount(rocket);
    rocket = roles::mount(rocket);
    rocket = hour_entries::mount(rocket);

    rocket.launch();
}

A  => ulairi-api/src/roles/mod.rs +17 -0
@@ 1,17 @@
pub mod model;

use self::model::Role;
use rocket::{self, http::Status};
use rocket_contrib::json::{Json, JsonValue};

use crate::db;

#[get("/all")]
pub fn get_all(connection: db::DbConn) -> Json<JsonValue> {
    // Return all roles as an array of objects
    Json(json!({ "roles": Role::get_all(&connection) }))
}

pub fn mount(rocket: rocket::Rocket) -> rocket::Rocket {
    rocket.mount("/api/roles", routes![get_all])
}

A  => ulairi-api/src/roles/model.rs +20 -0
@@ 1,20 @@
use crate::schema::roles;

use diesel;
use diesel::pg::PgConnection;
use diesel::prelude::*;

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Queryable, AsChangeset)]
#[table_name = "roles"]
pub struct Role {
    pub id: i32,
    pub name: String,
}

impl Role {
    pub fn get_all(conn: &PgConnection) -> Vec<Role> {
        roles::table.load::<Role>(conn).unwrap()
    }
}

A  => ulairi-api/src/schema.rs +44 -0
@@ 1,44 @@
table! {
    hour_entries (id) {
        id -> Int4,
        user_id -> Int4,
        hours -> Int4,
        date_worked -> Date,
        date_entered -> Date,
    }
}

table! {
    roles (id) {
        id -> Int4,
        name -> Varchar,
    }
}

table! {
    users (id) {
        id -> Int4,
        username -> Varchar,
        email -> Varchar,
        password -> Varchar,
    }
}

table! {
    users_roles (id) {
        id -> Int4,
        user_id -> Int4,
        role_id -> Int4,
    }
}

joinable!(hour_entries -> users (user_id));
joinable!(users_roles -> roles (role_id));
joinable!(users_roles -> users (user_id));

allow_tables_to_appear_in_same_query!(
    hour_entries,
    roles,
    users,
    users_roles,
);

A  => ulairi-api/src/users/auth.rs +40 -0
@@ 1,40 @@
use rocket::request::{self, FromRequest, Request};
use rocket::Outcome;

pub extern crate crypto;
pub extern crate jwt;
pub extern crate rustc_serialize;

use self::jwt::{Header, Registered, Token};
use crypto::sha2::Sha256;

pub struct ApiKey(pub String);

pub fn read_token(key: &str) -> Result<String, String> {
    let token =
        Token::<Header, Registered>::parse(key).map_err(|_| "Unable to parse key".to_string())?;

    // Check if the key is valid
    if token.verify(dotenv!("JWT_KEY").as_bytes(), Sha256::new()) {
        token.claims.sub.ok_or("Claims not valid".to_string())
    } else {
        Err("Token not valid".to_string())
    }
}

impl<'a, 'r> FromRequest<'a, 'r> for ApiKey {
    type Error = ();

    fn from_request(request: &'a Request<'r>) -> request::Outcome<ApiKey, ()> {
        // Get the key(s) from the authorization header
        let keys: Vec<_> = request.headers().get("Authentication").collect();
        if keys.len() != 1 {
            return Outcome::Forward(());
        }
        // Check if the key is valid
        match read_token(keys[0]) {
            Ok(claim) => Outcome::Success(ApiKey(claim)),
            Err(_) => Outcome::Forward(()),
        }
    }
}

A  => ulairi-api/src/users/mod.rs +113 -0
@@ 1,113 @@
pub mod auth;
pub mod model;

use self::auth::crypto::sha2::Sha256;
use self::auth::jwt::{Header, Registered, Token};
use self::auth::ApiKey;
use self::model::{InsertableUser, User};
use rocket;
use rocket_contrib::json::{Json, JsonValue};

use crate::db;
use serde::{Deserialize, Serialize};

#[post("/register", format = "application/json", data = "<user>")]
fn register(user: Json<InsertableUser>, connection: db::DbConn) -> Json<JsonValue> {
    if User::email_is_taken(user.email.clone(), &connection) {
        return Json(json!(
        {
            "success": false,
            "message": "Email is already taken"
        }));
    } else if User::username_is_taken(user.username.clone(), &connection) {
        return Json(json!(
        {
            "success": false,
            "message": "Username is already taken"
        }));
    }

    match User::create(user.into_inner(), &connection) {
        Ok(_) => {
            return Json(json!(
            {
                "success": true,
                "message": "User created"
            }));
        }
        Err(_) => {
            return Json(json!(
            {
                "success": false,
                "message": "Internal server error, please try again or contact support"
            }));
        }
    }
}

#[get("/info")]
fn info(key: ApiKey) -> Json<JsonValue> {
    Json(json!(
        {
            "success": true,
            "message": key.0
        }
    ))
}

#[get("/info", rank = 2)]
fn info_forbidden() -> Json<JsonValue> {
    Json(json!(
        {
            "success": false,
            "message": "Forbidden"
        }
    ))
}

#[derive(Serialize, Deserialize)]
struct Login {
    username: String,
    password: String,
}

#[post("/login", format = "application/json", data = "<login>")]
fn login(login: Json<Login>, connection: db::DbConn) -> Json<JsonValue> {
    let header: Header = Default::default();
    let username = login.username.to_string();
    let password = login.password.to_string();

    match User::get_by_username_and_password(username, password, &connection) {
        None => Json(json!(
        {
            "success": false,
            "message": "Username or password is incorrect"
        })),
        Some(user) => {
            let claims = Registered {
                sub: Some(user.email.into()),
                ..Default::default()
            };
            let token = Token::new(header, claims);

            match token.signed(dotenv!("JWT_KEY").as_bytes(), Sha256::new()) {
                Ok(token) => Json(json!(
                {
                    "success": true,
                    "message": token
                })),
                Err(_) => Json(json!(
                {
                    "success": false,
                    "message": "Internal server error, please try again or contact support"
                })),
            }
        }
    }
}

pub fn mount(rocket: rocket::Rocket) -> rocket::Rocket {
    rocket
        .mount("/api/user", routes![info, info_forbidden])
        .mount("/api/auth", routes![register, login])
}

A  => ulairi-api/src/users/model.rs +123 -0
@@ 1,123 @@
use crate::schema::users;

use diesel;
use diesel::pg::PgConnection;
use diesel::prelude::*;

use serde::{Deserialize, Serialize};

use argon2::{
    password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
    Argon2,
};

#[derive(Serialize, Deserialize, Queryable, AsChangeset)]
#[table_name = "users"]
pub struct User {
    pub id: i32,
    pub username: String,
    pub email: String,
    pub password: String,
}

#[derive(Serialize, Deserialize, Insertable)]
#[table_name = "users"]
pub struct InsertableUser {
    pub username: String,
    pub email: String,
    pub password: String,
}

impl User {
    pub fn create(user: InsertableUser, connection: &PgConnection) -> QueryResult<User> {
        // Generate a salt
        let salt = SaltString::generate(&mut OsRng);

        // Hash the password to PHC string
        let password_hash = Argon2::default()
            .hash_password(user.password.as_bytes(), &salt)
            .unwrap()
            .to_string();

        let encrypted_user = InsertableUser {
            password: password_hash,
            ..user
        };

        // Insert the user into the database
        diesel::insert_into(users::table)
            .values(encrypted_user)
            .execute(connection)?;

        // Query the database for the encrypted user
        users::table.order(users::id.desc()).first(connection)
    }

    pub fn get_by_username_and_password(
        username: String,
        password: String,
        connection: &PgConnection,
    ) -> Option<User> {
        // Query the database for the user
        let res = users::table
            .filter(users::username.eq(username))
            .get_result::<User>(connection);

        // Check if the user exists
        match res {
            Ok(user) => {
                let parsed_hash = PasswordHash::new(&user.password).unwrap();

                // Check if the password is correct
                if Argon2::default()
                    .verify_password(password.as_bytes(), &parsed_hash)
                    .is_ok()
                {
                    Some(user)
                } else {
                    None
                }
            }
            Err(_) => None,
        }
    }

    pub fn get_by_email(email: String, connection: &PgConnection) -> Option<User> {
        // Query the database for the user
        let res = users::table
            .filter(users::email.eq(email))
            .get_result::<User>(connection);

        // Check if the user exists
        match res {
            Ok(user) => Some(user),
            Err(_) => None,
        }
    }

    pub fn email_is_taken(email: String, connection: &PgConnection) -> bool {
        // Query the database for the user
        let res = users::table
            .filter(users::email.eq(email))
            .get_result::<User>(connection);

        // Check if the user exists
        match res {
            Ok(_) => true,
            Err(_) => false,
        }
    }

    pub fn username_is_taken(username: String, connection: &PgConnection) -> bool {
        // Query the database for the user
        let res = users::table
            .filter(users::username.eq(username))
            .get_result::<User>(connection);

        // Check if the user exists
        match res {
            Ok(_) => true,
            Err(_) => false,
        }
    }
}

A  => ulairi-client/Cargo.lock +3030 -0
@@ 1,3030 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "ab_glyph"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24606928a235e73cdef55a0c909719cadd72fce573e5713d58cb2952d8f5794c"
dependencies = [
 "ab_glyph_rasterizer",
 "owned_ttf_parser",
]

[[package]]
name = "ab_glyph_rasterizer"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a13739d7177fbd22bb0ed28badfff9f372f8bef46c863db4e1c6248f6b223b6e"

[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"

[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
 "getrandom",
 "once_cell",
 "serde",
 "version_check",
]

[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
 "memchr",
]

[[package]]
name = "android_glue"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407"

[[package]]
name = "arboard"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc120354d1b5ec6d7aaf4876b602def75595937b5e15d356eb554ab5177e08bb"
dependencies = [
 "clipboard-win",
 "log",
 "objc",
 "objc-foundation",
 "objc_id",
 "parking_lot 0.12.0",
 "thiserror",
 "winapi",
 "x11rb",
]

[[package]]
name = "arrayvec"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"

[[package]]
name = "atomic_refcell"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b5e5f48b927f04e952dedc932f31995a65a0bf65ec971c74436e51bf6e970d"

[[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.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"

[[package]]
name = "bigdecimal"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aaf33151a6429fe9211d1b276eafdf70cdff28b071e76c0b0e1503221ea3744"
dependencies = [
 "num-bigint",
 "num-integer",
 "num-traits",
]

[[package]]
name = "bindgen"
version = "0.59.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8"
dependencies = [
 "bitflags",
 "cexpr",
 "clang-sys",
 "lazy_static",
 "lazycell",
 "peeking_take_while",
 "proc-macro2",
 "quote",
 "regex",
 "rustc-hash",
 "shlex",
]

[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"

[[package]]
name = "bitvec"
version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5237f00a8c86130a0cc317830e558b966dd7850d48a953d998c813f01a41b527"
dependencies = [
 "funty",
 "radium",
 "tap",
 "wyz",
]

[[package]]
name = "block"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"

[[package]]
name = "block-buffer"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
dependencies = [
 "generic-array",
]

[[package]]
name = "bufstream"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8"

[[package]]
name = "bumpalo"
version = "3.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"

[[package]]
name = "bytemuck"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc"
dependencies = [
 "bytemuck_derive",
]

[[package]]
name = "bytemuck_derive"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562e382481975bc61d11275ac5e62a19abd00b0547d99516a415336f183dcd0e"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"

[[package]]
name = "bytes"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"

[[package]]
name = "calloop"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf2eec61efe56aa1e813f5126959296933cf0700030e4314786c48779a66ab82"
dependencies = [
 "log",
 "nix 0.22.3",
]

[[package]]
name = "cc"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"

[[package]]
name = "cesu8"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"

[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
 "nom",
]

[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"

[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"

[[package]]
name = "cgl"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff"
dependencies = [
 "libc",
]

[[package]]
name = "clang-sys"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf6b561dcf059c85bbe388e0a7b0a1469acb3934cc0cfa148613a830629e3049"
dependencies = [
 "glob",
 "libc",
 "libloading",
]

[[package]]
name = "clipboard-win"
version = "4.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f3e1238132dc01f081e1cbb9dace14e5ef4c3a51ee244bd982275fb514605db"
dependencies = [
 "error-code",
 "str-buf",
 "winapi",
]

[[package]]
name = "cmake"
version = "0.1.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a"
dependencies = [
 "cc",
]

[[package]]
name = "cocoa"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832"
dependencies = [
 "bitflags",
 "block",
 "cocoa-foundation",
 "core-foundation 0.9.3",
 "core-graphics 0.22.3",
 "foreign-types",
 "libc",
 "objc",
]

[[package]]
name = "cocoa-foundation"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318"
dependencies = [
 "bitflags",
 "block",
 "core-foundation 0.9.3",
 "core-graphics-types",
 "foreign-types",
 "libc",
 "objc",
]

[[package]]
name = "combine"
version = "4.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948"
dependencies = [
 "bytes",
 "memchr",
]

[[package]]
name = "core-foundation"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
dependencies = [
 "core-foundation-sys 0.7.0",
 "libc",
]

[[package]]
name = "core-foundation"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
dependencies = [
 "core-foundation-sys 0.8.3",
 "libc",
]

[[package]]
name = "core-foundation-sys"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"

[[package]]
name = "core-foundation-sys"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"

[[package]]
name = "core-graphics"
version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923"
dependencies = [
 "bitflags",
 "core-foundation 0.7.0",
 "foreign-types",
 "libc",
]

[[package]]
name = "core-graphics"
version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
dependencies = [
 "bitflags",
 "core-foundation 0.9.3",
 "core-graphics-types",
 "foreign-types",
 "libc",
]

[[package]]
name = "core-graphics-types"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
dependencies = [
 "bitflags",
 "core-foundation 0.9.3",
 "foreign-types",
 "libc",
]

[[package]]
name = "core-video-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828"
dependencies = [
 "cfg-if 0.1.10",
 "core-foundation-sys 0.7.0",
 "core-graphics 0.19.2",
 "libc",
 "objc",
]

[[package]]
name = "cpufeatures"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
dependencies = [
 "libc",
]

[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
 "cfg-if 1.0.0",
]

[[package]]
name = "crossbeam"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845"
dependencies = [
 "cfg-if 1.0.0",
 "crossbeam-channel",
 "crossbeam-deque",
 "crossbeam-epoch",
 "crossbeam-queue",
 "crossbeam-utils",
]

[[package]]
name = "crossbeam-channel"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
dependencies = [
 "cfg-if 1.0.0",
 "crossbeam-utils",
]

[[package]]
name = "crossbeam-deque"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
dependencies = [
 "cfg-if 1.0.0",
 "crossbeam-epoch",
 "crossbeam-utils",
]

[[package]]
name = "crossbeam-epoch"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
dependencies = [
 "autocfg",
 "cfg-if 1.0.0",
 "crossbeam-utils",
 "lazy_static",
 "memoffset",
 "scopeguard",
]

[[package]]
name = "crossbeam-queue"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2"
dependencies = [
 "cfg-if 1.0.0",
 "crossbeam-utils",
]

[[package]]
name = "crossbeam-utils"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
dependencies = [
 "cfg-if 1.0.0",
 "lazy_static",
]

[[package]]
name = "crypto-common"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
dependencies = [
 "generic-array",
 "typenum",
]

[[package]]
name = "cty"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"

[[package]]
name = "darling"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
dependencies = [
 "darling_core",
 "darling_macro",
]

[[package]]
name = "darling_core"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
dependencies = [
 "fnv",
 "ident_case",
 "proc-macro2",
 "quote",
 "strsim",
 "syn",
]

[[package]]
name = "darling_macro"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
dependencies = [
 "darling_core",
 "quote",
 "syn",
]

[[package]]
name = "derive_utils"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "532b4c15dccee12c7044f1fcad956e98410860b22231e44a3b827464797ca7bf"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "digest"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [
 "block-buffer",
 "crypto-common",
]

[[package]]
name = "directories-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc"
dependencies = [
 "cfg-if 1.0.0",
 "dirs-sys-next",
]

[[package]]
name = "dirs-sys-next"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
 "libc",
 "redox_users",
 "winapi",
]

[[package]]
name = "dispatch"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"

[[package]]
name = "dlib"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794"
dependencies = [
 "libloading",
]

[[package]]
name = "dotenv"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"

[[package]]
name = "downcast-rs"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"

[[package]]
name = "eframe"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53fa97a8188c36261ea162e625dbb23599f67b60777b462b834fe38161b81dce"
dependencies = [
 "bytemuck",
 "directories-next",
 "egui",
 "egui-winit",
 "egui_glow",
 "glow",
 "glutin",
 "js-sys",
 "percent-encoding",
 "ron",
 "serde",
 "tracing",
 "wasm-bindgen",
 "wasm-bindgen-futures",
 "web-sys",
 "winit",
]

[[package]]
name = "egui"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb095a8b9feb9b7ff8f00b6776dffcef059538a3f4a91238e03c900e9c9ad9a2"
dependencies = [
 "ahash",
 "epaint",
 "nohash-hasher",
 "ron",
 "serde",
 "tracing",
]

[[package]]
name = "egui-winit"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b040afd583fd95a9b9578d4399214a13d948ed26bc0ff7cc0104502501f34e68"
dependencies = [
 "arboard",
 "egui",
 "instant",
 "serde",
 "tracing",
 "webbrowser",
 "winit",
]

[[package]]
name = "egui_extras"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877bfcce06463cdbcfd7f4efd57608b1384d6d9ae03b33e503fbba1d1a899a52"
dependencies = [
 "egui",
]

[[package]]
name = "egui_glow"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af43ed7ec7199907ab5853c3bb3883ae1e741ab540aa127a798a60b7bdb906f1"
dependencies = [
 "bytemuck",
 "egui",
 "glow",
 "memoffset",
 "tracing",
 "wasm-bindgen",
 "web-sys",
]

[[package]]
name = "emath"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c223f58c7e38abe1770f367b969f1b3fbd4704b67666bcb65dbb1adb0980ba72"
dependencies = [
 "bytemuck",
 "serde",
]

[[package]]
name = "encoding_rs"
version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
dependencies = [
 "cfg-if 1.0.0",
]

[[package]]
name = "epaint"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c29567088888e8ac3e8f61bbb2ddc820207ebb8d69eefde5bcefa06d65e4e89"
dependencies = [
 "ab_glyph",
 "ahash",
 "atomic_refcell",
 "bytemuck",
 "emath",
 "nohash-hasher",
 "parking_lot 0.12.0",
 "serde",
]

[[package]]
name = "error-code"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21"
dependencies = [
 "libc",
 "str-buf",
]

[[package]]
name = "fastrand"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
dependencies = [
 "instant",
]

[[package]]
name = "flate2"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af"
dependencies = [
 "cfg-if 1.0.0",
 "crc32fast",
 "libc",
 "libz-sys",
 "miniz_oxide",
]

[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"

[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
 "foreign-types-shared",
]

[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"

[[package]]
name = "form_urlencoded"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
dependencies = [
 "matches",
 "percent-encoding",
]

[[package]]
name = "frunk"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cd67cf7d54b7e72d0ea76f3985c3747d74aee43e0218ad993b7903ba7a5395e"
dependencies = [
 "frunk_core",
 "frunk_derives",
 "frunk_proc_macros",
]

[[package]]
name = "frunk_core"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1246cf43ec80bf8b2505b5c360b8fb999c97dabd17dbb604d85558d5cbc25482"

[[package]]
name = "frunk_derives"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dbc4f084ec5a3f031d24ccedeb87ab2c3189a2f33b8d070889073837d5ea09e"
dependencies = [
 "frunk_proc_macro_helpers",
 "quote",
 "syn",
]

[[package]]
name = "frunk_proc_macro_helpers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99f11257f106c6753f5ffcb8e601fb39c390a088017aaa55b70c526bff15f63e"
dependencies = [
 "frunk_core",
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "frunk_proc_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a078bd8459eccbb85e0b007b8f756585762a72a9efc53f359b371c3b6351dbcc"
dependencies = [
 "frunk_core",
 "frunk_proc_macros_impl",
 "proc-macro-hack",
]

[[package]]
name = "frunk_proc_macros_impl"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ffba99f0fa4f57e42f57388fbb9a0ca863bc2b4261f3c5570fed579d5df6c32"
dependencies = [
 "frunk_core",
 "frunk_proc_macro_helpers",
 "proc-macro-hack",
 "quote",
 "syn",
]

[[package]]
name = "funty"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1847abb9cb65d566acd5942e94aea9c8f547ad02c98e1649326fc0e8910b8b1e"

[[package]]
name = "futures-channel"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
dependencies = [
 "futures-core",
]

[[package]]
name = "futures-core"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"

[[package]]
name = "futures-sink"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"

[[package]]
name = "futures-task"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"

[[package]]
name = "futures-util"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
dependencies = [
 "futures-core",
 "futures-task",
 "pin-project-lite",
 "pin-utils",
]

[[package]]
name = "generic-array"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
dependencies = [
 "typenum",
 "version_check",
]

[[package]]
name = "gethostname"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e"
dependencies = [
 "libc",
 "winapi",
]

[[package]]
name = "getrandom"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
dependencies = [
 "cfg-if 1.0.0",
 "libc",
 "wasi 0.10.2+wasi-snapshot-preview1",
]

[[package]]
name = "gl_generator"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
dependencies = [
 "khronos_api",
 "log",
 "xml-rs",
]

[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"

[[package]]
name = "glow"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8bd5877156a19b8ac83a29b2306fe20537429d318f3ff0a1a2119f8d9c61919"
dependencies = [
 "js-sys",
 "slotmap",
 "wasm-bindgen",
 "web-sys",
]

[[package]]
name = "glutin"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00ea9dbe544bc8a657c4c4a798c2d16cd01b549820e47657297549d28371f6d2"
dependencies = [
 "android_glue",
 "cgl",
 "cocoa",
 "core-foundation 0.9.3",
 "glutin_egl_sys",
 "glutin_emscripten_sys",
 "glutin_gles2_sys",
 "glutin_glx_sys",
 "glutin_wgl_sys",
 "lazy_static",
 "libloading",
 "log",
 "objc",
 "osmesa-sys",
 "parking_lot 0.11.2",
 "wayland-client",
 "wayland-egl",
 "winapi",
 "winit",
]

[[package]]
name = "glutin_egl_sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2abb6aa55523480c4adc5a56bbaa249992e2dddb2fc63dc96e04a3355364c211"
dependencies = [
 "gl_generator",
 "winapi",
]

[[package]]
name = "glutin_emscripten_sys"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80de4146df76e8a6c32b03007bc764ff3249dcaeb4f675d68a06caf1bac363f1"

[[package]]
name = "glutin_gles2_sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8094e708b730a7c8a1954f4f8a31880af00eb8a1c5b5bf85d28a0a3c6d69103"
dependencies = [
 "gl_generator",
 "objc",
]

[[package]]
name = "glutin_glx_sys"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e393c8fc02b807459410429150e9c4faffdb312d59b8c038566173c81991351"
dependencies = [
 "gl_generator",
 "x11-dl",
]

[[package]]
name = "glutin_wgl_sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3da5951a1569dbab865c6f2a863efafff193a93caf05538d193e9e3816d21696"
dependencies = [
 "gl_generator",
]

[[package]]
name = "h2"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57"
dependencies = [
 "bytes",
 "fnv",
 "futures-core",
 "futures-sink",
 "futures-util",
 "http",
 "indexmap",
 "slab",
 "tokio",
 "tokio-util",
 "tracing",
]

[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
 "ahash",
]

[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
 "libc",
]

[[package]]
name = "http"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb"
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.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c"

[[package]]
name = "httpdate"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"

[[package]]
name = "hyper"
version = "0.14.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2"
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-tls"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
 "bytes",
 "hyper",
 "native-tls",
 "tokio",
 "tokio-native-tls",
]

[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"

[[package]]
name = "idna"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
dependencies = [
 "matches",
 "unicode-bidi",
 "unicode-normalization",
]

[[package]]
name = "indexmap"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
dependencies = [
 "autocfg",
 "hashbrown",
]

[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
 "cfg-if 1.0.0",
 "js-sys",
 "wasm-bindgen",
 "web-sys",
]

[[package]]
name = "io-enum"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03e3306b0f260aad2872563eb0d5d1a59f2420fad270a661dce59a01e92d806b"
dependencies = [
 "autocfg",
 "derive_utils",
 "quote",
 "syn",
]

[[package]]
name = "ipnet"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"

[[package]]
name = "itoa"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"

[[package]]
name = "jni"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec"
dependencies = [
 "cesu8",
 "combine",
 "jni-sys",
 "log",
 "thiserror",
 "walkdir",
]

[[package]]
name = "jni-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"

[[package]]
name = "js-sys"
version = "0.3.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397"
dependencies = [
 "wasm-bindgen",
]

[[package]]
name = "khronos_api"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"

[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"

[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"

[[package]]
name = "lexical"
version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7aefb36fd43fef7003334742cbf77b243fcd36418a1d1bdd480d613a67968f6"
dependencies = [
 "lexical-core",
]

[[package]]
name = "lexical-core"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46"
dependencies = [
 "lexical-parse-float",
 "lexical-parse-integer",
 "lexical-util",
 "lexical-write-float",
 "lexical-write-integer",
]

[[package]]
name = "lexical-parse-float"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f"
dependencies = [
 "lexical-parse-integer",
 "lexical-util",
 "static_assertions",
]

[[package]]
name = "lexical-parse-integer"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "125e1f93e5003d4bd89758c2ca2771bfae13632df633cde581efe07c87d354e5"
dependencies = [
 "lexical-util",
 "static_assertions",
]

[[package]]
name = "lexical-util"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc"
dependencies = [
 "static_assertions",
]

[[package]]
name = "lexical-write-float"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862"
dependencies = [
 "lexical-util",
 "lexical-write-integer",
 "static_assertions",
]

[[package]]
name = "lexical-write-integer"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446"
dependencies = [
 "lexical-util",
 "static_assertions",
]

[[package]]
name = "libc"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"

[[package]]
name = "libloading"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
dependencies = [
 "cfg-if 1.0.0",
 "winapi",
]

[[package]]
name = "libz-sys"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e7e15d7610cce1d9752e137625f14e61a28cd45929b6e12e47b50fe154ee2e"
dependencies = [
 "cc",
 "pkg-config",
 "vcpkg",
]

[[package]]
name = "load-dotenv"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71abcf6d8da2a6c163554a61cdbca2438bbe9e687a9c5ebeee12e9af42864b54"
dependencies = [
 "dotenv",
]

[[package]]
name = "lock_api"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
dependencies = [
 "autocfg",
 "scopeguard",
]

[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
 "cfg-if 1.0.0",
]

[[package]]
name = "lru"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32613e41de4c47ab04970c348ca7ae7382cf116625755af070b008a15516a889"
dependencies = [
 "hashbrown",
]

[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
dependencies = [
 "libc",
]

[[package]]
name = "matches"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"

[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"

[[package]]
name = "memmap2"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357"
dependencies = [
 "libc",
]

[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
 "autocfg",
]

[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"

[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"

[[package]]
name = "miniz_oxide"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082"
dependencies = [
 "adler",
]

[[package]]
name = "mio"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799"
dependencies = [
 "libc",
 "log",
 "wasi 0.11.0+wasi-snapshot-preview1",
 "windows-sys",
]

[[package]]
name = "mysql"
version = "22.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e6f8658d54e3c294d94a741a5f414c21431f56f253b5dc2e2582941d834b3a9"
dependencies = [
 "bufstream",
 "bytes",
 "crossbeam",
 "flate2",
 "io-enum",
 "libc",
 "lru",
 "mysql_common",
 "named_pipe",
 "native-tls",
 "nix 0.23.1",
 "once_cell",
 "pem",
 "percent-encoding",
 "serde",
 "serde_json",
 "socket2",
 "twox-hash",
 "url",
]

[[package]]
name = "mysql_common"
version = "0.28.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4140827f2d12750de1e8755442577e4292a835f26ff2f659f0a380d1d71020b0"
dependencies = [
 "base64",
 "bigdecimal",
 "bindgen",
 "bitflags",
 "bitvec",
 "byteorder",
 "bytes",
 "cc",
 "cmake",
 "crc32fast",
 "flate2",
 "frunk",
 "lazy_static",
 "lexical",
 "num-bigint",
 "num-traits",
 "rand",
 "regex",
 "rust_decimal",
 "saturating",
 "serde",
 "serde_json",
 "sha-1",
 "sha2",
 "smallvec",
 "subprocess",
 "thiserror",
 "time",
 "uuid",
]

[[package]]
name = "named_pipe"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad9c443cce91fc3e12f017290db75dde490d685cdaaf508d7159d7cf41f0eb2b"
dependencies = [
 "winapi",
]

[[package]]
name = "native-tls"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9"
dependencies = [
 "lazy_static",
 "libc",
 "log",
 "openssl",
 "openssl-probe",
 "openssl-sys",
 "schannel",
 "security-framework",
 "security-framework-sys",
 "tempfile",
]

[[package]]
name = "ndk"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d868f654c72e75f8687572699cdabe755f03effbb62542768e995d5b8d699d"
dependencies = [
 "bitflags",
 "jni-sys",
 "ndk-sys 0.2.2",
 "num_enum",
 "thiserror",
]

[[package]]
name = "ndk"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4"
dependencies = [
 "bitflags",
 "jni-sys",
 "ndk-sys 0.3.0",
 "num_enum",
 "thiserror",
]

[[package]]
name = "ndk-context"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"

[[package]]
name = "ndk-glue"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c71bee8ea72d685477e28bd004cfe1bf99c754d688cd78cad139eae4089484d4"
dependencies = [
 "lazy_static",
 "libc",
 "log",
 "ndk 0.5.0",
 "ndk-context",
 "ndk-macro",
 "ndk-sys 0.2.2",
]

[[package]]
name = "ndk-glue"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d0c4a7b83860226e6b4183edac21851f05d5a51756e97a1144b7f5a6b63e65f"
dependencies = [
 "lazy_static",
 "libc",
 "log",
 "ndk 0.6.0",
 "ndk-context",
 "ndk-macro",
 "ndk-sys 0.3.0",
]

[[package]]
name = "ndk-macro"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c"
dependencies = [
 "darling",
 "proc-macro-crate",
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "ndk-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121"

[[package]]
name = "ndk-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97"
dependencies = [
 "jni-sys",
]

[[package]]
name = "nix"
version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf"
dependencies = [
 "bitflags",
 "cc",
 "cfg-if 1.0.0",
 "libc",
 "memoffset",
]

[[package]]
name = "nix"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
dependencies = [
 "bitflags",
 "cc",
 "cfg-if 1.0.0",
 "libc",
 "memoffset",
]

[[package]]
name = "nohash-hasher"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"

[[package]]
name = "nom"
version = "7.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
dependencies = [
 "memchr",
 "minimal-lexical",
]

[[package]]
name = "num-bigint"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
dependencies = [
 "autocfg",
 "num-integer",
 "num-traits",
]

[[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_cpus"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
 "hermit-abi",
 "libc",
]

[[package]]
name = "num_enum"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9"
dependencies = [
 "num_enum_derive",
]

[[package]]
name = "num_enum_derive"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
dependencies = [
 "proc-macro-crate",
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "num_threads"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
dependencies = [
 "libc",
]

[[package]]
name = "objc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
 "malloc_buf",
]

[[package]]
name = "objc-foundation"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
dependencies = [
 "block",
 "objc",
 "objc_id",
]

[[package]]
name = "objc_id"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
dependencies = [
 "objc",
]

[[package]]
name = "once_cell"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"

[[package]]
name = "openssl"
version = "0.10.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e"
dependencies = [
 "bitflags",
 "cfg-if 1.0.0",
 "foreign-types",
 "libc",
 "once_cell",
 "openssl-macros",
 "openssl-sys",
]

[[package]]
name = "openssl-macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"

[[package]]
name = "openssl-sys"
version = "0.9.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5fd19fb3e0a8191c1e34935718976a3e70c112ab9a24af6d7cadccd9d90bc0"
dependencies = [
 "autocfg",
 "cc",
 "libc",
 "pkg-config",
 "vcpkg",
]

[[package]]
name = "osmesa-sys"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b"
dependencies = [
 "shared_library",
]

[[package]]
name = "owned_ttf_parser"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb1e509cfe7a12db2a90bfa057dfcdbc55a347f5da677c506b53dd099cfec9d"
dependencies = [
 "ttf-parser",
]

[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
 "instant",
 "lock_api",
 "parking_lot_core 0.8.5",
]

[[package]]
name = "parking_lot"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
dependencies = [
 "lock_api",
 "parking_lot_core 0.9.3",
]

[[package]]
name = "parking_lot_core"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [
 "cfg-if 1.0.0",
 "instant",
 "libc",
 "redox_syscall",
 "smallvec",
 "winapi",
]

[[package]]
name = "parking_lot_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
dependencies = [
 "cfg-if 1.0.0",
 "libc",
 "redox_syscall",
 "smallvec",
 "windows-sys",
]

[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"

[[package]]
name = "pem"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9a3b09a20e374558580a4914d3b7d89bd61b954a5a5e1dcbea98753addb1947"
dependencies = [
 "base64",
]

[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"

[[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 = "pkg-config"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"

[[package]]
name = "poll-promise"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "260817b339544e5b23d4bb66d4522d89dd64af88d16b6dcd7b2a2409ee2e095d"
dependencies = [
 "static_assertions",
 "tokio",
]

[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"

[[package]]
name = "proc-macro-crate"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a"
dependencies = [
 "thiserror",
 "toml",
]

[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"

[[package]]
name = "proc-macro2"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
dependencies = [
 "unicode-ident",
]

[[package]]
name = "quote"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
 "proc-macro2",
]

[[package]]
name = "radium"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb"

[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
 "libc",
 "rand_chacha",
 "rand_core",
]

[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
 "ppv-lite86",
 "rand_core",
]

[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
 "getrandom",
]

[[package]]
name = "raw-window-handle"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41"
dependencies = [
 "cty",
]

[[package]]
name = "redox_syscall"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
dependencies = [
 "bitflags",
]

[[package]]
name = "redox_users"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
 "getrandom",
 "redox_syscall",
 "thiserror",
]

[[package]]
name = "regex"
version = "1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
dependencies = [
 "aho-corasick",
 "memchr",
 "regex-syntax",
]

[[package]]
name = "regex-syntax"
version = "0.6.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"

[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
 "winapi",
]

[[package]]
name = "reqwest"
version = "0.11.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb"
dependencies = [
 "base64",
 "bytes",
 "encoding_rs",
 "futures-core",
 "futures-util",
 "h2",
 "http",
 "http-body",
 "hyper",
 "hyper-tls",
 "ipnet",
 "js-sys",
 "lazy_static",
 "log",
 "mime",
 "native-tls",
 "percent-encoding",
 "pin-project-lite",
 "serde",
 "serde_json",
 "serde_urlencoded",
 "tokio",
 "tokio-native-tls",
 "url",
 "wasm-bindgen",
 "wasm-bindgen-futures",
 "web-sys",
 "winreg",
]

[[package]]
name = "ron"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b861ecaade43ac97886a512b360d01d66be9f41f3c61088b42cedf92e03d678"
dependencies = [
 "base64",
 "bitflags",
 "serde",
]

[[package]]
name = "rust_decimal"
version = "1.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22dc69eadbf0ee2110b8d20418c0c6edbaefec2811c4963dc17b6344e11fe0f8"
dependencies = [
 "arrayvec",
 "num-traits",
 "serde",
]

[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"

[[package]]
name = "ryu"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"

[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
 "winapi-util",
]

[[package]]
name = "saturating"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ece8e78b2f38ec51c51f5d475df0a7187ba5111b2a28bdc761ee05b075d40a71"

[[package]]
name = "schannel"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
dependencies = [
 "lazy_static",
 "windows-sys",
]

[[package]]
name = "scoped-tls"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"

[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"

[[package]]
name = "security-framework"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc"
dependencies = [
 "bitflags",
 "core-foundation 0.9.3",
 "core-foundation-sys 0.8.3",
 "libc",
 "security-framework-sys",
]

[[package]]
name = "security-framework-sys"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
dependencies = [
 "core-foundation-sys 0.8.3",
 "libc",
]

[[package]]
name = "serde"
version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
dependencies = [
 "serde_derive",
]

[[package]]
name = "serde_derive"
version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "serde_json"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
dependencies = [
 "itoa",
 "ryu",
 "serde",
]

[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
 "form_urlencoded",
 "itoa",
 "ryu",
 "serde",
]

[[package]]
name = "sha-1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
dependencies = [
 "cfg-if 1.0.0",
 "cpufeatures",
 "digest",
]

[[package]]
name = "sha2"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
dependencies = [
 "cfg-if 1.0.0",
 "cpufeatures",
 "digest",
]

[[package]]
name = "shared_library"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11"
dependencies = [
 "lazy_static",
 "libc",
]

[[package]]
name = "shlex"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"

[[package]]
name = "signal-hook-registry"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
dependencies = [
 "libc",
]

[[package]]
name = "slab"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"

[[package]]
name = "slotmap"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
dependencies = [
 "version_check",
]

[[package]]
name = "smallvec"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"

[[package]]
name = "smithay-client-toolkit"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a28f16a97fa0e8ce563b2774d1e732dd5d4025d2772c5dba0a41a0f90a29da3"
dependencies = [
 "bitflags",
 "calloop",
 "dlib",
 "lazy_static",
 "log",
 "memmap2",
 "nix 0.22.3",
 "pkg-config",
 "wayland-client",
 "wayland-cursor",
 "wayland-protocols",
]

[[package]]
name = "socket2"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
dependencies = [
 "libc",
 "winapi",
]

[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"

[[package]]
name = "str-buf"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a"

[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"

[[package]]
name = "subprocess"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086"
dependencies = [
 "libc",
 "winapi",
]

[[package]]
name = "syn"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
dependencies = [
 "proc-macro2",
 "quote",
 "unicode-ident",
]

[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"

[[package]]
name = "tempfile"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
 "cfg-if 1.0.0",
 "fastrand",
 "libc",
 "redox_syscall",
 "remove_dir_all",
 "winapi",
]

[[package]]
name = "thiserror"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
dependencies = [
 "thiserror-impl",
]

[[package]]
name = "thiserror-impl"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "time"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd"
dependencies = [
 "libc",
 "num_threads",
]

[[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.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"

[[package]]
name = "tokio"
version = "1.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395"
dependencies = [
 "bytes",
 "libc",
 "memchr",
 "mio",
 "num_cpus",
 "once_cell",
 "parking_lot 0.12.0",
 "pin-project-lite",
 "signal-hook-registry",
 "socket2",
 "tokio-macros",
 "winapi",
]

[[package]]
name = "tokio-macros"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "tokio-native-tls"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
dependencies = [
 "native-tls",
 "tokio",
]

[[package]]
name = "tokio-util"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c"
dependencies = [
 "bytes",
 "futures-core",
 "futures-sink",
 "pin-project-lite",
 "tokio",
 "tracing",
]

[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
 "serde",
]

[[package]]
name = "tower-service"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"

[[package]]
name = "tracing"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09"
dependencies = [
 "cfg-if 1.0.0",
 "pin-project-lite",
 "tracing-attributes",
 "tracing-core",
]

[[package]]
name = "tracing-attributes"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "tracing-core"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f"
dependencies = [
 "lazy_static",
]

[[package]]
name = "try-lock"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"

[[package]]
name = "ttf-parser"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c74c96594835e10fa545e2a51e8709f30b173a092bfd6036ef2cec53376244f3"

[[package]]
name = "twox-hash"
version = "1.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
dependencies = [
 "cfg-if 1.0.0",
 "rand",
 "static_assertions",
]

[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"

[[package]]
name = "ulairi-client"
version = "0.1.0"
dependencies = [
 "eframe",
 "egui_extras",
 "load-dotenv",
 "mysql",
 "poll-promise",
 "regex",
 "reqwest",
 "serde",
 "tokio",
]

[[package]]
name = "unicode-bidi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"

[[package]]
name = "unicode-ident"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"

[[package]]
name = "unicode-normalization"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
dependencies = [
 "tinyvec",
]

[[package]]
name = "url"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
dependencies = [
 "form_urlencoded",
 "idna",
 "matches",
 "percent-encoding",
]

[[package]]
name = "uuid"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"

[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"

[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"

[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
dependencies = [
 "same-file",
 "winapi",
 "winapi-util",
]

[[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.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"

[[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.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad"
dependencies = [
 "cfg-if 1.0.0",
 "wasm-bindgen-macro",
]

[[package]]
name = "wasm-bindgen-backend"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4"
dependencies = [
 "bumpalo",
 "lazy_static",
 "log",
 "proc-macro2",
 "quote",
 "syn",
 "wasm-bindgen-shared",
]

[[package]]
name = "wasm-bindgen-futures"
version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2"
dependencies = [
 "cfg-if 1.0.0",
 "js-sys",
 "wasm-bindgen",
 "web-sys",
]

[[package]]
name = "wasm-bindgen-macro"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5"
dependencies = [
 "quote",
 "wasm-bindgen-macro-support",
]

[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
 "wasm-bindgen-backend",
 "wasm-bindgen-shared",
]

[[package]]
name = "wasm-bindgen-shared"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"

[[package]]
name = "wayland-client"
version = "0.29.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91223460e73257f697d9e23d401279123d36039a3f7a449e983f123292d4458f"
dependencies = [
 "bitflags",
 "downcast-rs",
 "libc",
 "nix 0.22.3",
 "scoped-tls",
 "wayland-commons",
 "wayland-scanner",
 "wayland-sys",
]

[[package]]
name = "wayland-commons"
version = "0.29.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94f6e5e340d7c13490eca867898c4cec5af56c27a5ffe5c80c6fc4708e22d33e"
dependencies = [
 "nix 0.22.3",
 "once_cell",
 "smallvec",
 "wayland-sys",
]

[[package]]
name = "wayland-cursor"
version = "0.29.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c52758f13d5e7861fc83d942d3d99bf270c83269575e52ac29e5b73cb956a6bd"
dependencies = [
 "nix 0.22.3",
 "wayland-client",
 "xcursor",
]

[[package]]
name = "wayland-egl"
version = "0.29.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83281d69ee162b59031c666385e93bde4039ec553b90c4191cdb128ceea29a3a"
dependencies = [
 "wayland-client",
 "wayland-sys",
]

[[package]]
name = "wayland-protocols"
version = "0.29.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60147ae23303402e41fe034f74fb2c35ad0780ee88a1c40ac09a3be1e7465741"
dependencies = [
 "bitflags",
 "wayland-client",
 "wayland-commons",
 "wayland-scanner",
]

[[package]]
name = "wayland-scanner"
version = "0.29.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39a1ed3143f7a143187156a2ab52742e89dac33245ba505c17224df48939f9e0"
dependencies = [
 "proc-macro2",
 "quote",
 "xml-rs",
]

[[package]]
name = "wayland-sys"
version = "0.29.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9341df79a8975679188e37dab3889bfa57c44ac2cb6da166f519a81cbe452d4"
dependencies = [
 "dlib",
 "lazy_static",
 "pkg-config",
]

[[package]]
name = "web-sys"
version = "0.3.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283"
dependencies = [
 "js-sys",
 "wasm-bindgen",
]

[[package]]
name = "webbrowser"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc6a3cffdb686fbb24d9fb8f03a213803277ed2300f11026a3afe1f108dc021b"
dependencies = [
 "jni",
 "ndk-glue 0.6.2",
 "url",
 "web-sys",
 "widestring",
 "winapi",
]

[[package]]
name = "widestring"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983"

[[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-wsapoll"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e"
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-sys"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
 "windows_aarch64_msvc",
 "windows_i686_gnu",
 "windows_i686_msvc",
 "windows_x86_64_gnu",
 "windows_x86_64_msvc",
]

[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"

[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"

[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"

[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"

[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"

[[package]]
name = "winit"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b43cc931d58b99461188607efd7acb2a093e65fc621f54cad78517a6063e73a"
dependencies = [
 "bitflags",
 "cocoa",
 "core-foundation 0.9.3",
 "core-graphics 0.22.3",
 "core-video-sys",
 "dispatch",
 "instant",
 "lazy_static",
 "libc",
 "log",
 "mio",
 "ndk 0.5.0",
 "ndk-glue 0.5.2",
 "ndk-sys 0.2.2",
 "objc",
 "parking_lot 0.11.2",
 "percent-encoding",
 "raw-window-handle",
 "smithay-client-toolkit",
 "wasm-bindgen",
 "wayland-client",
 "wayland-protocols",
 "web-sys",
 "winapi",
 "x11-dl",
]

[[package]]
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
 "winapi",
]

[[package]]
name = "wyz"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "129e027ad65ce1453680623c3fb5163cbf7107bfe1aa32257e7d0e63f9ced188"
dependencies = [
 "tap",
]

[[package]]
name = "x11-dl"
version = "2.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea26926b4ce81a6f5d9d0f3a0bc401e5a37c6ae14a1bfaa8ff6099ca80038c59"
dependencies = [
 "lazy_static",
 "libc",
 "pkg-config",
]

[[package]]
name = "x11rb"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e99be55648b3ae2a52342f9a870c0e138709a3493261ce9b469afe6e4df6d8a"
dependencies = [
 "gethostname",
 "nix 0.22.3",
 "winapi",
 "winapi-wsapoll",
]

[[package]]
name = "xcursor"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7"
dependencies = [
 "nom",
]

[[package]]
name = "xml-rs"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"

A  => ulairi-client/Cargo.toml +23 -0
@@ 1,23 @@
[package]
name = "ulairi-client"
version = "0.1.0"
authors = ["Jonni Liljamo <jonni@liljamo.com>"]
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
eframe = { version = "0.18.0", features = ["persistence"] }
egui_extras = "0.18.0"

serde = { version = "1", features = ["derive"], optional = false }

mysql = "22.1.0"

regex = "1"

reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
poll-promise = { version = "0.1.0", features = ["tokio"] }

load-dotenv = "0.1.2"

A  => ulairi-client/Projektisuunnitelma.odt +0 -0
A  => ulairi-client/README.md +5 -0
@@ 1,5 @@
# Úlairi Client

Edit .env:
 * WEB_API_ADDRESS = The address of the web api.


A  => ulairi-client/src/app.rs +491 -0
@@ 1,491 @@
use eframe::egui;
use egui_extras::{Size, TableBuilder};

use poll_promise::Promise;
use regex::Regex;

use load_dotenv::load_dotenv;
load_dotenv!();

mod api;
mod widget;

enum LoginState {
    LoggedOut,
    LoggingIn,
    Registering,
}

struct InputFields {
    username: String,
    email: String,
    password: String,
    hours: i32,
    date: String,
}

struct PopupDetails {
    visible: bool,
    title: String,
    message: String,
}

struct UserDetails {
    api_key: String,
    email: String,
}

struct Promises {
    login: Promise<api::AuthResponse>,
    register: Promise<api::AuthResponse>,
    user_info: Promise<api::UserInfoResponse>,
    hour_entries: Promise<Vec<api::HourEntry>>,
    insert_hour_entry: Promise<api::HourEntry>,
}

struct PromisesStates {
    login_waiting: bool,
    register_waiting: bool,
    user_info_waiting: bool,
    hour_entries_waiting: bool,
    insert_hour_entry_waiting: bool,
}

/// We derive Deserialize/Serialize so we can persist app state on shutdown.
#[derive(serde::Deserialize, serde::Serialize)]
#[serde(default)] // if we add new fields, give them default values when deserializing old state
pub struct App {
    #[serde(skip)]
    api_address: String,

    #[serde(skip)]
    user: UserDetails,

    #[serde(skip)]
    login_state: LoginState,

    #[serde(skip)]
    popup_details: PopupDetails,

    #[serde(skip)]
    input: InputFields,

    #[serde(skip)]
    promise: Promises,

    #[serde(skip)]
    promise_state: PromisesStates,

    #[serde(skip)]
    logged_in: bool,

    #[serde(skip)]
    hour_entries: Vec<api::HourEntry>,

    #[serde(skip)]
    adding_hour_entry: bool,
}

impl Default for App {
    fn default() -> Self {
        Self {
            api_address: env!("WEB_API_ADDRESS").to_string(),
            user: UserDetails {
                api_key: String::new(),
                email: String::new(),
            },
            login_state: LoginState::LoggedOut,
            popup_details: PopupDetails {
                visible: false,
                title: String::new(),
                message: String::new(),
            },
            input: InputFields {
                username: String::new(),
                email: String::new(),
                password: String::new(),
                hours: 0,
                date: String::new(),
            },
            promise: Promises {
                login: Promise::from_ready(api::AuthResponse::default()),
                register: Promise::from_ready(api::AuthResponse::default()),
                user_info: Promise::from_ready(api::UserInfoResponse::default()),
                hour_entries: Promise::from_ready(vec![]),
                insert_hour_entry: Promise::from_ready(api::HourEntry::default()),
            },
            promise_state: PromisesStates {
                login_waiting: false,
                register_waiting: false,
                user_info_waiting: false,
                hour_entries_waiting: false,
                insert_hour_entry_waiting: false,
            },
            logged_in: false,
            hour_entries: Vec::new(),
            adding_hour_entry: false,
        }
    }
}

impl App {
    // Called once before the first frame.
    pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
        // Load previous app state (if any).
        if let Some(storage) = cc.storage {
            return eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default();
        }

        Default::default()
    }
}

impl eframe::App for App {
    /// Called by the frame work to save state before shutdown.
    fn save(&mut self, storage: &mut dyn eframe::Storage) {
        eframe::set_value(storage, eframe::APP_KEY, self);
    }

    /// Called each time the UI needs repainting, which may be many times per second.
    fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
        let Self {
            api_address,
            user,
            login_state,
            popup_details,
            input,
            promise,
            promise_state,
            logged_in,
            hour_entries,
            adding_hour_entry,
        } = self;

        egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
            // The top panel is often a good place for a menu bar:
            egui::menu::bar(ui, |ui| {
                ui.menu_button("File", |ui| {
                    if ui.button("Quit").clicked() {
                        frame.quit();
                    }
                });
            });
        });

        egui::SidePanel::left("side_panel").show(ctx, |ui| {
            if *logged_in {
                if promise_state.user_info_waiting {
                    ui.label("Loading user info...");

                    if let Some(result) = promise.user_info.ready() {
                        user.email = result.message.clone();

                        promise_state.user_info_waiting = false;
                    }
                } else {
                    ui.heading(format!("Logged in as: {}", user.email));
                }

                if ui.button("New hour entry").clicked() {
                    *adding_hour_entry = true;
                }

                ui.with_layout(egui::Layout::bottom_up(egui::Align::Center), |ui| {
                    if ui.button("Logout").clicked() {
                        *logged_in = false;
                        user.api_key = String::new();
                        *hour_entries = Vec::new();
                    }
                });
            } else {
                ui.heading("Not logged in");
            }
        });

        egui::CentralPanel::default().show(ctx, |ui| {
            // The central panel the region left after adding TopPanel's and SidePanel's

            if *logged_in {
                if promise_state.hour_entries_waiting {
                    ui.heading("Loading...");

                    // Repaint
                    ctx.request_repaint();

                    if let Some(result) = promise.hour_entries.ready() {
                        *hour_entries = result.to_vec();
                        promise_state.hour_entries_waiting = false;
                    }
                } else {
                    egui::ScrollArea::vertical().show(ui, |ui| {
                        TableBuilder::new(ui)
                            .striped(true)
                            .cell_layout(
                                egui::Layout::left_to_right().with_cross_align(egui::Align::Center),
                            )
                            .column(Size::initial(60.0).at_least(40.0))
                            .column(Size::initial(60.0).at_least(40.0))
                            .column(Size::initial(60.0).at_least(40.0))
                            .column(Size::remainder().at_least(60.0))
                            .resizable(true)
                            .header(20.0, |mut header| {
                                header.col(|ui| {
                                    ui.heading("Hours");
                                });
                                header.col(|ui| {
                                    ui.heading("Date Worked");
                                });
                                header.col(|ui| {
                                    ui.heading("Date Entered");
                                });
                                header.col(|ui| {
                                    ui.heading("Actions");
                                });
                            })
                            .body(|mut body| {
                                for entry in hour_entries.clone() {
                                    body.row(15.0, |mut row| {
                                        row.col(|ui| {
                                            ui.label(entry.hours.to_string());
                                        });
                                        row.col(|ui| {
                                            ui.label(entry.date_worked.to_string());
                                        });
                                        row.col(|ui| {
                                            ui.label(entry.date_entered.to_string());
                                        });
                                        row.col(|ui| {
                                            if ui.button("Delete").clicked() {
                                                let id = entry.id.clone();
                                                let api_key = user.api_key.clone();
                                                let api_addr = api_address.clone();

                                                let _delete_promise =
                                                    Promise::spawn_async(async move {
                                                        api::delete_hour_entry(
                                                            id, api_key, api_addr,
                                                        )
                                                        .await
                                                    });

                                                hour_entries.remove(
                                                    hour_entries
                                                        .clone()
                                                        .iter()
                                                        .position(|x| x.id == id)
                                                        .unwrap(),
                                                );
                                            }
                                        });
                                    });
                                }
                            });
                    });
                }
            }
        });

        if *adding_hour_entry && *logged_in {
            egui::Window::new("New time entry").show(ctx, |ui| {
                ui.label("Hours worked:");
                ui.add(egui::Slider::new(&mut input.hours, 0..=24).suffix("h"));
                ui.label("Date worked in YYYY-MM-DD format:");
                ui.text_edit_singleline(&mut input.date);

                if ui.button("Save").clicked() {
                    if Regex::new(r"^\d{4}\-(0?[1-9]|1[012])\-(0?[1-9]|[12][0-9]|3[01])$")
                        .unwrap()
                        .is_match(&mut input.date)
                    {
                        promise_state.insert_hour_entry_waiting = true;

                        let hours = input.hours.clone();
                        let date_worked = input.date.clone();
                        let api_key = user.api_key.clone();
                        let api_addr = api_address.clone();

                        promise.insert_hour_entry = Promise::spawn_async(async move {
                            api::insert_hour_entry(hours, date_worked, api_key, api_addr).await
                        });

                        *adding_hour_entry = false;
                    } else {
                        popup_details.visible = true;
                        popup_details.title = "Invalid date".to_owned();
                        popup_details.message = "Date format is YYYY-MM-DD".to_owned();
                    }
                }

                if ui.button("Cancel").clicked() {
                    *adding_hour_entry = false;
                }
            });
        } else if promise_state.insert_hour_entry_waiting && *logged_in {
            // Repaint
            ctx.request_repaint();

            if let Some(result) = promise.insert_hour_entry.ready() {
                self.hour_entries.push(result.clone());
                promise_state.insert_hour_entry_waiting = false;
            }
        }

        if !*logged_in {
            egui::Window::new("Login / Register")
                .title_bar(false)
                .resizable(false)
                .show(ctx, |ui| match *login_state {
                    LoginState::LoggedOut => {
                        ui.label("Welcome! Please, login or register.");

                        ui.horizontal(|ui| {
                            if ui.button("Register").clicked() {
                                *login_state = LoginState::Registering;
                            }

                            if ui.button("Login").clicked() {
                                *login_state = LoginState::LoggingIn;
                            }
                        });
                    }
                    LoginState::LoggingIn => {
                        ui.horizontal(|ui| {
                            ui.label("Username:");
                            ui.text_edit_singleline(&mut input.username);
                        });

                        ui.horizontal(|ui| {
                            ui.label("Password:");
                            ui.add(widget::password::password(&mut input.password));
                        });

                        ui.add_enabled_ui(!promise_state.login_waiting, |ui| {
                            ui.horizontal(|ui| {
                                if ui.button("Back").clicked() {
                                    *login_state = LoginState::LoggedOut;
                                }

                                if ui.button("Login").clicked() {
                                    promise_state.login_waiting = true;

                                    let username = input.username.clone();
                                    let password = input.password.clone();
                                    let api_addr = api_address.clone();

                                    promise.login = Promise::spawn_async(async {
                                        api::auth(username, password, api_addr).await
                                    });

                                    input.password = String::new();
                                }
                            });
                        });

                        if promise_state.login_waiting {
                            // Repaint
                            ctx.request_repaint();

                            if let Some(result) = promise.login.ready() {
                                if result.success {
                                    user.api_key = result.message.clone();

                                    // TODO: There has to be another way to do this, right? Here I have to make the temporary values twice...
                                    // NOTE: It's a problem with the lifetime of the variables when using them in async functions, while the main program is not async.
                                    let api_key = user.api_key.clone();
                                    let api_addr = api_address.clone();
                                    promise.hour_entries = Promise::spawn_async(async {
                                        api::get_hour_entries(api_key, api_addr).await
                                    });
                                    promise_state.hour_entries_waiting = true;

                                    let api_key = user.api_key.clone();
                                    let api_addr = api_address.clone();
                                    promise.user_info = Promise::spawn_async(async {
                                        api::get_user_info(api_key, api_addr).await
                                    });
                                    promise_state.user_info_waiting = true;

                                    *logged_in = true;
                                } else {
                                    popup_details.visible = true;
                                    popup_details.title = "Login failed".to_owned();
                                    popup_details.message = result.message.clone();
                                }

                                promise_state.login_waiting = false;
                            }
                        }
                    }
                    LoginState::Registering => {
                        ui.horizontal(|ui| {
                            ui.label("Username:");
                            ui.text_edit_singleline(&mut input.username);
                        });

                        ui.horizontal(|ui| {
                            ui.label("Email:");
                            ui.text_edit_singleline(&mut input.email);
                        });

                        ui.horizontal(|ui| {
                            ui.label("Password:");
                            ui.add(widget::password::password(&mut input.password));
                        });

                        ui.add_enabled_ui(!promise_state.register_waiting, |ui| {
                            ui.horizontal(|ui| {
                                if ui.button("Back").clicked() {
                                    *login_state = LoginState::LoggedOut;
                                }

                                if ui.button("Register").clicked() {
                                    promise_state.register_waiting = true;

                                    let username = input.username.clone();
                                    let email = input.email.clone();
                                    let password = input.password.clone();
                                    let api_addr = api_address.clone();

                                    promise.register = Promise::spawn_async(async {
                                        api::register(username, email, password, api_addr).await
                                    });

                                    input.password = String::new();
                                }
                            });
                        });

                        if promise_state.register_waiting {
                            // Repaint
                            ctx.request_repaint();

                            if let Some(result) = promise.register.ready() {
                                if result.success {
                                    popup_details.visible = true;
                                    popup_details.title = "Successfully registered!".to_owned();
                                    popup_details.message = result.message.clone();
                                } else {
                                    popup_details.visible = true;
                                    popup_details.title = "Registration failed".to_owned();
                                    popup_details.message = result.message.clone();
                                }

                                promise_state.register_waiting = false;
                            }
                        }
                    }
                });
        }

        if popup_details.visible {
            egui::Window::new(popup_details.title.to_string()).show(ctx, |ui| {
                ui.vertical_centered(|ui| {
                    ui.label(popup_details.message.to_string());
                    if ui.button("Ok").clicked() {
                        popup_details.visible = false;
                    }
                });
            });
        }
    }
}

A  => ulairi-client/src/app/api.rs +465 -0
@@ 1,465 @@
use reqwest;

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
pub struct AuthInput {
    pub username: String,
    pub password: String,
}

#[derive(Serialize, Deserialize)]
pub struct RegisterInput {
    pub username: String,
    pub email: String,
    pub password: String,
}

#[derive(Serialize, Deserialize)]
pub struct AuthResponse {
    pub success: bool,
    pub message: String,
}

impl Default for AuthResponse {
    fn default() -> Self {
        AuthResponse {
            success: false,
            message: String::new(),
        }
    }
}

#[derive(Serialize, Deserialize)]
pub struct HourEntryInsertInput {
    pub email: String,
    pub hours: i32,
    pub date_worked: String,
}

#[derive(Serialize, Deserialize)]
pub struct HourEntryDeleteInput {
    pub email: String,
    pub id: i32,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct HourEntry {
    pub id: i32,
    pub user_id: i32,
    pub hours: i32,
    pub date_worked: String,
    pub date_entered: String,
}

impl Default for HourEntry {
    fn default() -> Self {
        HourEntry {
            id: 0,
            user_id: 0,
            hours: 0,
            date_worked: String::new(),
            date_entered: String::new(),
        }
    }
}

#[derive(Serialize, Deserialize, Clone)]
pub struct UserInfoInput {
    pub email: String,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct UserInfoResponse {
    pub success: bool,
    pub message: String,
}

impl Default for UserInfoResponse {
    fn default() -> Self {
        UserInfoResponse {
            success: false,
            message: String::new(),
        }
    }
}

pub async fn auth(username: String, password: String, api_address: String) -> AuthResponse {
    // Connect to env!("WEB_API_ADDRESS")/api/auth/login and return AuthResponse
    let client = reqwest::Client::new();
    let response = client
        .post(&format!("{}/api/auth/login", api_address))
        .json(&AuthInput {
            username: username.to_string(),
            password: password.to_string(),
        })
        .send()
        .await
        .unwrap();
    response.json().await.unwrap()
}

pub async fn register(
    username: String,
    email: String,
    password: String,
    api_address: String,
) -> AuthResponse {
    // Connect to env!("WEB_API_ADDRESS")/api/auth/register and return AuthResponse
    let client = reqwest::Client::new();
    let response = client
        .post(&format!("{}/api/auth/register", api_address))
        .json(&RegisterInput {
            username: username.to_string(),
            email: email.to_string(),
            password: password.to_string(),
        })
        .send()
        .await
        .unwrap();
    response.json().await.unwrap()
}

pub async fn get_user_info(api_key: String, api_address: String) -> UserInfoResponse {
    // Connect to env!("WEB_API_ADDRESS")/api/user/info and return UserInfo
    let client = reqwest::Client::new();
    let response = client
        .get(&format!("{}/api/user/info", api_address))
        .header("Authentication", api_key)
        .send()
        .await
        .unwrap();
    response.json().await.unwrap()
}

pub async fn get_hour_entries(api_key: String, api_address: String) -> Vec<HourEntry> {
    // Connect to env!("WEB_API_ADDRESS")/api/hours/all and return Vec<HourEntry>
    let email = get_user_info(api_key.clone(), api_address.clone())
        .await
        .message;

    let client = reqwest::Client::new();

    let response = client
        .post(&format!("{}/api/hours/all", api_address))
        .header("Authentication", api_key)
        .json(&UserInfoInput {
            email: email.to_string(),
        })
        .send()
        .await
        .unwrap();
    response.json().await.unwrap()
}

pub async fn insert_hour_entry(
    hours: i32,
    date_worked: String,
    api_key: String,
    api_address: String,
) -> HourEntry {
    // Connect to env!("WEB_API_ADDRESS")/api/hours/insert and return HourEntry
    let email = get_user_info(api_key.clone(), api_address.clone())
        .await
        .message;

    let client = reqwest::Client::new();

    let response = client
        .post(&format!("{}/api/hours/insert", api_address))
        .header("Authentication", api_key)
        .json(&HourEntryInsertInput {
            email: email.to_string(),
            hours: hours,
            date_worked: date_worked.to_string(),
        })
        .send()
        .await
        .unwrap();
    response.json().await.unwrap()
}

pub async fn delete_hour_entry(id: i32, api_key: String, api_address: String) {
    // Connect to env!("WEB_API_ADDRESS")/api/hours/delete and return ()
    let email = get_user_info(api_key.clone(), api_address.clone())
        .await
        .message;

    let client = reqwest::Client::new();

    client
        .post(&format!("{}/api/hours/delete", api_address))
        .header("Authentication", api_key)
        .json(&HourEntryDeleteInput {
            email: email.to_string(),
            id: id,
        })
        .send()
        .await
        .unwrap();
}
/* All the nice old things!
use mysql::prelude::*;
use mysql::*;

use argon2::{
    password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
    Argon2,
};

// Different exit contituons for verify_user
#[derive(Debug, PartialEq, Eq)]
pub enum VerifyExit {
    EmptyArg,
    Success,
    WrongPassword,
    UserNotFound,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct User {
    pub id: i32,
    pub username: String,
    pub password: String,
}

impl User {
    pub fn new() -> User {
        User {
            id: 0,
            username: "".to_owned(),
            password: "".to_owned(),
        }
    }
}

// Time entry struct
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct TimeEntry {
    pub id: i32,
    pub user_id: i32,
    pub hours: i32,
    pub date_worked: String,
    pub date_entered: String,
}

// Role struct
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Role {
    pub id: i32,
    pub name: String,
}

#[derive(Debug, PartialEq, Eq)]
pub enum Roles {
    Member,
    Leader,
}

// UserRole struct
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct UserRole {
    pub user_id: i32,
    pub role_id: i32,
}

pub struct DatabaseManager {
    pub pool: Pool,
}

impl DatabaseManager {
    pub fn new() -> DatabaseManager {
        DatabaseManager {
            pool: Pool::new(Opts::from_url("mysql://root:QXSrgXhclUKrC6jl37qQ5ytRVbthtyJSCvFR23ZhYvlbAGwysvqecSsg1eeLPkLP3J9YrSinM192JmcgjrIPdLAhDc5wpU1im1ocNV7oU01CWL4GYgfNdMFx9mMlKZOc@172.104.253.237:3306/tuntikirjanpito").unwrap()).unwrap(),
        }
    }

    pub fn init_connection(&mut self) {
        println!("Connected to mysql");
    }

    pub fn register_user(&mut self, username: &str, password: &str) -> bool {
        if username.is_empty() || password.is_empty() {
            println!("Username or password is empty");
            return false;
        }

        let password = password.as_bytes();
        let salt = SaltString::generate(&mut OsRng);

        // Argon2 with default params (Argon2id v19)
        let argon2 = Argon2::default();

        // Hash password to PHC string
        let password_hash = argon2.hash_password(password, &salt).unwrap().to_string();

        self.pool
            .get_conn()
            .ok()
            .unwrap()
            .exec_drop(
                "INSERT INTO users (username, password) values (:username, :password)",
                params! {
                    "username" => username,
                    "password" => password_hash,
                },
            )
            .unwrap();

        println!("User {} registered", username);
        return true;
    }

    pub fn verify_user(&mut self, username: &str, password: &str) -> VerifyExit {
        if username.is_empty() || password.is_empty() {
            println!("Username or password is empty");
            return VerifyExit::EmptyArg;
        }

        let res = self
            .pool
            .get_conn()
            .ok()
            .unwrap()
            .query_first(format!(
                "SELECT id, username, password FROM users WHERE username='{un}'",
                un = username
            ))
            //Unpack Result
            .map(|row| {
                //Unpack Option
                row.map(|(id, username, password)| User {
                    id: id,
                    username: username,
                    password: password,
                })
            });

        match res.unwrap() {
            Some(user) => {
                let parsed_hash = PasswordHash::new(&user.password);

                if Argon2::default()
                    .verify_password(password.as_bytes(), &parsed_hash.unwrap())
                    .is_ok()
                {
                    println!("Password verified");
                    return VerifyExit::Success;
                } else {
                    println!("Password not verified");
                    return VerifyExit::WrongPassword;
                }
            }
            None => return VerifyExit::UserNotFound,
        }
    }

    pub fn get_user(&mut self, username: &str) -> User {
        let res = self
            .pool
            .get_conn()
            .ok()
            .unwrap()
            .query_first(format!(
                "SELECT id, username, password FROM users WHERE username='{un}'",
                un = username
            ))
            //Unpack Result
            .map(|row| {
                //Unpack Option
                row.map(|(id, username, password)| User {
                    id: id,
                    username: username,
                    password: password,
                })
            });

        match res.unwrap() {
            Some(mut user) => {
                println!("User {} found", user.username);
                user.password = "".to_owned();
                return user.clone();
            }
            None => {
                return User {
                    id: -1,
                    username: "".to_owned(),
                    password: "".to_owned(),
                }
            }
        }
    }

    pub fn add_time_entry(&mut self, user_id: i32, hours: i32, date_worked: &str) {
        self.pool
            .get_conn()
            .ok()
            .unwrap()
            .exec_drop(
                "INSERT INTO entries (user_id, hours, date_worked, date_entered) values (:user_id, :hours, :date_worked, CURDATE())",
                params! {
                    "user_id" => user_id,
                    "hours" => hours,
                    "date_worked" => date_worked,
                },
            )
            .unwrap();
    }

    pub fn delete_time_entry(&mut self, id: i32) {
        self.pool
            .get_conn()
            .ok()
            .unwrap()
            .exec_drop(
                "DELETE FROM entries WHERE id=:id",
                params! {
                    "id" => id,
                },
            )
            .unwrap();
    }

    pub fn get_time_entries(&mut self, user_id: i32) -> Vec<TimeEntry> {
        self.pool
            .get_conn()
            .ok()
            .unwrap()
            .query_map(
                format!(
                    "SELECT id, user_id, hours, date_worked, date_entered FROM entries WHERE user_id={uid}",
                    uid = user_id
                ),
                |(id, user_id, hours, date_worked, date_entered)| TimeEntry {
                    id,
                    user_id,
                    hours,
                    date_worked,
                    date_entered,
                },
            )
            .unwrap()
    }

    pub fn get_roles(&mut self) -> Vec<Role> {
        self.pool
            .get_conn()
            .ok()
            .unwrap()
            .query_map("SELECT id, name FROM roles", |(id, name)| Role { id, name })
            .unwrap()
    }

    pub fn get_user_role_pairs(&mut self) -> Vec<UserRole> {
        self.pool
            .get_conn()
            .ok()
            .unwrap()
            .query_map(
                "SELECT user_id, role_id FROM users_roles",
                |(user_id, role_id)| UserRole { user_id, role_id },
            )
            .unwrap()
    }
}
*/

A  => ulairi-client/src/app/widget/mod.rs +2 -0
@@ 1,2 @@
pub mod password;


A  => ulairi-client/src/app/widget/password.rs +53 -0
@@ 1,53 @@
use eframe::egui;

pub fn password_ui(ui: &mut egui::Ui, password: &mut String) -> egui::Response {
    // This widget has its own state — show or hide password characters (`show_plaintext`).
    // In this case we use a simple `bool`, but you can also declare your own type.
    // It must implement at least `Clone` and be `'static`.
    // If you use the `persistence` feature, it also must implement `serde::{Deserialize, Serialize}`.

    // Generate an id for the state
    let state_id = ui.id().with("show_plaintext");

    // Get state for this widget.
    // You should get state by value, not by reference to avoid borrowing of [`Memory`].
    let mut show_plaintext = ui.data().get_temp::<bool>(state_id).unwrap_or(false);

    // Process ui, change a local copy of the state
    // We want TextEdit to fill entire space, and have button after that, so in that case we can
    // change direction to right_to_left.
    let result = ui.with_layout(egui::Layout::right_to_left(), |ui| {
        // Toggle the `show_plaintext` bool with a button:
        let response = ui
            .add(egui::SelectableLabel::new(show_plaintext, "👁"))
            .on_hover_text("Show/hide password");

        if response.clicked() {
            show_plaintext = !show_plaintext;
        }

        // Show the password field:
        ui.add_sized(
            ui.available_size(),
            egui::TextEdit::singleline(password).password(!show_plaintext),
        );
    });

    // Store the (possibly changed) state:
    ui.data().insert_temp(state_id, show_plaintext);

    // All done! Return the interaction response so the user can check what happened
    // (hovered, clicked, …) and maybe show a tooltip:
    result.response
}

// A wrapper that allows the more idiomatic usage pattern: `ui.add(…)`
/// Password entry field with ability to toggle character hiding.
///
/// ## Example:
/// ``` ignore
/// ui.add(password(&mut my_password));
/// ```
pub fn password(password: &mut String) -> impl egui::Widget + '_ {
    move |ui: &mut egui::Ui| password_ui(ui, password)
}

A  => ulairi-client/src/main.rs +23 -0
@@ 1,23 @@
#![forbid(unsafe_code)]
#![cfg_attr(not(debug_assertions), deny(warnings))] // Forbid warnings in release builds
#![warn(clippy::all, rust_2018_idioms)]
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] //Hide console window in release builds on Windows, this blocks stdout.

mod app;

use load_dotenv::load_dotenv;

load_dotenv!();

#[tokio::main]
async fn main() {
    println!("Starting tuntikirjanpito...");
    println!("Using {} as web api address.", env!("WEB_API_ADDRESS"));

    let native_options = eframe::NativeOptions::default();
    eframe::run_native(
        "Tuntikirjanpito",
        native_options,
        Box::new(|cc| Box::new(app::App::new(cc))),
    );
}