diff --git a/.gitignore b/.gitignore index d787b70..3611256 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /result +.env diff --git a/Cargo.lock b/Cargo.lock index 8a31f85..09675c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -66,12 +81,44 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "async-trait" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "bitflags" version = "1.3.2" @@ -84,6 +131,15 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -96,6 +152,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + [[package]] name = "cc" version = "1.0.104" @@ -118,6 +180,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-targets 0.52.5", ] @@ -174,6 +237,15 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + [[package]] name = "crossterm" version = "0.25.0" @@ -199,12 +271,99 @@ dependencies = [ "winapi", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "dyn-clone" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "fuzzy-matcher" version = "0.3.7" @@ -223,12 +382,54 @@ dependencies = [ "byteorder", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -307,13 +508,44 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + [[package]] name = "mindmap" version = "0.1.0" dependencies = [ + "bytes", "chrono", "clap", + "dotenv", "inquire", + "once_cell", + "postgres-types", + "tokio", + "tokio-postgres", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", ] [[package]] @@ -346,6 +578,25 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -370,11 +621,83 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.2", "smallvec", "windows-targets 0.52.5", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "postgres-protocol" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b6c5ef183cd3ab4ba005f1ca64c21e8bd97ce4699cfea9e8d9a2c4958ca520" +dependencies = [ + "base64", + "byteorder", + "bytes", + "fallible-iterator", + "hmac", + "md-5", + "memchr", + "rand", + "sha2", + "stringprep", +] + +[[package]] +name = "postgres-types" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2234cdee9408b523530a9b6d2d6b373d1db34f6a8e51dc03ded1828d7fb67c" +dependencies = [ + "bytes", + "chrono", + "fallible-iterator", + "postgres-protocol", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro2" version = "1.0.86" @@ -393,6 +716,45 @@ dependencies = [ "proc-macro2", ] +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.5.2" @@ -402,12 +764,49 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "signal-hook" version = "0.3.17" @@ -438,18 +837,60 @@ dependencies = [ "libc", ] +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.68" @@ -471,12 +912,123 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tinyvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-postgres" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d340244b32d920260ae7448cb72b6e238bddc3d4f7603394e7dd46ed8e48f5b8" +dependencies = [ + "async-trait", + "byteorder", + "bytes", + "fallible-iterator", + "futures-channel", + "futures-util", + "log", + "parking_lot", + "percent-encoding", + "phf", + "pin-project-lite", + "postgres-protocol", + "postgres-types", + "rand", + "socket2", + "tokio", + "tokio-util", + "whoami", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" + [[package]] name = "unicode-segmentation" version = "1.11.0" @@ -495,12 +1047,24 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.92" @@ -555,6 +1119,27 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "whoami" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +dependencies = [ + "redox_syscall 0.4.1", + "wasite", + "web-sys", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 04cf45f..f4a7b2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,15 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -chrono = "0.4.38" +chrono = { version = "0.4.38", features = ["serde"]} clap = { version = "4.5.8", features = ["derive"] } +dotenv = "0.15.0" inquire = { version = "0.7.5", features = ["date"] } +once_cell = "1.19.0" +tokio = { version = "1.38.0", features = ["full"] } +tokio-postgres = { version = "0.7.10", features = ["with-chrono-0_4"] } +postgres-types = "0.2.6" +bytes = "1.6.0" [profile.dev] debug = "line-tables-only" diff --git a/src/bin/todo/create.rs b/src/bin/todo/create.rs index 72442a0..ef85c04 100644 --- a/src/bin/todo/create.rs +++ b/src/bin/todo/create.rs @@ -1,12 +1,13 @@ use chrono::{Local, Weekday}; use clap::Parser; use inquire::{DateSelect, Select}; +use mindmap::db::get_client; use mindmap::{Difficulty, Priority, Task}; #[derive(Parser)] pub struct Args {} -pub fn command(_args: &Args) { +pub async fn command(_args: &Args) { let task = Task { description: inquire::prompt_text("Description").expect("An error occurred!"), difficulty: Select::new( @@ -27,5 +28,15 @@ pub fn command(_args: &Args) { .prompt_skippable() .expect("An error occurred!"), }; + + let client = get_client().await.expect("Failed to get client"); + client + .execute( + "INSERT INTO todo (description, priority, difficulty, deadline) VALUES ($1, $2, $3, $4)", + &[&task.description, &task.priority, &task.difficulty, &task.deadline], + ) + .await + .expect("Failed to insert task"); + println!("Task \"{}\" created successfully!", task.description); } diff --git a/src/bin/todo/delete.rs b/src/bin/todo/delete.rs index 17afc7f..b8fac1c 100644 --- a/src/bin/todo/delete.rs +++ b/src/bin/todo/delete.rs @@ -1,31 +1,14 @@ use clap::Parser; use inquire::Select; -use mindmap::Task; +use mindmap::db::{get_all_tasks, get_client}; #[derive(Parser)] pub struct Args {} -pub fn command(_args: &Args) { - let tasks = [ - Task { - description: String::from("learn rust"), - difficulty: None, - priority: None, - deadline: None, - }, - Task { - description: String::from("build mindmap cli"), - difficulty: None, - priority: None, - deadline: None, - }, - Task { - description: String::from("build mindmap gui"), - difficulty: None, - priority: None, - deadline: None, - }, - ]; +pub async fn command(_args: &Args) { + let tasks = get_all_tasks() + .await + .expect("Internal Server Error. Try Again!"); let task_description = Select::new( "Select the task to delete:", tasks.iter().map(|task| &task.description).collect(), @@ -33,5 +16,14 @@ pub fn command(_args: &Args) { .prompt() .expect("An error occurred!"); + let client = get_client().await.expect("Failed to fetch client"); + client + .execute( + "DELETE FROM todo WHERE description = $1", + &[&task_description], + ) + .await + .expect("Failed to delete task"); + println!("Task \"{}\" deleted successfully!", task_description); } diff --git a/src/bin/todo/list.rs b/src/bin/todo/list.rs index 572ac47..4502300 100644 --- a/src/bin/todo/list.rs +++ b/src/bin/todo/list.rs @@ -1,8 +1,30 @@ +use chrono::Local; use clap::Parser; +use mindmap::db::get_client; #[derive(Parser)] pub struct Args {} -pub fn command(_args: &Args) { - println!("Tasks due today:"); +pub async fn command(_args: &Args) { + let client = get_client().await.expect("Failed to fetch client"); + let today = Local::now().date_naive(); + let rows = client + .query( + "SELECT description, priority, difficulty, deadline FROM todo WHERE deadline = $1::date", + &[&today], + ) + .await + .expect("Failed to fetch tasks"); + + for row in rows { + let description: String = row.get(0); + let priority: mindmap::Priority = row.get(1); + let difficulty: mindmap::Difficulty = row.get(2); + let deadline: chrono::NaiveDate = row.get(3); + + println!("------------------------\nTask: {}", description); + println!("Priority: {:?}", priority); + println!("Difficulty: {:?}", difficulty); + println!("Deadline: {}\n------------------------\n", deadline); + } } diff --git a/src/bin/todo/main.rs b/src/bin/todo/main.rs index 57b8086..808d6d4 100644 --- a/src/bin/todo/main.rs +++ b/src/bin/todo/main.rs @@ -26,14 +26,15 @@ enum Commands { Delete(delete::Args), } -fn main() { +#[tokio::main] +async fn main() { let cli = Args::parse(); match &cli.command { - Commands::Create(args) => create::command(args), - Commands::List(args) => list::command(args), + Commands::Create(args) => create::command(args).await, + Commands::List(args) => list::command(args).await, Commands::Show(args) => show::command(args), - Commands::Update(args) => update::command(args), - Commands::Delete(args) => delete::command(args), + Commands::Update(args) => update::command(args).await, + Commands::Delete(args) => delete::command(args).await, } } diff --git a/src/bin/todo/update.rs b/src/bin/todo/update.rs index 4eccd35..971b3ad 100644 --- a/src/bin/todo/update.rs +++ b/src/bin/todo/update.rs @@ -1,8 +1,67 @@ +use chrono::{Local, Weekday}; use clap::Parser; +use mindmap::db::{get_all_tasks, get_client}; +use mindmap::{Difficulty, Priority, Task}; #[derive(Parser)] pub struct Args {} -pub fn command(_args: &Args) { - println!("Updated task successfully!") +pub async fn command(_args: &Args) { + let tasks = get_all_tasks() + .await + .expect("Internal Server Error. Try Again!"); + + if tasks.is_empty() { + println!("Task not found!"); + return; + }; + + let task_choices: Vec = tasks.iter().map(|task| { + format!( + "\nDescription: {}\nDifficulty: {}\nPriority: {}\nDeadline: {}", + task.description, + task.difficulty.as_ref().map_or("Not set".to_string(), |d| d.to_string()), + task.priority.as_ref().map_or("Not set".to_string(), |p| p.to_string()), + task.deadline.map_or("Not set".to_string(), |d| d.to_string()) + ) + }).collect(); + + let task_description = inquire::Select::new( + "Select the task to update:", + task_choices, + ) + .prompt() + .expect("An error occurred!"); + + let new_task = Task { + description: inquire::prompt_text("New Description").expect("An error occurred!"), + difficulty: inquire::Select::new( + "New Difficulty", + vec![Difficulty::Low, Difficulty::Medium, Difficulty::High], + ) + .prompt_skippable() + .expect("An error occurred!"), + priority: inquire::Select::new( + "New Priority", + vec![Priority::Low, Priority::Medium, Priority::High], + ) + .prompt_skippable() + .expect("An error occurred!"), + deadline: inquire::DateSelect::new("New Deadline") + .with_min_date(Local::now().date_naive()) + .with_week_start(Weekday::Mon) + .prompt_skippable() + .expect("An error occurred!"), + }; + + let client = get_client().await.expect("Failed to fetch client"); + client + .execute( + "UPDATE todo SET description = $1, priority = $2, difficulty = $3, deadline = $4 WHERE description = $5", + &[&new_task.description, &new_task.priority, &new_task.difficulty, &new_task.deadline, &task_description], + ) + .await + .expect("Failed to update task"); + + println!("Task updated successfully!"); } diff --git a/src/db/mod.rs b/src/db/mod.rs new file mode 100644 index 0000000..24d92ff --- /dev/null +++ b/src/db/mod.rs @@ -0,0 +1,50 @@ +use crate::Task; +use dotenv::dotenv; +use std::env; +use tokio::sync::OnceCell; +use tokio_postgres::{Client, Error, NoTls}; + +static CLIENT: OnceCell = OnceCell::const_new(); + +async fn init_client() -> Result { + dotenv().ok(); + let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); + + let (client, connection) = tokio_postgres::connect(&database_url, NoTls).await?; + + tokio::spawn(async move { + if let Err(e) = connection.await { + eprintln!("connection error: {}", e); + } + }); + + Ok(client) +} + +pub async fn get_client() -> Result<&'static Client, Error> { + CLIENT.get_or_try_init(init_client).await +} + +pub async fn get_all_tasks() -> Result, Error> { + let client = get_client().await?; + + let rows = client + .query( + "SELECT description, priority, difficulty, deadline FROM todo", + &[], + ) + .await + .expect("Failed to fetch all tasks."); + + let tasks: Vec = rows + .iter() + .map(|row| Task { + description: row.get(0), + priority: row.get(1), + difficulty: row.get(2), + deadline: row.get(3), + }) + .collect(); + + Ok(tasks) +} diff --git a/src/lib.rs b/src/lib.rs index 4344e61..dd95232 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,15 @@ +use std::error::Error as StdError; use std::fmt; +use std::str::FromStr; +use bytes::BytesMut; use chrono::NaiveDate; +use clap::ValueEnum; +use tokio_postgres::types::{FromSql, IsNull, ToSql, Type}; +pub mod db; + +#[derive(Debug, Clone, ValueEnum)] pub enum Difficulty { Low, Medium, @@ -18,6 +26,55 @@ impl fmt::Display for Difficulty { } } +impl FromStr for Difficulty { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "low" => Ok(Difficulty::Low), + "medium" => Ok(Difficulty::Medium), + "high" => Ok(Difficulty::High), + _ => Err(format!("Invalid difficulty: {}", s)), + } + } +} + +impl ToSql for Difficulty { + fn to_sql( + &self, + _ty: &Type, + out: &mut BytesMut, + ) -> Result> { + match self { + Difficulty::Low => out.extend_from_slice(b"low"), + Difficulty::Medium => out.extend_from_slice(b"medium"), + Difficulty::High => out.extend_from_slice(b"high"), + } + Ok(IsNull::No) + } + + fn accepts(ty: &Type) -> bool { + ty.name() == "difficulty" + } + + tokio_postgres::types::to_sql_checked!(); +} + +impl<'a> FromSql<'a> for Difficulty { + fn from_sql(_ty: &Type, raw: &'a [u8]) -> Result> { + let s = std::str::from_utf8(raw)?; + s.parse().map_err(|e| { + Box::new(std::io::Error::new(std::io::ErrorKind::InvalidData, e)) + as Box + }) + } + + fn accepts(ty: &Type) -> bool { + ty.name() == "difficulty" + } +} + +#[derive(Debug, Clone, ValueEnum)] pub enum Priority { Low, Medium, @@ -34,6 +91,55 @@ impl fmt::Display for Priority { } } +impl ToSql for Priority { + fn to_sql( + &self, + _ty: &Type, + out: &mut BytesMut, + ) -> Result> { + match self { + Priority::Low => out.extend_from_slice(b"low"), + Priority::Medium => out.extend_from_slice(b"medium"), + Priority::High => out.extend_from_slice(b"high"), + } + Ok(IsNull::No) + } + + fn accepts(ty: &Type) -> bool { + ty.name() == "priority" + } + + tokio_postgres::types::to_sql_checked!(); +} + +impl FromStr for Priority { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "low" => Ok(Priority::Low), + "medium" => Ok(Priority::Medium), + "high" => Ok(Priority::High), + _ => Err(format!("Invalid priority: {}", s)), + } + } +} + +impl<'a> FromSql<'a> for Priority { + fn from_sql(_ty: &Type, raw: &'a [u8]) -> Result> { + let s = std::str::from_utf8(raw)?; + s.parse().map_err(|e| { + Box::new(std::io::Error::new(std::io::ErrorKind::InvalidData, e)) + as Box + }) + } + + fn accepts(ty: &Type) -> bool { + ty.name() == "priority" + } +} + +#[derive(Debug, Clone)] pub struct Task { pub description: String, pub difficulty: Option,