diff --git a/Cargo.lock b/Cargo.lock index 505b0da..692607f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -504,6 +504,15 @@ dependencies = [ "system-deps", ] +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -606,6 +615,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bit-set" version = "0.5.3" @@ -1026,6 +1041,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "const-random" version = "0.1.18" @@ -1118,8 +1139,8 @@ version = "0.1.0" dependencies = [ "chrono", "cli-clipboard", + "cosmic-tasks-core", "dirs", - "done_core", "emojis", "env_logger", "fork", @@ -1139,6 +1160,26 @@ dependencies = [ "vergen", ] +[[package]] +name = "cosmic-tasks-core" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "chrono", + "derive-getters", + "derive_setters", + "dirs", + "emojis", + "libset", + "ron", + "serde", + "serde_json", + "sqlx", + "tracing", + "uuid", +] + [[package]] name = "cosmic-text" version = "0.11.2" @@ -1186,6 +1227,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.4.0" @@ -1223,6 +1279,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.19" @@ -1341,6 +1406,17 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + [[package]] name = "deranged" version = "0.3.11" @@ -1362,10 +1438,10 @@ dependencies = [ ] [[package]] -name = "derive-new" -version = "0.5.9" +name = "derive-getters" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" +checksum = "7a2c35ab6e03642397cdda1dd58abbc05d418aef8e36297f336d5aba060fe8df" dependencies = [ "proc-macro2", "quote", @@ -1373,62 +1449,28 @@ dependencies = [ ] [[package]] -name = "derive_setters" -version = "0.1.6" +name = "derive-new" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d" +checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" dependencies = [ - "darling", "proc-macro2", "quote", - "syn 2.0.58", -] - -[[package]] -name = "diesel" -version = "2.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03fc05c17098f21b89bc7d98fe1dd3cce2c11c2ad8e145f2a44fe08ed28eb559" -dependencies = [ - "chrono", - "diesel_derives", - "libsqlite3-sys", - "r2d2", - "time", + "syn 1.0.109", ] [[package]] -name = "diesel_derives" -version = "2.1.3" +name = "derive_setters" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d02eecb814ae714ffe61ddc2db2dd03e6c49a42e269b5001355500d431cce0c" +checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d" dependencies = [ - "diesel_table_macro_syntax", + "darling", "proc-macro2", "quote", "syn 2.0.58", ] -[[package]] -name = "diesel_migrations" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" -dependencies = [ - "diesel", - "migrations_internals", - "migrations_macros", -] - -[[package]] -name = "diesel_table_macro_syntax" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" -dependencies = [ - "syn 2.0.58", -] - [[package]] name = "digest" version = "0.10.7" @@ -1436,7 +1478,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", + "subtle", ] [[package]] @@ -1508,25 +1552,10 @@ dependencies = [ ] [[package]] -name = "done_core" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "chrono", - "diesel", - "diesel_migrations", - "emojis", - "futures", - "libset", - "serde", - "serde_json", - "strum", - "strum_macros", - "tracing", - "url", - "uuid", -] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] name = "downcast-rs" @@ -1578,6 +1607,9 @@ name = "either" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +dependencies = [ + "serde", +] [[package]] name = "emojis" @@ -1674,6 +1706,17 @@ dependencies = [ "svg_fmt", ] +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + [[package]] name = "euclid" version = "0.22.9" @@ -1809,6 +1852,12 @@ dependencies = [ "toml 0.5.11", ] +[[package]] +name = "finl_unicode" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" + [[package]] name = "fixedbitset" version = "0.4.2" @@ -1887,7 +1936,9 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ - "spin", + "futures-core", + "futures-sink", + "spin 0.9.8", ] [[package]] @@ -2048,6 +2099,17 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot 0.12.1", +] + [[package]] name = "futures-io" version = "0.3.30" @@ -2409,6 +2471,15 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown", +] + [[package]] name = "hassle-rs" version = "0.11.0" @@ -2429,6 +2500,9 @@ name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "heck" @@ -2454,6 +2528,24 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "home" version = "0.5.9" @@ -2899,6 +2991,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -3023,6 +3124,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] [[package]] name = "lebe" @@ -3138,10 +3242,11 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.28.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" dependencies = [ + "cc", "pkg-config", "vcpkg", ] @@ -3263,6 +3368,16 @@ dependencies = [ "libc", ] +[[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.2" @@ -3320,27 +3435,6 @@ dependencies = [ "paste", ] -[[package]] -name = "migrations_internals" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" -dependencies = [ - "serde", - "toml 0.7.8", -] - -[[package]] -name = "migrations_macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" -dependencies = [ - "migrations_internals", - "proc-macro2", - "quote", -] - [[package]] name = "mime" version = "0.1.0" @@ -3511,6 +3605,23 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + [[package]] name = "num-complex" version = "0.4.5" @@ -3891,6 +4002,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -3978,6 +4098,27 @@ dependencies = [ "futures-io", ] +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.30" @@ -4131,17 +4272,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "r2d2" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" -dependencies = [ - "log", - "parking_lot 0.12.1", - "scheduled-thread-pool", -] - [[package]] name = "rand" version = "0.8.5" @@ -4373,6 +4503,26 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rust-embed" version = "8.3.0" @@ -4494,15 +4644,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scheduled-thread-pool" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" -dependencies = [ - "parking_lot 0.12.1", -] - [[package]] name = "scoped-tls" version = "1.0.1" @@ -4625,6 +4766,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -4789,6 +4940,12 @@ dependencies = [ "x11rb 0.13.0", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spin" version = "0.9.8" @@ -4807,6 +4964,217 @@ dependencies = [ "bitflags 2.5.0", ] +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlformat" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" +dependencies = [ + "itertools", + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" +dependencies = [ + "ahash", + "atoi", + "byteorder", + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener 2.5.3", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashlink", + "hex", + "indexmap", + "log", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "tracing", + "url", +] + +[[package]] +name = "sqlx-macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 1.0.109", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" +dependencies = [ + "dotenvy", + "either", + "heck 0.4.1", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-sqlite", + "syn 1.0.109", + "tempfile", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" +dependencies = [ + "atoi", + "base64", + "bitflags 2.5.0", + "byteorder", + "bytes", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" +dependencies = [ + "atoi", + "base64", + "bitflags 2.5.0", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core", + "tracing", + "url", + "urlencoding", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -4828,6 +5196,17 @@ dependencies = [ "float-cmp", ] +[[package]] +name = "stringprep" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" +dependencies = [ + "finl_unicode", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "strsim" version = "0.10.0" @@ -4835,23 +5214,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] -name = "strum" -version = "0.25.0" +name = "subtle" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" - -[[package]] -name = "strum_macros" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.58", -] +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "svg_fmt" @@ -5124,18 +5490,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.15", -] - [[package]] name = "toml" version = "0.8.12" @@ -5164,8 +5518,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", - "serde", - "serde_spanned", "toml_datetime", "winnow 0.5.40", ] @@ -5200,6 +5552,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -5365,6 +5718,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + [[package]] name = "url" version = "2.5.0" @@ -5377,6 +5736,12 @@ dependencies = [ "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "usvg" version = "0.37.0" @@ -5505,6 +5870,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" @@ -5882,6 +6253,16 @@ dependencies = [ "web-sys", ] +[[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", +] + [[package]] name = "widestring" version = "1.1.0" @@ -6497,6 +6878,12 @@ dependencies = [ "syn 2.0.58", ] +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" + [[package]] name = "zune-inflate" version = "0.2.54" diff --git a/Cargo.toml b/Cargo.toml index 9c5e90b..5eceb76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ open = "5.0.2" dirs = "5.0.1" chrono = "0.4.35" tokio = "1" -done_core = { path = "src/core" } +cosmic-tasks-core = { path = "src/core" } tracing = "0.1.40" cli-clipboard = "0.4.0" slotmap = "1.0.7" diff --git a/src/app.rs b/src/app.rs index 0e80d7a..d3552d6 100644 --- a/src/app.rs +++ b/src/app.rs @@ -18,9 +18,9 @@ use cosmic::{ app, cosmic_config, cosmic_theme, executor, theme, widget, Application, ApplicationExt, Command, Element, }; -use done_core::models::list::List; -use done_core::models::task::Task; -use done_core::service::Service; +use cosmic_tasks_core::models::list::List; +use cosmic_tasks_core::models::task::Task; +use cosmic_tasks_core::service::{Provider, TaskService}; use crate::app::config::{AppTheme, CONFIG_VERSION}; use crate::app::key_bind::key_binds; @@ -38,6 +38,7 @@ pub mod settings; pub struct App { core: Core, + service: TaskService, nav_model: segmented_button::SingleSelectModel, content: Content, details: Details, @@ -57,6 +58,7 @@ pub enum Message { Details(details::Message), ToggleContextPage(ContextPage), LaunchUrl(String), + FetchLists, PopulateLists(Vec), WindowClose, WindowNew, @@ -65,7 +67,7 @@ pub enum Message { DialogUpdate(DialogPage), Key(Modifiers, Key), Modifiers(Modifiers), - AppTheme(AppTheme), + AppTheme(usize), SystemThemeModeChange(cosmic_theme::ThemeMode), OpenNewListDialog, OpenRenameListDialog, @@ -166,7 +168,7 @@ impl App { } fn about(&self) -> Element { - let cosmic_theme::Spacing { space_xxs, .. } = theme::active().cosmic().spacing; + let spacing = theme::active().cosmic().spacing; let repository = "https://github.com/edfloreshz/cosmic-tasks"; let hash = env!("VERGEN_GIT_SHA"); let short_hash: String = hash.chars().take(7).collect(); @@ -181,15 +183,15 @@ impl App { widget::text::title3(fl!("cosmic-tasks")).into(), widget::button::link(repository) .on_press(Message::LaunchUrl(repository.to_string())) - .padding(0) + .padding(spacing.space_none) .into(), widget::button::link(fl!("git-description", hash = short_hash.as_str(), date = date)) .on_press(Message::LaunchUrl(format!("{}/commits/{}", repository, hash))) - .padding(0) + .padding(spacing.space_none) .into(), ]) .align_items(Alignment::Center) - .spacing(space_xxs) + .spacing(spacing.space_xxs) .width(Length::Fill) .into() } @@ -205,13 +207,7 @@ impl App { widget::settings::item::builder(fl!("theme")).control(widget::dropdown( &self.app_themes, Some(app_theme_selected), - move |index| { - Message::AppTheme(match index { - 1 => AppTheme::Dark, - 2 => AppTheme::Light, - _ => AppTheme::System, - }) - }, + Message::AppTheme, )), ) .into()]) @@ -248,11 +244,12 @@ impl Application for App { fn init(mut core: Core, flags: Self::Flags) -> (Self, Command>) { core.nav_bar_toggle_condensed(); - let nav_model = segmented_button::ModelBuilder::default(); - + let nav_model = segmented_button::ModelBuilder::default().build(); + let service = TaskService::new(Self::APP_ID, Provider::Computer); let app = App { core, - nav_model: nav_model.build(), + service: service.clone(), + nav_model, content: Content::new(), details: Details::new(), config_handler: flags.config_handler, @@ -266,9 +263,9 @@ impl Application for App { }; let commands = vec![Command::perform( - todo::fetch_lists(), + TaskService::migrate(Self::APP_ID), |result| match result { - Ok(data) => message::app(Message::PopulateLists(data)), + Ok(_) => message::app(Message::FetchLists), Err(_) => message::none(), }, )]; @@ -346,7 +343,6 @@ impl Application for App { ), DialogPage::Icon(icon) => { let icon_buttons: Vec> = emojis::iter() - .into_iter() .map(|emoji| { widget::button( widget::container(widget::text(emoji.to_string())) @@ -569,14 +565,15 @@ impl Application for App { match content_command { content::Command::Iced(command) => return command, content::Command::GetTasks(list_id) => { - commands.push(Command::perform(todo::fetch_tasks(list_id), |result| { - match result { + commands.push(Command::perform( + todo::fetch_tasks(list_id, self.service.clone()), + |result| match result { Ok(data) => message::app(Message::Content( content::Message::SetItems(data), )), Err(_) => message::none(), - } - })); + }, + )); } content::Command::DisplayTask(task) => { let entity = @@ -599,18 +596,24 @@ impl Application for App { } content::Command::UpdateTask(task) => { self.details.task = Some(task.clone()); - let command = - Command::perform(todo::update_task(task), |result| match result { + let command = Command::perform( + todo::update_task(task, self.service.clone().clone()), + |result| match result { Ok(_) => message::none(), Err(_) => message::none(), - }); + }, + ); commands.push(command); } content::Command::Delete(id) => { if let Some(list) = self.nav_model.data::(self.nav_model.active()) { let command = Command::perform( - todo::delete_task(list.id.clone(), id.clone()), + todo::delete_task( + list.id().clone(), + id.clone(), + self.service.clone().clone(), + ), |result| match result { Ok(_) => message::none(), Err(_) => message::none(), @@ -620,11 +623,13 @@ impl Application for App { } } content::Command::CreateTask(task) => { - let command = - Command::perform(todo::create_task(task), |result| match result { + let command = Command::perform( + todo::create_task(task, self.service.clone()), + |result| match result { Ok(_) => message::none(), Err(_) => message::none(), - }); + }, + ); commands.push(command); } content::Command::Export(tasks) => { @@ -698,13 +703,27 @@ impl Application for App { log::warn!("failed to open {:?}: {}", url, err); } }, - Message::AppTheme(app_theme) => { + Message::AppTheme(index) => { + let app_theme = match index { + 1 => AppTheme::Dark, + 2 => AppTheme::Light, + _ => AppTheme::System, + }; config_set!(app_theme, app_theme); return self.update_config(); } Message::SystemThemeModeChange(_) => { return self.update_config(); } + Message::FetchLists => { + commands.push(Command::perform( + todo::fetch_lists(self.service.clone()), + |result| match result { + Ok(data) => message::app(Message::PopulateLists(data)), + Err(_) => message::none(), + }, + )); + } Message::PopulateLists(lists) => { for list in lists { self.create_nav_item(list); @@ -736,14 +755,13 @@ impl Application for App { } Message::DeleteList => { if let Some(list) = self.nav_model.data::(self.nav_model.active()) { - let command = - Command::perform( - todo::delete_list(list.id.clone()), - |result| match result { - Ok(_) => message::none(), - Err(_) => message::none(), - }, - ); + let command = Command::perform( + todo::delete_list(list.id().clone(), self.service.clone()), + |result| match result { + Ok(_) => message::none(), + Err(_) => message::none(), + }, + ); commands.push(self.update(Message::Content(content::Message::List(None)))); @@ -801,23 +819,24 @@ impl Application for App { if let Some(dialog_page) = self.dialog_pages.pop_front() { match dialog_page { DialogPage::New(name) => { - let list = List::new(&name, Service::Computer); - commands.push(Command::perform(todo::create_list(list), |result| { - match result { + let list = List::new(&name); + commands.push(Command::perform( + todo::create_list(list, self.service.clone()), + |result| match result { Ok(list) => message::app(Message::AddList(list)), Err(_) => message::none(), - } - })); + }, + )); } DialogPage::Rename { to: name } => { let entity = self.nav_model.active(); self.nav_model.text_set(entity, name.clone()); if let Some(list) = self.nav_model.active_data_mut::() { list.name = name.clone(); - let command = - Command::perform(todo::update_list(list.clone()), |_| { - message::none() - }); + let command = Command::perform( + todo::update_list(list.clone(), self.service.clone()), + |_| message::none(), + ); commands.push(command); } } @@ -832,10 +851,10 @@ impl Application for App { } if let Some(list) = self.nav_model.active_data_mut::() { list.icon = Some(icon); - let command = - Command::perform(todo::update_list(list.clone()), |_| { - message::none() - }); + let command = Command::perform( + todo::update_list(list.clone(), self.service.clone()), + |_| message::none(), + ); commands.push(command); } } diff --git a/src/app/markdown.rs b/src/app/markdown.rs index 26981f7..f85a1c6 100644 --- a/src/app/markdown.rs +++ b/src/app/markdown.rs @@ -1,6 +1,6 @@ -use done_core::models::list::List; -use done_core::models::status::Status; -use done_core::models::task::Task; +use cosmic_tasks_core::models::list::List; +use cosmic_tasks_core::models::status::Status; +use cosmic_tasks_core::models::task::Task; pub trait Markdown { fn markdown(&self) -> String; diff --git a/src/app/settings.rs b/src/app/settings.rs index 6c4f7ec..f03ccd1 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -1,9 +1,7 @@ use crate::app::icon_cache::{IconCache, ICON_CACHE}; -use crate::app::{App, Flags}; +use crate::app::Flags; use cosmic::app::Settings; use cosmic::iced::{Limits, Size}; -use cosmic::Application; -use done_core::service::Services; use std::sync::Mutex; use super::config::CosmicTasksConfig; @@ -13,7 +11,6 @@ pub fn init() -> (Settings, Flags) { set_localization(); set_icon_cache(); set_logger(); - start_services(); let settings = get_app_settings(); let flags = get_flags(); (settings, flags) @@ -38,10 +35,6 @@ pub fn set_icon_cache() { ICON_CACHE.get_or_init(|| Mutex::new(IconCache::new())); } -pub fn start_services() { - Services::init(App::APP_ID); -} - pub fn get_flags() -> Flags { let (config_handler, config) = ( CosmicTasksConfig::config_handler(), diff --git a/src/content.rs b/src/content.rs index 0b00717..512f577 100644 --- a/src/content.rs +++ b/src/content.rs @@ -2,10 +2,10 @@ use crate::app::icon_cache::IconCache; use cosmic::iced::alignment::{Horizontal, Vertical}; use cosmic::iced::{Alignment, Length, Subscription}; use cosmic::iced_widget::row; -use cosmic::{cosmic_theme, theme, widget, Apply, Element}; -use done_core::models::list::List; -use done_core::models::status::Status; -use done_core::models::task::Task; +use cosmic::{theme, widget, Apply, Element}; +use cosmic_tasks_core::models::list::List; +use cosmic_tasks_core::models::status::Status; +use cosmic_tasks_core::models::task::Task; use slotmap::{DefaultKey, SecondaryMap, SlotMap}; use crate::fl; @@ -77,12 +77,7 @@ impl Content { } pub fn list_view<'a>(&'a self, list: &'a List) -> Element<'a, Message> { - let cosmic_theme::Spacing { - space_none, - space_xxxs, - space_xxs, - .. - } = theme::active().cosmic().spacing; + let spacing = theme::active().cosmic().spacing; if self.tasks.is_empty() { return self.empty(list); @@ -90,8 +85,8 @@ impl Content { let mut items = widget::list::list_column() .style(theme::Container::ContextDrawer) - .spacing(space_xxxs) - .padding([space_none, space_xxs]); + .spacing(spacing.space_xxxs) + .padding([spacing.space_none, spacing.space_xxs]); for (id, item) in &self.tasks { let item_checkbox = @@ -100,58 +95,57 @@ impl Content { }); let delete_button = widget::button(IconCache::get("user-trash-full-symbolic", 18)) - .padding(space_xxs) + .padding(spacing.space_xxs) .style(theme::Button::Destructive) .on_press(Message::Delete(id)); let details_button = widget::button(IconCache::get("info-outline-symbolic", 18)) - .padding(space_xxs) + .padding(spacing.space_xxs) .style(theme::Button::Standard) .on_press(Message::Select(item.clone())); - let task_item_text = - widget::editable_input("", &item.title, *self.editing.get(id).unwrap_or(&false), { - let id = id.clone(); - move |editing| Message::EditMode(id, editing) - }) - .id(self.task_input_ids[id].clone()) - .on_submit(Message::TitleSubmit(id.clone())) - .on_input(move |text| Message::TitleUpdate(id, text)) - .width(Length::Fill); + let task_item_text = widget::editable_input( + "", + &item.title, + *self.editing.get(id).unwrap_or(&false), + move |editing| Message::EditMode(id, editing), + ) + .id(self.task_input_ids[id].clone()) + .on_submit(Message::TitleSubmit(id)) + .on_input(move |text| Message::TitleUpdate(id, text)) + .width(Length::Fill); let row = widget::row::with_capacity(4) .align_items(Alignment::Center) - .spacing(space_xxs) - .padding([space_xxxs, space_xxs]) + .spacing(spacing.space_xxs) + .padding([spacing.space_xxxs, spacing.space_xxs]) .push(item_checkbox) .push(task_item_text) .push(details_button) .push(delete_button); - // let button = widget::button(row) - // .padding([space_xxs, space_xs]) - // .width(Length::Fill) - // .height(Length::Shrink) - // .style(button_style(false, true)) - // .on_press(Message::Select(item.clone())); - items = items.add(row); } widget::column::with_capacity(2) - .spacing(space_xxs) + .spacing(spacing.space_xxs) .push(self.list_header(list)) .push(items) .apply(widget::container) .height(Length::Shrink) - .padding([0, space_xxs, 0, space_xxs]) + .padding([ + spacing.space_none, + spacing.space_xxs, + spacing.space_none, + spacing.space_xxs, + ]) .apply(widget::scrollable) .height(Length::Fill) .into() } pub fn empty<'a>(&'a self, list: &'a List) -> Element<'a, Message> { - let cosmic_theme::Spacing { space_xxs, .. } = theme::active().cosmic().spacing; + let spacing = theme::active().cosmic().spacing; let container = widget::container( widget::column::with_children(vec![ @@ -168,15 +162,20 @@ impl Content { .width(Length::Fill); widget::column::with_capacity(2) - .spacing(space_xxs) - .padding([0, space_xxs, 0, space_xxs]) + .spacing(spacing.space_xxs) + .padding([ + spacing.space_none, + spacing.space_xxs, + spacing.space_none, + spacing.space_xxs, + ]) .push(self.list_header(list)) .push(container) .into() } pub fn new_task_view(&self) -> Element { - let cosmic_theme::Spacing { space_xxs, .. } = theme::active().cosmic().spacing; + let spacing = theme::active().cosmic().spacing; row(vec![ widget::text_input(fl!("add-new-task"), &self.input) .on_input(Message::Input) @@ -184,13 +183,18 @@ impl Content { .width(Length::Fill) .into(), widget::button(IconCache::get("mail-send-symbolic", 18)) - .padding(space_xxs) + .padding(spacing.space_xxs) .style(theme::Button::Suggested) .on_press(Message::AddTask) .into(), ]) - .padding([space_xxs, space_xxs, 0, space_xxs]) - .spacing(space_xxs) + .padding([ + spacing.space_xxs, + spacing.space_xxs, + spacing.space_none, + spacing.space_xxs, + ]) + .spacing(spacing.space_xxs) .align_items(Alignment::Center) .into() } @@ -201,7 +205,7 @@ impl Content { Message::List(list) => { self.list = list.clone(); if let Some(list) = list { - commands.push(Command::GetTasks(list.id)); + commands.push(Command::GetTasks(list.id().clone())); } } Message::ItemDown => {} @@ -219,7 +223,7 @@ impl Content { } Message::Delete(id) => { if let Some(task) = self.tasks.remove(id) { - commands.push(Command::Delete(task.id)); + commands.push(Command::Delete(task.id().clone())); } } Message::EditMode(id, editing) => { @@ -257,7 +261,7 @@ impl Content { Message::AddTask => { if let Some(list) = &self.list { if !self.input.is_empty() { - let task = Task::new(self.input.clone(), list.id.clone()); + let task = Task::new(self.input.clone(), list.id().clone()); commands.push(Command::CreateTask(task.clone())); let id = self.tasks.insert(task); self.task_input_ids.insert(id, widget::Id::unique()); @@ -266,7 +270,10 @@ impl Content { } } Message::UpdateTask(updated_task) => { - let task = self.tasks.values_mut().find(|t| t.id == updated_task.id); + let task = self + .tasks + .values_mut() + .find(|t| t.id() == updated_task.id()); if let Some(task) = task { *task = updated_task.clone(); commands.push(Command::UpdateTask(task.clone())); diff --git a/src/core/Cargo.toml b/src/core/Cargo.toml index 07ae5d8..b117a93 100644 --- a/src/core/Cargo.toml +++ b/src/core/Cargo.toml @@ -1,23 +1,32 @@ [package] -name = "done_core" +name = "cosmic-tasks-core" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] -serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.87" -diesel = { version = "2.0.2", features = ["sqlite", "chrono", "r2d2"] } -chrono = { version = "0.4.19", features = ["serde"] } +ron = "0.8.1" anyhow = "1.0.66" -uuid = { version = "1.2.1", features = ["v4"] } -diesel_migrations = "2.0.0" tracing = "0.1.37" -strum = "0.25" -strum_macros = "0.25.2" async-trait = "0.1.68" -url = "2.3.1" -futures = "0.3" libset = "0.1.6" -emojis = "0.6.1" \ No newline at end of file +emojis = "0.6.1" +dirs = "5.0.1" +derive-getters = "0.3.0" +derive_setters = "0.1.6" + +[dependencies.sqlx] +version = "0.7.4" +features = ["sqlite"] + +[dependencies.serde] +version = "1.0.147" +features = ["derive"] + +[dependencies.chrono] +version = "0.4.19" +features = ["serde"] + +[dependencies.uuid] +version = "1.2.1" +features = ["v4"] diff --git a/src/core/diesel.toml b/src/core/diesel.toml deleted file mode 100644 index 92267c8..0000000 --- a/src/core/diesel.toml +++ /dev/null @@ -1,5 +0,0 @@ -# For documentation on how to configure this file, -# see diesel.rs/guides/configuring-diesel-cli - -[print_schema] -file = "src/schema.rs" diff --git a/src/core/migrations/2023-05-16-035137_create_lists/down.sql b/src/core/migrations/2023-05-16-035137_create_lists/down.sql deleted file mode 100644 index e360b7b..0000000 --- a/src/core/migrations/2023-05-16-035137_create_lists/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE lists; \ No newline at end of file diff --git a/src/core/migrations/2023-05-16-035137_create_lists/up.sql b/src/core/migrations/2023-05-16-035137_create_lists/up.sql deleted file mode 100644 index 12f5020..0000000 --- a/src/core/migrations/2023-05-16-035137_create_lists/up.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE lists -( - id_list TEXT NOT NULL PRIMARY KEY, - name TEXT NOT NULL, - description TEXT, - icon_name TEXT DEFAULT 'view-list-symbolic' -) \ No newline at end of file diff --git a/src/core/migrations/2023-05-16-035142_create_tasks/down.sql b/src/core/migrations/2023-05-16-035142_create_tasks/down.sql deleted file mode 100644 index 3518cd0..0000000 --- a/src/core/migrations/2023-05-16-035142_create_tasks/down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE tasks; \ No newline at end of file diff --git a/src/core/migrations/2023-05-16-035142_create_tasks/up.sql b/src/core/migrations/2023-05-16-035142_create_tasks/up.sql deleted file mode 100644 index 548b57f..0000000 --- a/src/core/migrations/2023-05-16-035142_create_tasks/up.sql +++ /dev/null @@ -1,24 +0,0 @@ -CREATE TABLE tasks ( - id_task TEXT NOT NULL CONSTRAINT tasks_pk PRIMARY KEY, - parent TEXT NOT NULL, - title TEXT NOT NULL, - notes TEXT, - priority INTEGER DEFAULT 1 NOT NULL, - favorite BOOLEAN DEFAULT false NOT NULL, - status INTEGER DEFAULT 1 NOT NULL, - completion_date TIMESTAMP, - due_date TIMESTAMP, - reminder_date TIMESTAMP, - created_date_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, - last_modified_date_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, - sub_tasks TEXT DEFAULT "[]" NOT NULL, - tags TEXT DEFAULT "[]" NOT NULL, - today BOOLEAN DEFAULT false NOT NULL, - deletion_date TIMESTAMP, - recurrence TEXT -); -CREATE UNIQUE INDEX tasks_id_uindex ON tasks (id_task); -CREATE TRIGGER remove_tasks_on_list_delete BEFORE DELETE ON lists BEGIN -DELETE FROM tasks -WHERE tasks.parent = old.id_list; -END; \ No newline at end of file diff --git a/src/core/migrations/2024-03-13-133556_update_tasks/down.sql b/src/core/migrations/2024-03-13-133556_update_tasks/down.sql deleted file mode 100644 index 8dd0a76..0000000 --- a/src/core/migrations/2024-03-13-133556_update_tasks/down.sql +++ /dev/null @@ -1,33 +0,0 @@ -CREATE TABLE temp_tasks AS -SELECT * -FROM tasks; - -DROP TABLE tasks; - -CREATE TABLE tasks -( - id_task TEXT NOT NULL - CONSTRAINT tasks_pk PRIMARY KEY, - parent TEXT NOT NULL, - title TEXT NOT NULL, - notes TEXT, - priority INTEGER DEFAULT 1 NOT NULL, - favorite BOOLEAN DEFAULT false NOT NULL, - status INTEGER DEFAULT 1 NOT NULL, - completion_date TIMESTAMP, - due_date TIMESTAMP, - reminder_date TIMESTAMP, - created_date_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, - last_modified_date_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, - sub_tasks TEXT DEFAULT "[]" NOT NULL, - tags TEXT DEFAULT "[]" NOT NULL, - today BOOLEAN DEFAULT false NOT NULL, - deletion_date TIMESTAMP, - recurrence TEXT -); - -INSERT INTO tasks -SELECT * -FROM temp_tasks; - -DROP TABLE temp_tasks; \ No newline at end of file diff --git a/src/core/migrations/2024-03-13-133556_update_tasks/up.sql b/src/core/migrations/2024-03-13-133556_update_tasks/up.sql deleted file mode 100644 index 7d7cd5c..0000000 --- a/src/core/migrations/2024-03-13-133556_update_tasks/up.sql +++ /dev/null @@ -1,40 +0,0 @@ -UPDATE tasks -SET notes = '' -WHERE notes IS NULL; - -UPDATE tasks -SET sub_tasks = replace(sub_tasks, '"notes":null', '"notes":""'); - -CREATE TABLE temp_tasks AS -SELECT * -FROM tasks; - -DROP TABLE tasks; - -CREATE TABLE tasks -( - id_task TEXT NOT NULL - CONSTRAINT tasks_pk PRIMARY KEY, - parent TEXT NOT NULL, - title TEXT NOT NULL, - notes TEXT NOT NULL, - priority INTEGER DEFAULT 1 NOT NULL, - favorite BOOLEAN DEFAULT false NOT NULL, - status INTEGER DEFAULT 1 NOT NULL, - completion_date TIMESTAMP, - due_date TIMESTAMP, - reminder_date TIMESTAMP, - created_date_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, - last_modified_date_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, - sub_tasks TEXT DEFAULT "[]" NOT NULL, - tags TEXT DEFAULT "[]" NOT NULL, - today BOOLEAN DEFAULT false NOT NULL, - deletion_date TIMESTAMP, - recurrence TEXT -); - -INSERT INTO tasks -SELECT * -FROM temp_tasks; - -DROP TABLE temp_tasks; \ No newline at end of file diff --git a/src/core/src/lib.rs b/src/core/src/lib.rs index 5ab124e..43b2ac2 100644 --- a/src/core/src/lib.rs +++ b/src/core/src/lib.rs @@ -1,5 +1,4 @@ pub mod models; -pub(crate) mod schema; pub mod service; pub mod services; pub(crate) mod task_service; diff --git a/src/core/src/models/list.rs b/src/core/src/models/list.rs index 79eb278..5fa04f4 100644 --- a/src/core/src/models/list.rs +++ b/src/core/src/models/list.rs @@ -1,37 +1,34 @@ +use derive_getters::Getters; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use crate::service::Service; - #[derive( - Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, + Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Getters, )] pub struct List { - pub id: String, - pub name: String, - pub description: String, - pub icon: Option, - pub service: Service, + pub(crate) id: String, + pub name: String, + pub description: String, + pub icon: Option, } impl FromIterator for List { - fn from_iter>(iter: T) -> Self { - let mut list = Self::default(); - for item in iter { - list.name.push_str(&item.name); - } - list - } + fn from_iter>(iter: T) -> Self { + let mut list = Self::default(); + for item in iter { + list.name.push_str(&item.name); + } + list + } } impl List { - pub fn new(name: &str, service: Service) -> Self { - Self { - id: Uuid::new_v4().to_string(), - name: name.to_string(), - service, - description: String::new(), - icon: Some(emojis::get_by_shortcode("pencil").unwrap().to_string()), - } - } -} \ No newline at end of file + pub fn new(name: &str) -> Self { + Self { + id: Uuid::new_v4().to_string(), + name: name.to_string(), + description: String::new(), + icon: Some(emojis::get_by_shortcode("pencil").unwrap().to_string()), + } + } +} diff --git a/src/core/src/models/task.rs b/src/core/src/models/task.rs index b384c4f..db82567 100644 --- a/src/core/src/models/task.rs +++ b/src/core/src/models/task.rs @@ -1,63 +1,54 @@ use chrono::{DateTime, Utc}; +use derive_getters::Getters; use serde::{Deserialize, Serialize}; use uuid::Uuid; use super::{priority::Priority, recurrence::Recurrence, status::Status}; #[derive( - Clone, - Default, - Debug, - PartialEq, - Eq, - Hash, - PartialOrd, - Ord, - Serialize, - Deserialize, + Clone, Default, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, Getters, )] pub struct Task { - pub id: String, - pub parent: String, - pub title: String, - pub favorite: bool, - pub today: bool, - pub status: Status, - pub priority: Priority, - pub sub_tasks: Vec, - pub tags: Vec, - pub notes: String, - pub completion_date: Option>, - pub deletion_date: Option>, - pub due_date: Option>, - pub reminder_date: Option>, - pub recurrence: Recurrence, - pub created_date_time: DateTime, - pub last_modified_date_time: DateTime, + pub(crate) id: String, + pub parent: String, + pub title: String, + pub favorite: bool, + pub today: bool, + pub status: Status, + pub priority: Priority, + pub sub_tasks: Vec, + pub tags: Vec, + pub notes: String, + pub completion_date: Option>, + pub due_date: Option>, + pub reminder_date: Option>, + pub recurrence: Recurrence, + pub(crate) deletion_date: Option>, + pub(crate) created_date_time: DateTime, + pub(crate) last_modified_date_time: DateTime, } impl Task { - pub fn new(title: String, parent: String) -> Self { - let now = Utc::now(); - Self { - id: Uuid::new_v4().to_string(), - parent, - title, - favorite: false, - today: false, - status: Status::NotStarted, - priority: Priority::Low, - sub_tasks: vec![], - tags: vec![], - notes: String::new(), - completion_date: None, - deletion_date: None, - due_date: None, - reminder_date: None, - recurrence: Default::default(), - created_date_time: now, - last_modified_date_time: now, - } - } + pub fn new(title: String, parent: String) -> Self { + let now = Utc::now(); + Self { + id: Uuid::new_v4().to_string(), + parent, + title, + favorite: false, + today: false, + status: Status::NotStarted, + priority: Priority::Low, + sub_tasks: vec![], + tags: vec![], + notes: String::new(), + completion_date: None, + deletion_date: None, + due_date: None, + reminder_date: None, + recurrence: Default::default(), + created_date_time: now, + last_modified_date_time: now, + } + } } - diff --git a/src/core/src/schema.rs b/src/core/src/schema.rs deleted file mode 100644 index 47dbfc4..0000000 --- a/src/core/src/schema.rs +++ /dev/null @@ -1,34 +0,0 @@ -// @generated automatically by Diesel CLI. - -diesel::table! { - lists (id_list) { - id_list -> Text, - name -> Text, - description -> Text, - icon_name -> Nullable, - } -} - -diesel::table! { - tasks (id_task) { - id_task -> Text, - parent -> Text, - title -> Text, - favorite -> Bool, - today -> Bool, - status -> Integer, - priority -> Integer, - sub_tasks -> Text, - tags -> Text, - notes -> Text, - completion_date -> Nullable, - deletion_date -> Nullable, - due_date -> Nullable, - reminder_date -> Nullable, - recurrence -> Text, - created_date_time -> Timestamp, - last_modified_date_time -> Timestamp, - } -} - -diesel::allow_tables_to_appear_in_same_query!(lists, tasks,); diff --git a/src/core/src/service.rs b/src/core/src/service.rs index ace6c7c..5dfd765 100644 --- a/src/core/src/service.rs +++ b/src/core/src/service.rs @@ -1,82 +1,137 @@ -use std::fmt::Display; -use std::sync::OnceLock; +use std::path::PathBuf; +use chrono::NaiveDateTime; use serde::{Deserialize, Serialize}; -use strum::IntoEnumIterator; -use strum_macros::{EnumIter, EnumString}; +use sqlx::{sqlite::SqliteRow, Connection}; use crate::{ - services::{ - local::service::ComputerStorage, - }, - task_service::TodoProvider, + models::{list::List, priority::Priority, recurrence::Recurrence, status::Status, task::Task}, + services::computer::ComputerStorage, + task_service::TasksProvider, }; -static APP_ID: OnceLock<&str> = OnceLock::new(); - -pub struct Services; - -impl Services { - pub fn init(app_id: &'static str) { - APP_ID.get_or_init(|| app_id); - } +#[derive(Debug, Clone)] +pub struct TaskService { + pub app_id: String, + pub provider: Provider, } -#[derive( - Debug, - Default, - EnumIter, - EnumString, - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Serialize, - Deserialize, -)] -pub enum Service { - #[default] - Computer, +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub enum Provider { + #[default] + Computer, } -impl Service { - /// Finds the requested service and returns it. - /// After implementing the Service trait in your service - /// struct, register your service here. - pub fn get_service(&self) -> Box { - if APP_ID.get().is_none() { - panic!("Must call Service::init before trying to get a service"); - } +impl TaskService { + pub fn new(app_id: &str, provider: Provider) -> Self { + Self { + app_id: app_id.to_string(), + provider, + } + } + + pub fn services<'a>() -> &'a [Provider] { + &[Provider::Computer] + } - let app_id = APP_ID.get().unwrap().to_string(); + pub fn get_service(&self) -> Option> { + match self.provider { + Provider::Computer => ComputerStorage::new(&self.app_id) + .map(|storage| Box::new(storage) as Box), + } + } - match self { - Service::Computer => Box::new(ComputerStorage::new(app_id)), - } - } + pub fn title(&self) -> &str { + match self.provider { + Provider::Computer => "Computer", + } + } + + pub fn icon(&self) -> &str { + match self.provider { + Provider::Computer => "computer-symbolic", + } + } + + pub async fn migrate(app_id: &str) -> Result<(), Box> { + if let Some(mut service) = TaskService::new(app_id, Provider::Computer).get_service() { + let database_path = dirs::config_dir() + .unwrap() + .join(app_id) + .join("v1/database/") + .join(format!("{}.db", app_id)); + let lists = get_lists(&database_path).await?; + let tasks = get_tasks(&database_path).await?; + for list in lists { + service.create_list(list.clone()).await?; + let tasks = tasks + .iter() + .filter(|task| task.parent == list.id) + .cloned() + .collect::>(); + for task in tasks { + service.create_task(task).await?; + } + } + } + Ok(()) + } +} - /// Convenience method to get the list of services. - pub fn list() -> Vec { - Self::iter().collect() - } +use sqlx::Row; - /// Returns the icon for the service. - pub fn icon(&self) -> &str { - match self { - Service::Computer => { - "/dev/edfloreshz/Done/icons/scalable/services/computer.png" - }, - } - } +async fn get_tasks(database_path: &PathBuf) -> Result, Box> { + let mut conn = sqlx::SqliteConnection::connect(database_path.to_str().unwrap()).await?; + let tasks = sqlx::query("SELECT * FROM tasks") + .map(|row: SqliteRow| Task { + id: row.get(0), + parent: row.get(1), + title: row.get(2), + notes: row.get(3), + priority: Priority::from(row.get::(4)), + favorite: row.get(5), + status: Status::from(row.get::(6)), + completion_date: NaiveDateTime::parse_from_str(row.get(7), "%Y-%m-%d %H:%M:%S.%f") + .map(|ndt| ndt.and_utc()) + .ok(), + due_date: NaiveDateTime::parse_from_str(row.get(8), "%Y-%m-%d %H:%M:%S.%f") + .map(|ndt| ndt.and_utc()) + .ok(), + reminder_date: NaiveDateTime::parse_from_str(row.get(9), "%Y-%m-%d %H:%M:%S.%f") + .map(|ndt| ndt.and_utc()) + .ok(), + created_date_time: NaiveDateTime::parse_from_str(row.get(10), "%Y-%m-%d %H:%M:%S.%f") + .unwrap() + .and_utc(), + last_modified_date_time: NaiveDateTime::parse_from_str( + row.get(11), + "%Y-%m-%d %H:%M:%S.%f", + ) + .unwrap() + .and_utc(), + sub_tasks: serde_json::from_str(row.get(12)).unwrap(), + tags: serde_json::from_str(row.get(13)).unwrap(), + today: row.get(14), + deletion_date: NaiveDateTime::parse_from_str(row.get(15), "%Y-%m-%d %H:%M:%S.%f") + .map(|ndt| ndt.and_utc()) + .ok(), + recurrence: Recurrence::from_string(row.get(16)), + }) + .fetch_all(&mut conn) + .await?; + Ok(tasks) } -impl Display for Service { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let str = match self { - Service::Computer => "Computer".to_string(), - }; - write!(f, "{}", str) - } +async fn get_lists(database_path: &PathBuf) -> Result, Box> { + let mut conn = sqlx::SqliteConnection::connect(database_path.to_str().unwrap()).await?; + let tasks = sqlx::query("SELECT * FROM lists") + .map(|row: SqliteRow| List { + id: row.get(0), + name: row.get(1), + description: row.get(2), + icon: row.get(3), + }) + .fetch_all(&mut conn) + .await?; + Ok(tasks) } diff --git a/src/core/src/services/computer.rs b/src/core/src/services/computer.rs new file mode 100644 index 0000000..43217c7 --- /dev/null +++ b/src/core/src/services/computer.rs @@ -0,0 +1,65 @@ +use anyhow::Result; +use async_trait::async_trait; + +use crate::{ + models::{list::List, task::Task}, + task_service::TasksProvider, +}; + +use self::engine::ComputerStorageEngine; + +mod engine; + +#[derive(Debug, Clone)] +pub struct ComputerStorage { + engine: ComputerStorageEngine, +} + +impl ComputerStorage { + pub(crate) fn new(application_id: &str) -> Option { + ComputerStorageEngine::new(application_id).map(|engine| Self { engine }) + } +} + +#[async_trait] +impl TasksProvider for ComputerStorage { + async fn get_task(&mut self, list_id: String, task_id: String) -> Result { + self.engine.get_task(&list_id, &task_id) + } + + async fn get_tasks_from_list(&mut self, parent_list: String) -> Result> { + self.engine.tasks(&parent_list) + } + + async fn create_task(&mut self, task: Task) -> Result { + self.engine.create_task(task) + } + + async fn update_task(&mut self, task: Task) -> Result<()> { + self.engine.update_task(task) + } + + async fn delete_task(&mut self, list_id: String, task_id: String) -> Result<()> { + self.engine.delete_task(&list_id, &task_id) + } + + async fn get_lists(&mut self) -> Result> { + self.engine.lists() + } + + async fn get_list(&mut self, id: String) -> Result { + self.engine.get_list(&id) + } + + async fn create_list(&mut self, list: List) -> Result { + self.engine.create_list(list) + } + + async fn update_list(&mut self, list: List) -> Result<()> { + self.engine.update_list(list) + } + + async fn delete_list(&mut self, id: String) -> Result<()> { + self.engine.delete_list(&id) + } +} diff --git a/src/core/src/services/computer/engine.rs b/src/core/src/services/computer/engine.rs new file mode 100644 index 0000000..ce0cb72 --- /dev/null +++ b/src/core/src/services/computer/engine.rs @@ -0,0 +1,170 @@ +use std::path::PathBuf; + +use crate::models::{list::List, task::Task}; + +#[derive(Debug, Clone)] +pub struct ComputerStorageEngine { + path: PathBuf, +} + +impl ComputerStorageEngine { + pub fn new(application_id: &str) -> Option { + let path = dirs::data_local_dir()?.join(application_id); + let engine = Self { path }; + if !engine.path.exists() { + std::fs::create_dir_all(&engine.path).ok()?; + } + if !engine.lists_path().exists() { + std::fs::create_dir_all(&engine.lists_path()).ok()?; + } + if !engine.tasks_path().exists() { + std::fs::create_dir_all(&engine.tasks_path()).ok()?; + } + Some(engine) + } + + pub fn tasks(&self, list_id: &str) -> anyhow::Result> { + let mut tasks = vec![]; + let path = self.tasks_path().join(list_id); + if !path.exists() { + return Ok(tasks); + } + for entry in path.read_dir()? { + let entry = entry?; + let path = entry.path(); + let content = std::fs::read_to_string(&path)?; + let task = ron::from_str(&content)?; + tasks.push(task); + } + Ok(tasks) + } + + pub fn lists(&self) -> anyhow::Result> { + let mut tasks = vec![]; + let path = self.lists_path(); + if !path.exists() { + return Ok(tasks); + } + for entry in self.lists_path().read_dir()? { + let entry = entry?; + let path = entry.path(); + let content = std::fs::read_to_string(&path)?; + let task = ron::from_str(&content)?; + tasks.push(task); + } + Ok(tasks) + } + + pub fn get_task(&self, list_id: &str, task_id: &str) -> anyhow::Result { + let path = self + .tasks_path() + .join(list_id) + .join(task_id) + .with_extension("ron"); + if path.exists() { + let content = std::fs::read_to_string(path)?; + let task = ron::from_str(&content)?; + Ok(task) + } else { + Err(anyhow::anyhow!("Task does not exist")) + } + } + + pub fn create_task(&self, task: Task) -> anyhow::Result { + let path = self + .tasks_path() + .join(&task.parent) + .join(&task.id) + .with_extension("ron"); + if !path.exists() { + std::fs::create_dir_all(&self.tasks_path().join(&task.parent))?; + let content = ron::to_string(&task)?; + std::fs::write(path, content)?; + Ok(task) + } else { + Err(anyhow::anyhow!("Task already exists")) + } + } + + pub fn update_task(&self, task: Task) -> anyhow::Result<()> { + let path = self + .tasks_path() + .join(&task.parent) + .join(&task.id) + .with_extension("ron"); + if path.exists() { + let content = ron::to_string(&task)?; + std::fs::write(path, content)?; + Ok(()) + } else { + Err(anyhow::anyhow!("Task does not exist")) + } + } + + pub fn delete_task(&self, list_id: &str, task_id: &str) -> anyhow::Result<()> { + let path = self + .tasks_path() + .join(list_id) + .join(task_id) + .with_extension("ron"); + if path.exists() { + std::fs::remove_file(path)?; + Ok(()) + } else { + Err(anyhow::anyhow!("Task does not exist")) + } + } + + pub fn get_list(&self, list_id: &str) -> anyhow::Result { + let path = self.lists_path().join(list_id).with_extension("ron"); + if path.exists() { + let content = std::fs::read_to_string(path)?; + let list = ron::from_str(&content)?; + Ok(list) + } else { + Err(anyhow::anyhow!("List does not exist")) + } + } + + pub fn create_list(&self, list: List) -> anyhow::Result { + let path = self.lists_path().join(&list.id).with_extension("ron"); + if !path.exists() { + let content = ron::to_string(&list)?; + std::fs::write(path, content).unwrap(); + Ok(list) + } else { + Err(anyhow::anyhow!("List already exists")) + } + } + + pub fn update_list(&self, list: List) -> anyhow::Result<()> { + let path = self.lists_path().join(&list.id).with_extension("ron"); + if path.exists() { + let content = ron::to_string(&list)?; + std::fs::write(path, content)?; + Ok(()) + } else { + Err(anyhow::anyhow!("List does not exist")) + } + } + + pub fn delete_list(&self, list_id: &str) -> anyhow::Result<()> { + let path = self.lists_path().join(list_id).with_extension("ron"); + let tasks = self.tasks_path().join(list_id); + if path.exists() { + std::fs::remove_file(path)?; + std::fs::remove_dir_all(tasks)?; + Ok(()) + } else { + Err(anyhow::anyhow!("List does not exist")) + } + } + + pub fn lists_path(&self) -> PathBuf { + self.path.join("lists") + } + + pub fn tasks_path(&self) -> PathBuf { + self.path.join("tasks") + } +} diff --git a/src/core/src/services/local/database/mod.rs b/src/core/src/services/local/database/mod.rs deleted file mode 100644 index 133d2f9..0000000 --- a/src/core/src/services/local/database/mod.rs +++ /dev/null @@ -1,95 +0,0 @@ -use anyhow::{anyhow, bail, Result}; -use diesel::r2d2; -use diesel::r2d2::{ConnectionManager, PooledConnection}; -use diesel::SqliteConnection; -use diesel_migrations::{ - embed_migrations, EmbeddedMigrations, MigrationHarness, -}; -use libset::{Config, FileType}; - -pub mod models; - -pub type Pool = r2d2::Pool>; - -pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations"); - -#[derive(Debug, Clone)] -pub struct Database { - application_id: String, - pool: Option, -} - -impl Database { - pub fn new(application_id: String) -> Result { - let database = Self { - application_id, - pool: None, - }; - - database - .ensure_database_exists() - .expect("Failed to ensure database exists."); - - Ok(database) - } - - pub fn database_url(&self) -> Result { - let app_id = self.application_id.clone(); - let url = Config::new(&app_id, 1, Some("database"))? - .path(&format!("{app_id}.db"), libset::FileType::Plain)? - .display() - .to_string(); - Ok(url) - } - - pub fn establish_connection( - &mut self, - ) -> Result>> { - if self.pool.is_none() { - let manager = - ConnectionManager::::new(self.database_url()?); - let pool = Pool::builder() - .build(manager) - .expect("Failed to create pool"); - self.pool = Some(pool); - } - - let Some(pool) = &self.pool else { - bail!("Failed to get pool"); - }; - - self - .ensure_migrations_up_to_date() - .expect("Failed to ensure migrations are up to date"); - - pool.get().map_err(|e| anyhow!(e)) - } - - pub fn ensure_migrations_up_to_date(&self) -> Result<()> { - let Some(pool) = &self.pool else { - bail!("Failed to get pool"); - }; - - let mut connection = pool.get()?; - match connection.run_pending_migrations(MIGRATIONS) { - Ok(_) => Ok(()), - Err(err) => { - tracing::error!("{err}"); - Err(anyhow!(err)) - }, - } - } - - pub fn ensure_database_exists(&self) -> Result<()> { - let app_id = self.application_id.clone(); - let database_config = Config::new(&app_id, 1, Some("database"))?; - - let database_path = - database_config.path(&format!("{app_id}.db"), FileType::Plain)?; - - if !database_path.exists() { - database_config.set_plain(&format!("{app_id}.db"), String::new())?; - } - Ok(()) - } -} diff --git a/src/core/src/services/local/database/models/list.rs b/src/core/src/services/local/database/models/list.rs deleted file mode 100644 index 6b02ccd..0000000 --- a/src/core/src/services/local/database/models/list.rs +++ /dev/null @@ -1,52 +0,0 @@ -use diesel::{Insertable, Queryable}; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; - -use crate::{models::list::List, schema::lists, service::Service}; - -#[derive(Serialize, Deserialize, Debug, Clone, Queryable, Insertable)] -#[diesel(table_name = lists)] -pub struct QueryableList { - pub id_list: String, - pub name: String, - pub description: String, - pub icon_name: Option, -} - -impl QueryableList { - pub fn new( - display_name: &str, - description: &str, - icon_name: Option, - ) -> Self { - Self { - id_list: Uuid::new_v4().to_string(), - name: display_name.to_string(), - description: description.to_string(), - icon_name, - } - } -} - -impl From for List { - fn from(value: QueryableList) -> Self { - List { - id: value.id_list, - name: value.name, - service: Service::Computer, - icon: value.icon_name, - description: value.description, - } - } -} - -impl From for QueryableList { - fn from(list: List) -> Self { - Self { - id_list: list.id, - name: list.name, - description: list.description, - icon_name: list.icon, - } - } -} diff --git a/src/core/src/services/local/database/models/mod.rs b/src/core/src/services/local/database/models/mod.rs deleted file mode 100644 index a87d979..0000000 --- a/src/core/src/services/local/database/models/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod list; - -pub mod task; diff --git a/src/core/src/services/local/database/models/task.rs b/src/core/src/services/local/database/models/task.rs deleted file mode 100644 index c602a43..0000000 --- a/src/core/src/services/local/database/models/task.rs +++ /dev/null @@ -1,106 +0,0 @@ -use chrono::{NaiveDateTime, Utc}; -use diesel::{Insertable, Queryable}; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; - -use crate::{ - models::{ - priority::Priority, recurrence::Recurrence, status::Status, task::Task, - }, - schema::tasks, -}; - -#[derive(Debug, Clone, Insertable, Queryable, Serialize, Deserialize)] -#[diesel(table_name = tasks)] -pub struct QueryableTask { - pub id_task: String, - pub parent: String, - pub title: String, - pub favorite: bool, - pub today: bool, - pub status: i32, - pub priority: i32, - pub sub_tasks: String, - pub tags: String, - pub notes: String, - pub completion_date: Option, - pub deletion_date: Option, - pub due_date: Option, - pub reminder_date: Option, - pub recurrence: String, - pub created_date_time: NaiveDateTime, - pub last_modified_date_time: NaiveDateTime, -} - -impl QueryableTask { - pub fn new(title: String, parent: String) -> Self { - let empty_vec: Vec = vec![]; - Self { - id_task: Uuid::new_v4().to_string(), - parent, - title, - favorite: false, - today: false, - notes: String::new(), - status: Status::NotStarted as i32, - priority: Priority::Low as i32, - sub_tasks: serde_json::to_string(&empty_vec).unwrap(), - tags: serde_json::to_string(&empty_vec).unwrap(), - completion_date: None, - deletion_date: None, - due_date: None, - reminder_date: None, - recurrence: String::new(), - created_date_time: Utc::now().naive_utc(), - last_modified_date_time: Utc::now().naive_utc(), - } - } -} - -impl From for QueryableTask { - fn from(value: Task) -> Self { - Self { - id_task: value.id, - parent: value.parent, - title: value.title, - favorite: value.favorite, - today: value.today, - notes: value.notes, - status: value.status.into(), - priority: value.priority.into(), - sub_tasks: serde_json::to_string(&value.sub_tasks).unwrap(), - tags: serde_json::to_string(&value.tags).unwrap(), - completion_date: value.completion_date.map(|dt| dt.naive_local()), - deletion_date: value.deletion_date.map(|dt| dt.naive_local()), - due_date: value.due_date.map(|dt| dt.naive_local()), - reminder_date: value.reminder_date.map(|dt| dt.naive_local()), - recurrence: value.recurrence.to_string(), - created_date_time: value.created_date_time.naive_local(), - last_modified_date_time: value.last_modified_date_time.naive_local(), - } - } -} - -impl From for Task { - fn from(value: QueryableTask) -> Self { - Task { - id: value.id_task, - parent: value.parent, - title: value.title, - favorite: value.favorite, - today: value.today, - notes: value.notes, - status: value.status.into(), - priority: value.priority.into(), - sub_tasks: serde_json::from_str(&value.sub_tasks).unwrap(), - tags: serde_json::from_str(&value.tags).unwrap(), - completion_date: value.completion_date.map(|ndt| ndt.and_utc()), - deletion_date: value.deletion_date.map(|ndt| ndt.and_utc()), - due_date: value.due_date.map(|ndt| ndt.and_utc()), - reminder_date: value.reminder_date.map(|ndt| ndt.and_utc()), - recurrence: Recurrence::from_string(value.recurrence), - created_date_time: value.created_date_time.and_utc(), - last_modified_date_time: value.last_modified_date_time.and_utc(), - } - } -} diff --git a/src/core/src/services/local/mod.rs b/src/core/src/services/local/mod.rs deleted file mode 100644 index 4a726db..0000000 --- a/src/core/src/services/local/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod database; -pub(crate) mod service; diff --git a/src/core/src/services/local/service.rs b/src/core/src/services/local/service.rs deleted file mode 100644 index b673d0d..0000000 --- a/src/core/src/services/local/service.rs +++ /dev/null @@ -1,201 +0,0 @@ -use std::pin::Pin; - -use anyhow::{Context, Result}; -use async_trait::async_trait; -use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl}; -use futures::Stream; -use url::Url; - -use crate::{ - models::{list::List, task::Task}, - schema::lists::dsl::lists, - schema::lists::*, - schema::tasks::dsl::tasks, - schema::tasks::*, - task_service::TodoProvider, -}; - -use super::database::{ - models::{list::QueryableList, task::QueryableTask}, - Database, -}; - -#[derive(Debug, Clone)] -pub struct ComputerStorage { - database: Database, -} - -impl ComputerStorage { - pub(crate) fn new(application_id: String) -> Self { - let database = - Database::new(application_id).expect("Failed to create database"); - - Self { database } - } -} - -#[async_trait] -impl TodoProvider for ComputerStorage { - async fn handle_uri_params(&mut self, _uri: Url) -> Result<()> { - Ok(()) - } - - fn login(&self) -> Result<()> { - Ok(()) - } - - fn logout(&self) -> Result<()> { - Ok(()) - } - - fn available(&self) -> bool { - true - } - - fn stream_support(&self) -> bool { - false - } - - async fn read_tasks(&mut self) -> Result> { - let task_list: Vec = tasks - .load::(&mut self.database.establish_connection()?)? - .iter() - .map(|t| t.clone().into()) - .collect(); - - Ok(task_list) - } - - async fn get_tasks( - &mut self, - _parent_list: String, - ) -> Result + Send>>> { - todo!("This service does not implement streams") - } - - async fn read_tasks_from_list( - &mut self, - parent_list: String, - ) -> Result> { - let response: Vec = tasks - .filter(parent.eq(parent_list)) - .load::(&mut self.database.establish_connection()?)? - .iter() - .map(|t| t.clone().into()) - .collect(); - - Ok(response) - } - - async fn read_task( - &mut self, - _task_list_id: String, - task_id: String, - ) -> Result { - let task: QueryableTask = tasks - .find(task_id) - .first(&mut self.database.establish_connection()?) - .context("Failed to fetch list of tasks.")?; - - Ok(task.into()) - } - - async fn create_task(&mut self, task: Task) -> Result<()> { - let queryable_task: QueryableTask = task.into(); - - diesel::insert_into(tasks) - .values(&queryable_task) - .execute(&mut self.database.establish_connection()?)?; - - Ok(()) - } - - async fn update_task(&mut self, task: Task) -> Result { - let original_task = task.clone(); - let queryable_task: QueryableTask = task.into(); - - diesel::update(tasks.filter(id_task.eq(queryable_task.id_task.clone()))) - .set(( - id_task.eq(queryable_task.id_task), - parent.eq(queryable_task.parent), - title.eq(queryable_task.title), - favorite.eq(queryable_task.favorite), - today.eq(queryable_task.today), - status.eq(queryable_task.status), - priority.eq(queryable_task.priority), - sub_tasks.eq(queryable_task.sub_tasks), - tags.eq(queryable_task.tags), - notes.eq(queryable_task.notes), - completion_date.eq(queryable_task.completion_date), - deletion_date.eq(queryable_task.deletion_date), - due_date.eq(queryable_task.due_date), - reminder_date.eq(queryable_task.reminder_date), - recurrence.eq(queryable_task.recurrence), - created_date_time.eq(queryable_task.created_date_time), - last_modified_date_time.eq(queryable_task.last_modified_date_time), - )) - .execute(&mut self.database.establish_connection()?) - .context("Failed to update task.")?; - - Ok(original_task) - } - - async fn delete_task( - &mut self, - _list_id: String, - task_id: String, - ) -> Result<()> { - diesel::delete(tasks.filter(id_task.eq(task_id))) - .execute(&mut self.database.establish_connection()?)?; - - Ok(()) - } - - async fn read_lists(&mut self) -> Result> { - let results = lists - .load::(&mut self.database.establish_connection()?)?; - - let results: Vec = results.iter().map(|t| t.clone().into()).collect(); - Ok(results) - } - - async fn get_lists( - &mut self, - ) -> Result + Send>>> { - todo!("This service does not implement streams") - } - - async fn read_list(&mut self, id: String) -> Result { - let result: QueryableList = lists - .find(id) - .first(&mut self.database.establish_connection()?)?; - Ok(result.into()) - } - - async fn create_list(&mut self, list: List) -> Result { - let list: QueryableList = list.into(); - - diesel::insert_into(lists) - .values(&list) - .execute(&mut self.database.establish_connection()?)?; - - Ok(list.into()) - } - - async fn update_list(&mut self, list: List) -> Result<()> { - let list: QueryableList = list.into(); - - diesel::update(lists.filter(id_list.eq(list.id_list.clone()))) - .set((name.eq(list.name.clone()), icon_name.eq(list.icon_name))) - .execute(&mut self.database.establish_connection()?) - .context("Failed to update list.")?; - - Ok(()) - } - - async fn delete_list(&mut self, id: String) -> Result<()> { - diesel::delete(lists.filter(id_list.eq(id))) - .execute(&mut self.database.establish_connection()?)?; - Ok(()) - } -} diff --git a/src/core/src/services/mod.rs b/src/core/src/services/mod.rs index 2709962..ec0ef4f 100644 --- a/src/core/src/services/mod.rs +++ b/src/core/src/services/mod.rs @@ -1 +1 @@ -pub mod local; +pub mod computer; diff --git a/src/core/src/task_service.rs b/src/core/src/task_service.rs index 2eec2a1..d62c524 100644 --- a/src/core/src/task_service.rs +++ b/src/core/src/task_service.rs @@ -1,81 +1,37 @@ -use std::pin::Pin; - use anyhow::Result; use async_trait::async_trait; -use futures::Stream; -use url::Url; use crate::models::{list::List, task::Task}; #[async_trait] -pub trait TodoProvider: Sync + Send { - /// Sets the initial config for this service. - async fn handle_uri_params(&mut self, uri: Url) -> Result<()>; - - /// Handles the login action. - fn login(&self) -> Result<()>; - - /// Handles the logout action. - fn logout(&self) -> Result<()>; - - /// Checks to see if the service is available. - fn available(&self) -> bool; - - /// Checks to see if the service is available. - fn stream_support(&self) -> bool; - - /// Read all the tasks from a service, regardless of parent list. - async fn read_tasks(&mut self) -> Result>; - - /// Returns a stream of tasks from a list and sends it through a channel. - async fn get_tasks( - &mut self, - parent_list: String, - ) -> Result + Send>>>; - - /// Read all the tasks from a list. - async fn read_tasks_from_list( - &mut self, - parent_list: String, - ) -> Result>; - - /// Reads a single task by its id. - async fn read_task( - &mut self, - task_list_id: String, - task_id: String, - ) -> Result; +pub trait TasksProvider: Sync + Send { + /// Reads a single task by its id. + async fn get_task(&mut self, task_list_id: String, task_id: String) -> Result; - /// Creates a single task. - async fn create_task(&mut self, task: Task) -> Result<()>; + /// Read all the tasks from a list. + async fn get_tasks_from_list(&mut self, parent_list: String) -> Result>; - /// Updates a single task. - async fn update_task(&mut self, task: Task) -> Result; + /// Creates a single task. + async fn create_task(&mut self, task: Task) -> Result; - /// Deltes a single task. - async fn delete_task( - &mut self, - list_id: String, - task_id: String, - ) -> Result<()>; + /// Updates a single task. + async fn update_task(&mut self, task: Task) -> Result<()>; - /// Read all the lists from a service. - async fn read_lists(&mut self) -> Result>; + /// Deltes a single task. + async fn delete_task(&mut self, list_id: String, task_id: String) -> Result<()>; - /// Returns a stream of lists and sends it through a channel. - async fn get_lists( - &mut self, - ) -> Result + Send>>>; + /// Read all the lists from a service. + async fn get_lists(&mut self) -> Result>; - /// Read a single list from a service. - async fn read_list(&mut self, id: String) -> Result; + /// Read a single list from a service. + async fn get_list(&mut self, id: String) -> Result; - /// Creates a single task list. - async fn create_list(&mut self, list: List) -> Result; + /// Creates a single task list. + async fn create_list(&mut self, list: List) -> Result; - /// Updates a single task list. - async fn update_list(&mut self, list: List) -> Result<()>; + /// Updates a single task list. + async fn update_list(&mut self, list: List) -> Result<()>; - /// Deletes a single task list. - async fn delete_list(&mut self, id: String) -> Result<()>; + /// Deletes a single task list. + async fn delete_list(&mut self, id: String) -> Result<()>; } diff --git a/src/details.rs b/src/details.rs index 346e62b..851a481 100644 --- a/src/details.rs +++ b/src/details.rs @@ -4,10 +4,10 @@ use cosmic::iced::{Alignment, Length}; use cosmic::iced_widget::row; use cosmic::widget::segmented_button; use cosmic::widget::segmented_button::Entity; -use cosmic::{cosmic_theme, theme, widget, Element}; -use done_core::models::priority::Priority; -use done_core::models::status::Status; -use done_core::models::task::Task; +use cosmic::{theme, widget, Element}; +use cosmic_tasks_core::models::priority::Priority; +use cosmic_tasks_core::models::status::Status; +use cosmic_tasks_core::models::task::Task; use slotmap::{DefaultKey, SecondaryMap, SlotMap}; use crate::fl; @@ -142,7 +142,7 @@ impl Details { Message::AddTask => { if let Some(ref mut task) = &mut self.task { if !self.subtask_input.is_empty() { - let sub_task = Task::new(self.subtask_input.clone(), task.id.clone()); + let sub_task = Task::new(self.subtask_input.clone(), task.id().clone()); task.sub_tasks.push(sub_task.clone()); let id = self.subtasks.insert(sub_task); self.sub_task_input_ids.insert(id, widget::Id::unique()); @@ -171,13 +171,7 @@ impl Details { } pub fn view(&self) -> Element { - let cosmic_theme::Spacing { - space_none, - space_xxs, - space_xs, - space_s, - .. - } = theme::active().cosmic().spacing; + let spacing = theme::active().cosmic().spacing; if let Some(task) = self.task.as_ref() { let mut sub_tasks: Vec> = vec![]; @@ -191,24 +185,21 @@ impl Details { fl!("title"), sub_task.title.clone(), *self.editing.get(id).unwrap_or(&false), - { - let id = id.clone(); - move |editing| Message::EditMode(id, editing) - }, + move |editing| Message::EditMode(id, editing), ) .id(self.sub_task_input_ids[id].clone()) .on_input(move |title| Message::SetSubTaskTitle(id, title)) .on_submit(Message::SubTaskEditDone); let delete_button = widget::button(IconCache::get("user-trash-full-symbolic", 18)) - .padding(space_xxs) + .padding(spacing.space_xxs) .style(widget::button::Style::Destructive) .on_press(Message::DeleteSubTask(id)); let row = widget::row::with_capacity(3) .align_items(Alignment::Center) - .padding([space_none, space_s]) - .spacing(space_xs) + .padding([spacing.space_none, spacing.space_s]) + .spacing(spacing.space_xs) .push(item_checkbox) .push(sub_task_item) .push(delete_button); @@ -227,7 +218,7 @@ impl Details { .on_input(Message::SetTitle) .into(), ]) - .spacing(space_xxs) + .spacing(spacing.space_xxs) .padding([0, 15, 0, 15]), ) .add( @@ -270,12 +261,12 @@ impl Details { .on_input(Message::SetNotes) .into(), ]) - .spacing(space_xxs) + .spacing(spacing.space_xxs) .padding([0, 15, 0, 15]), ) .into(), widget::settings::view_section(fl!("sub-tasks")) - .add(widget::column::with_children(sub_tasks).spacing(space_xs)) + .add(widget::column::with_children(sub_tasks).spacing(spacing.space_xs)) .into(), ]) .into(); @@ -285,12 +276,7 @@ impl Details { } fn sub_task_input(&self) -> Element { - let cosmic_theme::Spacing { - space_xxs, - space_xs, - space_s, - .. - } = theme::active().cosmic().spacing; + let spacing = theme::active().cosmic().spacing; row(vec![ widget::text_input(fl!("add-sub-task"), &self.subtask_input) @@ -300,12 +286,12 @@ impl Details { .width(Length::Fill) .into(), widget::button(IconCache::get("mail-send-symbolic", 18)) - .padding(space_xxs) + .padding(spacing.space_xxs) .on_press(Message::AddTask) .into(), ]) - .padding([0, space_s]) - .spacing(space_xs) + .padding([spacing.space_none, spacing.space_s]) + .spacing(spacing.space_xs) .align_items(Alignment::Center) .into() } diff --git a/src/todo.rs b/src/todo.rs index 9480ac3..d5e8c4b 100644 --- a/src/todo.rs +++ b/src/todo.rs @@ -1,50 +1,73 @@ use crate::app::markdown::Markdown; -use done_core::models::list::List; -use done_core::models::task::Task; -use done_core::service::Service; +use cosmic_tasks_core::models::list::List; +use cosmic_tasks_core::models::task::Task; +use cosmic_tasks_core::service::TaskService; use std::error::Error; -pub async fn update_list(list: List) -> Result<(), Box> { - let mut service = Service::Computer.get_service(); - Ok(service.update_list(list).await?) +pub async fn update_list(list: List, service: TaskService) -> Result<(), Box> { + if let Some(mut service) = service.get_service() { + service.update_list(list).await?; + } + Ok(()) } -pub async fn delete_list(id: String) -> Result<(), Box> { - let mut service = Service::Computer.get_service(); - Ok(service.delete_list(id).await?) +pub async fn delete_list(id: String, service: TaskService) -> Result<(), Box> { + if let Some(mut service) = service.get_service() { + service.delete_list(id).await?; + } + Ok(()) } -pub async fn create_list(list: List) -> Result> { - let mut service = Service::Computer.get_service(); - Ok(service.create_list(list).await?) +pub async fn create_list(list: List, service: TaskService) -> Result> { + if let Some(mut service) = service.get_service() { + let list = service.create_list(list).await?; + return Ok(list); + } + Err("No service found".into()) } -pub async fn create_task(task: Task) -> Result<(), Box> { - let mut service = Service::Computer.get_service(); - Ok(service.create_task(task).await?) +pub async fn create_task(task: Task, service: TaskService) -> Result<(), Box> { + if let Some(mut service) = service.get_service() { + service.create_task(task).await?; + } + Ok(()) } -pub async fn fetch_lists() -> Result, Box> { - let mut service = Service::Computer.get_service(); - Ok(service.read_lists().await.unwrap_or(vec![])) +pub async fn fetch_lists(service: TaskService) -> Result, Box> { + if let Some(mut service) = service.get_service() { + let lists = service.get_lists().await?; + return Ok(lists); + } + Ok(vec![]) } -pub async fn fetch_tasks(list_id: String) -> Result, Box> { - let mut service = Service::Computer.get_service(); - Ok(service - .read_tasks_from_list(list_id) - .await - .unwrap_or(vec![])) +pub async fn fetch_tasks( + list_id: String, + service: TaskService, +) -> Result, Box> { + if let Some(mut service) = service.get_service() { + let tasks = service.get_tasks_from_list(list_id).await?; + return Ok(tasks); + } + Ok(vec![]) } -pub async fn update_task(task: Task) -> Result> { - let mut service = Service::Computer.get_service(); - Ok(service.update_task(task).await?) +pub async fn update_task(task: Task, service: TaskService) -> Result<(), Box> { + if let Some(mut service) = service.get_service() { + service.update_task(task).await?; + } + Ok(()) } -pub async fn delete_task(list_id: String, task_id: String) -> Result<(), Box> { - let mut service = Service::Computer.get_service(); - Ok(service.delete_task(list_id, task_id).await?) +pub async fn delete_task( + list_id: String, + task_id: String, + service: TaskService, +) -> Result<(), Box> { + if let Some(mut service) = service.get_service() { + service.delete_task(list_id, task_id).await?; + } + Ok(()) } pub fn export_list(list: List, tasks: Vec) -> String {