From 8d7e9f1c37c9df18c0f15bbae9bda3021fafa010 Mon Sep 17 00:00:00 2001 From: aryansri-19 Date: Wed, 3 Jul 2024 17:25:28 +0530 Subject: [PATCH 1/7] Feat: db crud operations complete --- .gitignore | 1 + Cargo.lock | 462 ++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 8 +- src/bin/todo/create.rs | 13 +- src/bin/todo/delete.rs | 36 ++-- src/bin/todo/list.rs | 26 ++- src/bin/todo/main.rs | 11 +- src/bin/todo/update.rs | 68 +++++- src/db/mod.rs | 50 +++++ src/lib.rs | 106 ++++++++++ 10 files changed, 747 insertions(+), 34 deletions(-) create mode 100644 src/db/mod.rs 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 1a5b149..7cad73d 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" @@ -83,6 +98,21 @@ 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" @@ -125,6 +155,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" @@ -147,6 +183,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-targets 0.52.5", ] @@ -319,6 +356,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -351,6 +389,12 @@ dependencies = [ "const-random", ] +[[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" @@ -363,6 +407,66 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[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" @@ -402,6 +506,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + [[package]] name = "hashbrown" version = "0.13.2" @@ -420,6 +530,21 @@ 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" @@ -547,6 +672,16 @@ 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" @@ -557,13 +692,19 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" name = "mindmap" version = "0.1.0" dependencies = [ + "bytes", "chrono", "clap", "clap_complete", "config", "dirs", + "dotenv", "inquire", + "once_cell", + "postgres-types", "serde", + "tokio", + "tokio-postgres", ] [[package]] @@ -572,6 +713,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.11" @@ -612,6 +762,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" @@ -652,7 +821,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.2", "smallvec", "windows-targets 0.52.5", ] @@ -663,6 +832,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pest" version = "2.7.11" @@ -708,6 +883,72 @@ dependencies = [ "sha2", ] +[[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" @@ -726,6 +967,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" @@ -768,6 +1048,12 @@ dependencies = [ "ordered-multimap", ] +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + [[package]] name = "ryu" version = "1.0.18" @@ -861,18 +1147,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" @@ -923,6 +1251,90 @@ dependencies = [ "crunchy", ] +[[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 = "toml" version = "0.8.14" @@ -969,12 +1381,33 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +[[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" @@ -1005,6 +1438,12 @@ 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" @@ -1059,6 +1498,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 d9611db..47f1cd2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,12 +6,18 @@ 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"] } clap_complete = "4.5.7" config = "0.14.0" dirs = "5.0.1" +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" serde = "1.0.203" [profile.dev] 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 9a73b7b..98c29de 100644 --- a/src/bin/todo/main.rs +++ b/src/bin/todo/main.rs @@ -30,15 +30,16 @@ enum Commands { Completion(completion::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, Commands::Completion(args) => completion::command(args), } } diff --git a/src/bin/todo/update.rs b/src/bin/todo/update.rs index 4eccd35..ac9c9ed 100644 --- a/src/bin/todo/update.rs +++ b/src/bin/todo/update.rs @@ -1,8 +1,72 @@ +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 1cfad68..0e1c682 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,17 @@ +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; pub mod configuration; +#[derive(Debug, Clone, ValueEnum)] pub enum Difficulty { Low, Medium, @@ -20,6 +28,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, @@ -36,6 +93,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, From b668f2e708103657196e1dec7eaef314019a50a9 Mon Sep 17 00:00:00 2001 From: aryansri-19 Date: Thu, 4 Jul 2024 21:50:48 +0530 Subject: [PATCH 2/7] Refactor: Moving client code to lib.rs --- src/bin/todo/create.rs | 20 +++++------ src/bin/todo/delete.rs | 13 ++----- src/bin/todo/list.rs | 13 ++----- src/bin/todo/update.rs | 21 ++++++----- src/db/mod.rs | 79 ++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 100 insertions(+), 46 deletions(-) diff --git a/src/bin/todo/create.rs b/src/bin/todo/create.rs index ef85c04..4d8cc25 100644 --- a/src/bin/todo/create.rs +++ b/src/bin/todo/create.rs @@ -1,7 +1,7 @@ use chrono::{Local, Weekday}; use clap::Parser; use inquire::{DateSelect, Select}; -use mindmap::db::get_client; +use mindmap::db::insert_todo; use mindmap::{Difficulty, Priority, Task}; #[derive(Parser)] @@ -29,14 +29,12 @@ pub async fn command(_args: &Args) { .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); + insert_todo( + task.description, + task.priority, + task.difficulty, + task.deadline, + ) + .await + .expect("Failed to insert task."); } diff --git a/src/bin/todo/delete.rs b/src/bin/todo/delete.rs index b8fac1c..f3c986c 100644 --- a/src/bin/todo/delete.rs +++ b/src/bin/todo/delete.rs @@ -1,6 +1,6 @@ use clap::Parser; use inquire::Select; -use mindmap::db::{get_all_tasks, get_client}; +use mindmap::db::{delete_task, get_all_tasks}; #[derive(Parser)] pub struct Args {} @@ -16,14 +16,7 @@ pub async 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], - ) + delete_task(task_description.to_string()) .await - .expect("Failed to delete task"); - - println!("Task \"{}\" deleted successfully!", task_description); + .expect("Failed to delete task."); } diff --git a/src/bin/todo/list.rs b/src/bin/todo/list.rs index 4502300..3c6a319 100644 --- a/src/bin/todo/list.rs +++ b/src/bin/todo/list.rs @@ -1,20 +1,11 @@ -use chrono::Local; use clap::Parser; -use mindmap::db::get_client; +use mindmap::db::list_tasks; #[derive(Parser)] pub struct Args {} 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"); + let rows = list_tasks().await.expect("Failed to list tasks."); for row in rows { let description: String = row.get(0); diff --git a/src/bin/todo/update.rs b/src/bin/todo/update.rs index ac9c9ed..20e9a6b 100644 --- a/src/bin/todo/update.rs +++ b/src/bin/todo/update.rs @@ -1,6 +1,6 @@ use chrono::{Local, Weekday}; use clap::Parser; -use mindmap::db::{get_all_tasks, get_client}; +use mindmap::db::{get_all_tasks, update_task}; use mindmap::{Difficulty, Priority, Task}; #[derive(Parser)] @@ -59,14 +59,13 @@ pub async fn command(_args: &Args) { .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!"); + update_task( + new_task.description, + new_task.priority, + new_task.difficulty, + new_task.deadline, + task_description, + ) + .await + .expect("Failed to update task."); } diff --git a/src/db/mod.rs b/src/db/mod.rs index 24d92ff..6a6a5fb 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,8 +1,11 @@ -use crate::Task; -use dotenv::dotenv; use std::env; + +use chrono::{Local, NaiveDate}; +use dotenv::dotenv; use tokio::sync::OnceCell; -use tokio_postgres::{Client, Error, NoTls}; +use tokio_postgres::{Client, Error, NoTls, Row}; + +use crate::{Difficulty, Priority, Task}; static CLIENT: OnceCell = OnceCell::const_new(); @@ -48,3 +51,73 @@ pub async fn get_all_tasks() -> Result, Error> { Ok(tasks) } + +pub async fn insert_todo( + description: String, + priority: Option, + difficulty: Option, + deadline: Option, +) -> Result<(), Error> { + let client = get_client().await?; + + client + .execute( + "INSERT INTO todo (description, priority, difficulty, deadline) VALUES ($1, $2, $3, $4)", + &[&description, &priority, &difficulty, &deadline], + ) + .await + .expect("Failed to insert task"); + + println!("Task \"{}\" created successfully!", description); + + Ok(()) +} + +pub async fn delete_task(description: String) -> Result<(), Error> { + let client = get_client().await?; + + client + .execute("DELETE FROM todo WHERE description = $1", &[&description]) + .await + .expect("Failed to delete task"); + + println!("Task \"{}\" deleted successfully!", description); + + Ok(()) +} + +pub async fn list_tasks() -> Result, Error> { + let client = get_client().await?; + + 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"); + + Ok(rows) +} + +pub async fn update_task( + new_description: String, + new_priority: Option, + new_difficulty: Option, + new_deadline: Option, + old_description: String, +) -> Result<(), Error> { + let client = get_client().await?; + client + .execute( + "UPDATE todo SET description = $1, priority = $2, difficulty = $3, deadline = $4 WHERE description = $5", + &[&new_description, &new_priority, &new_difficulty, &new_deadline, &old_description], + ) + .await + .expect("Failed to update task"); + + println!("Task updated successfully!"); + + Ok(()) +} From d4fe2ae003250b9704b5ebd1cfa9f57bdc3e8df5 Mon Sep 17 00:00:00 2001 From: aryansri-19 Date: Fri, 5 Jul 2024 09:48:50 +0530 Subject: [PATCH 3/7] Fix: Implement struct functions --- src/bin/todo/create.rs | 10 +---- src/bin/todo/delete.rs | 12 ++++-- src/bin/todo/list.rs | 19 ++++----- src/bin/todo/update.rs | 15 +++---- src/db/mod.rs | 75 +--------------------------------- src/lib.rs | 92 ++++++++++++++++++++++++++++++++++++++---- 6 files changed, 108 insertions(+), 115 deletions(-) diff --git a/src/bin/todo/create.rs b/src/bin/todo/create.rs index 4d8cc25..8df8dab 100644 --- a/src/bin/todo/create.rs +++ b/src/bin/todo/create.rs @@ -1,7 +1,6 @@ use chrono::{Local, Weekday}; use clap::Parser; use inquire::{DateSelect, Select}; -use mindmap::db::insert_todo; use mindmap::{Difficulty, Priority, Task}; #[derive(Parser)] @@ -29,12 +28,5 @@ pub async fn command(_args: &Args) { .expect("An error occurred!"), }; - insert_todo( - task.description, - task.priority, - task.difficulty, - task.deadline, - ) - .await - .expect("Failed to insert task."); + task.insert_todo().await.expect("Failed to insert task"); } diff --git a/src/bin/todo/delete.rs b/src/bin/todo/delete.rs index f3c986c..e9b680d 100644 --- a/src/bin/todo/delete.rs +++ b/src/bin/todo/delete.rs @@ -1,6 +1,6 @@ use clap::Parser; use inquire::Select; -use mindmap::db::{delete_task, get_all_tasks}; +use mindmap::db::get_all_tasks; #[derive(Parser)] pub struct Args {} @@ -9,6 +9,7 @@ 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(), @@ -16,7 +17,10 @@ pub async fn command(_args: &Args) { .prompt() .expect("An error occurred!"); - delete_task(task_description.to_string()) - .await - .expect("Failed to delete task."); + let task = tasks + .iter() + .find(|task| task.description == *task_description) + .expect("Task not found!"); + + task.delete_task().await.expect("Failed to delete task"); } diff --git a/src/bin/todo/list.rs b/src/bin/todo/list.rs index 3c6a319..75393aa 100644 --- a/src/bin/todo/list.rs +++ b/src/bin/todo/list.rs @@ -1,21 +1,18 @@ use clap::Parser; -use mindmap::db::list_tasks; +use mindmap::Task; #[derive(Parser)] pub struct Args {} pub async fn command(_args: &Args) { - let rows = list_tasks().await.expect("Failed to list tasks."); + let rows = Task::list_tasks() + .await + .expect("Failed to fetch all 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); + println!("------------------------\nTask: {}", row.description); + println!("Priority: {:?}", row.priority); + println!("Difficulty: {:?}", row.difficulty); + println!("Deadline: {:?}\n------------------------\n", row.deadline); } } diff --git a/src/bin/todo/update.rs b/src/bin/todo/update.rs index 20e9a6b..05871db 100644 --- a/src/bin/todo/update.rs +++ b/src/bin/todo/update.rs @@ -1,6 +1,6 @@ use chrono::{Local, Weekday}; use clap::Parser; -use mindmap::db::{get_all_tasks, update_task}; +use mindmap::db::get_all_tasks; use mindmap::{Difficulty, Priority, Task}; #[derive(Parser)] @@ -59,13 +59,8 @@ pub async fn command(_args: &Args) { .expect("An error occurred!"), }; - update_task( - new_task.description, - new_task.priority, - new_task.difficulty, - new_task.deadline, - task_description, - ) - .await - .expect("Failed to update task."); + new_task + .update_task(task_description) + .await + .expect("Failed to update task"); } diff --git a/src/db/mod.rs b/src/db/mod.rs index 6a6a5fb..c54186d 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,11 +1,10 @@ use std::env; -use chrono::{Local, NaiveDate}; use dotenv::dotenv; use tokio::sync::OnceCell; -use tokio_postgres::{Client, Error, NoTls, Row}; +use tokio_postgres::{Client, Error, NoTls}; -use crate::{Difficulty, Priority, Task}; +use crate::Task; static CLIENT: OnceCell = OnceCell::const_new(); @@ -51,73 +50,3 @@ pub async fn get_all_tasks() -> Result, Error> { Ok(tasks) } - -pub async fn insert_todo( - description: String, - priority: Option, - difficulty: Option, - deadline: Option, -) -> Result<(), Error> { - let client = get_client().await?; - - client - .execute( - "INSERT INTO todo (description, priority, difficulty, deadline) VALUES ($1, $2, $3, $4)", - &[&description, &priority, &difficulty, &deadline], - ) - .await - .expect("Failed to insert task"); - - println!("Task \"{}\" created successfully!", description); - - Ok(()) -} - -pub async fn delete_task(description: String) -> Result<(), Error> { - let client = get_client().await?; - - client - .execute("DELETE FROM todo WHERE description = $1", &[&description]) - .await - .expect("Failed to delete task"); - - println!("Task \"{}\" deleted successfully!", description); - - Ok(()) -} - -pub async fn list_tasks() -> Result, Error> { - let client = get_client().await?; - - 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"); - - Ok(rows) -} - -pub async fn update_task( - new_description: String, - new_priority: Option, - new_difficulty: Option, - new_deadline: Option, - old_description: String, -) -> Result<(), Error> { - let client = get_client().await?; - client - .execute( - "UPDATE todo SET description = $1, priority = $2, difficulty = $3, deadline = $4 WHERE description = $5", - &[&new_description, &new_priority, &new_difficulty, &new_deadline, &old_description], - ) - .await - .expect("Failed to update task"); - - println!("Task updated successfully!"); - - Ok(()) -} diff --git a/src/lib.rs b/src/lib.rs index 0e1c682..0a7582d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,11 @@ -use std::error::Error as StdError; +use std::error::Error; use std::fmt; use std::str::FromStr; use bytes::BytesMut; -use chrono::NaiveDate; +use chrono::{Local, NaiveDate}; use clap::ValueEnum; +use db::get_client; use tokio_postgres::types::{FromSql, IsNull, ToSql, Type}; pub mod db; @@ -46,7 +47,7 @@ impl ToSql for Difficulty { &self, _ty: &Type, out: &mut BytesMut, - ) -> Result> { + ) -> Result> { match self { Difficulty::Low => out.extend_from_slice(b"low"), Difficulty::Medium => out.extend_from_slice(b"medium"), @@ -63,11 +64,11 @@ impl ToSql for Difficulty { } impl<'a> FromSql<'a> for Difficulty { - fn from_sql(_ty: &Type, raw: &'a [u8]) -> Result> { + 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 + as Box }) } @@ -98,7 +99,7 @@ impl ToSql for Priority { &self, _ty: &Type, out: &mut BytesMut, - ) -> Result> { + ) -> Result> { match self { Priority::Low => out.extend_from_slice(b"low"), Priority::Medium => out.extend_from_slice(b"medium"), @@ -128,11 +129,11 @@ impl FromStr for Priority { } impl<'a> FromSql<'a> for Priority { - fn from_sql(_ty: &Type, raw: &'a [u8]) -> Result> { + 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 + as Box }) } @@ -148,3 +149,78 @@ pub struct Task { pub priority: Option, pub deadline: Option, } + +impl Task { + pub async fn insert_todo(&self) -> Result<(), Box> { + let client = get_client().await?; + + client + .execute( + "INSERT INTO todo (description, priority, difficulty, deadline) VALUES ($1, $2, $3, $4)", + &[&self.description, &self.priority, &self.difficulty, &self.deadline], + ) + .await + .expect("Failed to insert task"); + + println!("Task \"{}\" created successfully!", self.description); + + Ok(()) + } + + pub async fn delete_task(&self) -> Result<(), Box> { + let client = get_client().await?; + + client + .execute( + "DELETE FROM todo WHERE description = $1", + &[&self.description], + ) + .await + .expect("Failed to delete task"); + + println!("Task \"{}\" deleted successfully!", self.description); + + Ok(()) + } + + pub async fn list_tasks() -> Result, Box> { + let client = get_client().await?; + + 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 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) + } + + pub async fn update_task(&self, old_description: String) -> Result<(), Box> { + let client = get_client().await?; + + client + .execute( + "UPDATE todo SET description = $1, priority = $2, difficulty = $3, deadline = $4 WHERE description = $5", + &[&self.description, &self.priority, &self.difficulty, &self.deadline, &old_description], + ) + .await + .expect("Failed to update task"); + + println!("Task updated successfully!"); + + Ok(()) + } +} From 3caa77582126bc94dd0fc82e274efe4127734e76 Mon Sep 17 00:00:00 2001 From: aryansri-19 Date: Fri, 5 Jul 2024 15:41:23 +0530 Subject: [PATCH 4/7] Refactor: Change semantics and remove redundant function --- src/bin/todo/create.rs | 10 +++++----- src/bin/todo/delete.rs | 6 +++--- src/bin/todo/list.rs | 4 ++-- src/bin/todo/update.rs | 36 +++++++++--------------------------- src/db/mod.rs | 26 -------------------------- src/lib.rs | 30 ++++++++++++++++++++++++------ 6 files changed, 43 insertions(+), 69 deletions(-) diff --git a/src/bin/todo/create.rs b/src/bin/todo/create.rs index 8df8dab..678feaa 100644 --- a/src/bin/todo/create.rs +++ b/src/bin/todo/create.rs @@ -8,25 +8,25 @@ pub struct Args {} pub async fn command(_args: &Args) { let task = Task { - description: inquire::prompt_text("Description").expect("An error occurred!"), + description: inquire::prompt_text("Description:").expect("An error occurred!"), difficulty: Select::new( - "Difficulty", + "Difficulty:", vec![Difficulty::Low, Difficulty::Medium, Difficulty::High], ) .prompt_skippable() .expect("An error occurred!"), priority: Select::new( - "Priority", + "Priority:", vec![Priority::Low, Priority::Medium, Priority::High], ) .prompt_skippable() .expect("An error occurred!"), - deadline: DateSelect::new("Deadline") + deadline: DateSelect::new("Deadline:") .with_min_date(Local::now().date_naive()) .with_week_start(Weekday::Mon) .prompt_skippable() .expect("An error occurred!"), }; - task.insert_todo().await.expect("Failed to insert task"); + task.save_to_db().await.expect("Failed to insert task"); } diff --git a/src/bin/todo/delete.rs b/src/bin/todo/delete.rs index e9b680d..b912c9c 100644 --- a/src/bin/todo/delete.rs +++ b/src/bin/todo/delete.rs @@ -1,12 +1,12 @@ use clap::Parser; use inquire::Select; -use mindmap::db::get_all_tasks; +use mindmap::Task; #[derive(Parser)] pub struct Args {} pub async fn command(_args: &Args) { - let tasks = get_all_tasks() + let tasks = Task::list_tasks(false) .await .expect("Internal Server Error. Try Again!"); @@ -22,5 +22,5 @@ pub async fn command(_args: &Args) { .find(|task| task.description == *task_description) .expect("Task not found!"); - task.delete_task().await.expect("Failed to delete task"); + task.delete_from_db().await.expect("Failed to delete task"); } diff --git a/src/bin/todo/list.rs b/src/bin/todo/list.rs index 75393aa..35eabb7 100644 --- a/src/bin/todo/list.rs +++ b/src/bin/todo/list.rs @@ -5,9 +5,9 @@ use mindmap::Task; pub struct Args {} pub async fn command(_args: &Args) { - let rows = Task::list_tasks() + let rows = Task::list_tasks(true) .await - .expect("Failed to fetch all tasks."); + .expect("Failed to fetch today's tasks."); for row in rows { println!("------------------------\nTask: {}", row.description); diff --git a/src/bin/todo/update.rs b/src/bin/todo/update.rs index 05871db..415b786 100644 --- a/src/bin/todo/update.rs +++ b/src/bin/todo/update.rs @@ -1,13 +1,13 @@ use chrono::{Local, Weekday}; use clap::Parser; -use mindmap::db::get_all_tasks; -use mindmap::{Difficulty, Priority, Task}; +use mindmap::Task; +use mindmap::{Difficulty, Priority}; #[derive(Parser)] pub struct Args {} pub async fn command(_args: &Args) { - let tasks = get_all_tasks() + let tasks = Task::list_tasks(false) .await .expect("Internal Server Error. Try Again!"); @@ -16,43 +16,25 @@ pub async fn command(_args: &Args) { 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) + let old_task = inquire::Select::new("Select the task to update:", tasks) .prompt() .expect("An error occurred!"); let new_task = Task { - description: inquire::prompt_text("New Description").expect("An error occurred!"), + description: inquire::prompt_text("New Description:").expect("An error occurred!"), difficulty: inquire::Select::new( - "New Difficulty", + "New Difficulty:", vec![Difficulty::Low, Difficulty::Medium, Difficulty::High], ) .prompt_skippable() .expect("An error occurred!"), priority: inquire::Select::new( - "New Priority", + "New Priority:", vec![Priority::Low, Priority::Medium, Priority::High], ) .prompt_skippable() .expect("An error occurred!"), - deadline: inquire::DateSelect::new("New Deadline") + deadline: inquire::DateSelect::new("New Deadline:") .with_min_date(Local::now().date_naive()) .with_week_start(Weekday::Mon) .prompt_skippable() @@ -60,7 +42,7 @@ pub async fn command(_args: &Args) { }; new_task - .update_task(task_description) + .update_task(old_task.description) .await .expect("Failed to update task"); } diff --git a/src/db/mod.rs b/src/db/mod.rs index c54186d..af5f83c 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -4,8 +4,6 @@ use dotenv::dotenv; use tokio::sync::OnceCell; use tokio_postgres::{Client, Error, NoTls}; -use crate::Task; - static CLIENT: OnceCell = OnceCell::const_new(); async fn init_client() -> Result { @@ -26,27 +24,3 @@ async fn init_client() -> Result { 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 0a7582d..7ff9851 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -150,8 +150,18 @@ pub struct Task { pub deadline: Option, } +impl fmt::Display for Task { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "\nTask: {}\nPriority: {:?}\nDifficulty: {:?}\nDeadline: {:?}\n", + self.description, self.priority, self.difficulty, self.deadline + ) + } +} + impl Task { - pub async fn insert_todo(&self) -> Result<(), Box> { + pub async fn save_to_db(&self) -> Result<(), Box> { let client = get_client().await?; client @@ -167,7 +177,7 @@ impl Task { Ok(()) } - pub async fn delete_task(&self) -> Result<(), Box> { + pub async fn delete_from_db(&self) -> Result<(), Box> { let client = get_client().await?; client @@ -183,17 +193,25 @@ impl Task { Ok(()) } - pub async fn list_tasks() -> Result, Box> { + pub async fn list_tasks(current: bool) -> Result, Box> { let client = get_client().await?; let today = Local::now().date_naive(); - let rows = client - .query( + let (query, params): (&str, &[&(dyn ToSql + Sync)]) = if current { + ( "SELECT description, priority, difficulty, deadline FROM todo WHERE deadline = $1::date", &[&today], ) + } else { + ( + "SELECT description, priority, difficulty, deadline FROM todo", + &[], + ) + }; + let rows = client + .query(query, params) .await - .expect("Failed to fetch all tasks."); + .expect("Failed to fetch tasks."); let tasks: Vec = rows .iter() From aec4d1f81daab31f2f180d54b00764863d93e275 Mon Sep 17 00:00:00 2001 From: aryansri-19 Date: Fri, 5 Jul 2024 16:00:35 +0530 Subject: [PATCH 5/7] Fix: Correct parameter in list_tasks --- src/bin/todo/delete.rs | 2 +- src/bin/todo/list.rs | 10 ++++++---- src/bin/todo/update.rs | 2 +- src/lib.rs | 27 +++++++++++++-------------- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/bin/todo/delete.rs b/src/bin/todo/delete.rs index b912c9c..1e1ddd4 100644 --- a/src/bin/todo/delete.rs +++ b/src/bin/todo/delete.rs @@ -6,7 +6,7 @@ use mindmap::Task; pub struct Args {} pub async fn command(_args: &Args) { - let tasks = Task::list_tasks(false) + let tasks = Task::list_tasks(None) .await .expect("Internal Server Error. Try Again!"); diff --git a/src/bin/todo/list.rs b/src/bin/todo/list.rs index 35eabb7..94c771b 100644 --- a/src/bin/todo/list.rs +++ b/src/bin/todo/list.rs @@ -1,3 +1,4 @@ +use chrono::Local; use clap::Parser; use mindmap::Task; @@ -5,14 +6,15 @@ use mindmap::Task; pub struct Args {} pub async fn command(_args: &Args) { - let rows = Task::list_tasks(true) + let today = Local::now().date_naive(); + let rows = Task::list_tasks(Some(today)) .await .expect("Failed to fetch today's tasks."); for row in rows { println!("------------------------\nTask: {}", row.description); - println!("Priority: {:?}", row.priority); - println!("Difficulty: {:?}", row.difficulty); - println!("Deadline: {:?}\n------------------------\n", row.deadline); + println!("Priority: {:?}", row.priority.unwrap()); + println!("Difficulty: {:?}", row.difficulty.unwrap()); + println!("Deadline: {:?}\n------------------------\n", row.deadline.unwrap()); } } diff --git a/src/bin/todo/update.rs b/src/bin/todo/update.rs index 415b786..3cc7932 100644 --- a/src/bin/todo/update.rs +++ b/src/bin/todo/update.rs @@ -7,7 +7,7 @@ use mindmap::{Difficulty, Priority}; pub struct Args {} pub async fn command(_args: &Args) { - let tasks = Task::list_tasks(false) + let tasks = Task::list_tasks(None) .await .expect("Internal Server Error. Try Again!"); diff --git a/src/lib.rs b/src/lib.rs index 7ff9851..7cc4214 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ use std::fmt; use std::str::FromStr; use bytes::BytesMut; -use chrono::{Local, NaiveDate}; +use chrono::NaiveDate; use clap::ValueEnum; use db::get_client; use tokio_postgres::types::{FromSql, IsNull, ToSql, Type}; @@ -193,20 +193,19 @@ impl Task { Ok(()) } - pub async fn list_tasks(current: bool) -> Result, Box> { + pub async fn list_tasks(date: Option) -> Result, Box> { let client = get_client().await?; - - let today = Local::now().date_naive(); - let (query, params): (&str, &[&(dyn ToSql + Sync)]) = if current { - ( - "SELECT description, priority, difficulty, deadline FROM todo WHERE deadline = $1::date", - &[&today], - ) - } else { - ( - "SELECT description, priority, difficulty, deadline FROM todo", - &[], - ) + let (query, params): (&str, &[&(dyn ToSql + Sync)]) = match date { + Some(_d) => + ( + "SELECT description, priority, difficulty, deadline FROM todo WHERE deadline = $1::date", + &[&date.unwrap()], + ), + None => + ( + "SELECT description, priority, difficulty, deadline FROM todo", + &[], + ) }; let rows = client .query(query, params) From 94ee5b29bde4ff521ad279f07b9b03628816ad81 Mon Sep 17 00:00:00 2001 From: aryansri-19 Date: Sat, 6 Jul 2024 10:49:13 +0530 Subject: [PATCH 6/7] Fix: Lint error --- src/bin/todo/list.rs | 5 ++++- src/bin/todo/update.rs | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/bin/todo/list.rs b/src/bin/todo/list.rs index 94c771b..d608634 100644 --- a/src/bin/todo/list.rs +++ b/src/bin/todo/list.rs @@ -15,6 +15,9 @@ pub async fn command(_args: &Args) { println!("------------------------\nTask: {}", row.description); println!("Priority: {:?}", row.priority.unwrap()); println!("Difficulty: {:?}", row.difficulty.unwrap()); - println!("Deadline: {:?}\n------------------------\n", row.deadline.unwrap()); + println!( + "Deadline: {:?}\n------------------------\n", + row.deadline.unwrap() + ); } } diff --git a/src/bin/todo/update.rs b/src/bin/todo/update.rs index 3cc7932..af2ad98 100644 --- a/src/bin/todo/update.rs +++ b/src/bin/todo/update.rs @@ -1,7 +1,6 @@ use chrono::{Local, Weekday}; use clap::Parser; -use mindmap::Task; -use mindmap::{Difficulty, Priority}; +use mindmap::{Difficulty, Priority, Task}; #[derive(Parser)] pub struct Args {} From feadbee8e8836ce307aa3fe009db764c9bdb34bd Mon Sep 17 00:00:00 2001 From: aryansri-19 Date: Sat, 6 Jul 2024 21:08:30 +0530 Subject: [PATCH 7/7] Feat: Integrate configuration module for database connection string --- src/db/mod.rs | 11 ++++++----- src/lib.rs | 16 +++++++++------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/db/mod.rs b/src/db/mod.rs index af5f83c..f857a18 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,14 +1,15 @@ -use std::env; - -use dotenv::dotenv; use tokio::sync::OnceCell; use tokio_postgres::{Client, Error, NoTls}; +use crate::configuration::get_configuration; + 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 database_url = get_configuration() + .expect("Failed to read configuration") + .database + .connection_string(); let (client, connection) = tokio_postgres::connect(&database_url, NoTls).await?; diff --git a/src/lib.rs b/src/lib.rs index 7cc4214..50458d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -195,17 +195,19 @@ impl Task { pub async fn list_tasks(date: Option) -> Result, Box> { let client = get_client().await?; + let date_params: NaiveDate; let (query, params): (&str, &[&(dyn ToSql + Sync)]) = match date { - Some(_d) => + Some(d) => { + date_params = d; ( "SELECT description, priority, difficulty, deadline FROM todo WHERE deadline = $1::date", - &[&date.unwrap()], - ), - None => - ( - "SELECT description, priority, difficulty, deadline FROM todo", - &[], + &[&date_params], ) + } + None => ( + "SELECT description, priority, difficulty, deadline FROM todo", + &[], + ), }; let rows = client .query(query, params)