From ebe10335dc8d3c8fbe574bd2cc9116d37eb29c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Kr=C3=B6ppl?= Date: Mon, 11 Mar 2024 05:28:55 +0400 Subject: [PATCH] #progress --- .vscode/extensions.json | 1 - Cargo.lock | 426 ++++++++++++++++++++++++++++++- cli/Cargo.toml | 24 +- cli/README.md | 1 + cli/src/cli/mod.rs | 47 ++++ cli/src/main.rs | 40 +-- cli/src/timesheet/command.rs | 144 +++++++++++ cli/src/timesheet/mod.rs | 1 + lib/Cargo.toml | 17 +- lib/README.md | 0 lib/src/cache/mod.rs | 1 + lib/src/config/mod.rs | 1 + lib/src/lib.rs | 10 +- lib/src/time_entry/activity.rs | 9 +- lib/src/time_entry/mod.rs | 2 + lib/src/time_entry/time_entry.rs | 22 +- 16 files changed, 675 insertions(+), 71 deletions(-) create mode 100644 cli/README.md create mode 100644 cli/src/cli/mod.rs create mode 100644 cli/src/timesheet/command.rs create mode 100644 lib/README.md create mode 100644 lib/src/cache/mod.rs create mode 100644 lib/src/config/mod.rs diff --git a/.vscode/extensions.json b/.vscode/extensions.json index a9ebff2..2143a50 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,7 +3,6 @@ "EditorConfig.EditorConfig", "serayuzgur.crates", "rust-lang.rust-analyzer", - "ms-azuretools.vscode-docker", "kokakiwi.vscode-just", "github.vscode-github-actions" ] diff --git a/Cargo.lock b/Cargo.lock index 14bc486..9706546 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,21 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.13" @@ -65,6 +80,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "anyhow" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" + [[package]] name = "arrayref" version = "0.3.7" @@ -151,6 +172,71 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-options" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad71bf996c8e5b9d28ef3472d7ee41f277edf4e38cd597f51ad0438d05d76ea" +dependencies = [ + "anstyle", + "clap", +] + +[[package]] +name = "cargo-platform" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-zigbuild" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb76e6ab558f9138291c7e1fa954ffd58e060712eab13f97a317da712218ca24" +dependencies = [ + "anyhow", + "cargo-options", + "cargo_metadata", + "clap", + "dirs 5.0.1", + "fat-macho", + "fs-err", + "path-slash", + "rustc_version", + "semver", + "serde", + "serde_json", + "shlex", + "target-lexicon", + "which", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "cc" version = "1.0.90" @@ -163,6 +249,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.4", +] + [[package]] name = "clap" version = "4.5.2" @@ -183,6 +284,7 @@ dependencies = [ "anstyle", "clap_lex", "strsim", + "terminal_size", ] [[package]] @@ -212,6 +314,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +[[package]] +name = "clap_mangen" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1dd95b5ebb5c1c54581dd6346f3ed6a79a3eef95dd372fc2ac13d535535300e" +dependencies = [ + "clap", + "roff", +] + [[package]] name = "clippy" version = "0.0.302" @@ -283,10 +395,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" dependencies = [ "libc", - "redox_users", + "redox_users 0.3.5", "winapi", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.4.4", + "windows-sys 0.48.0", +] + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + [[package]] name = "encoding_rs" version = "0.8.33" @@ -318,6 +457,15 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "fat-macho" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4c93f393add03d72bc10dd3dea43a1610ecb29e0c0a6459c70b53b82931adf" +dependencies = [ + "goblin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -348,6 +496,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs-err" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41" +dependencies = [ + "autocfg", +] + [[package]] name = "futures-channel" version = "0.3.30" @@ -398,12 +555,34 @@ dependencies = [ "wasi 0.9.0+wasi-snapshot-preview1", ] +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "goblin" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb07a4ffed2093b118a525b1d8f5204ae274faed5604537caf7135d0f18d9887" +dependencies = [ + "log", + "plain", + "scroll", +] + [[package]] name = "h2" version = "0.3.24" @@ -435,6 +614,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "http" version = "0.2.12" @@ -506,6 +694,29 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "idna" version = "0.5.0" @@ -532,6 +743,15 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "iso8601" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153" +dependencies = [ + "nom", +] + [[package]] name = "itoa" version = "1.0.10" @@ -559,6 +779,17 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.2", + "libc", + "redox_syscall 0.4.1", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -583,6 +814,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.2" @@ -621,6 +858,25 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.32.2" @@ -640,20 +896,28 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" name = "openproject-client-cli" version = "0.0.0" dependencies = [ + "cargo-zigbuild", + "chrono", "clap", "clap_complete", + "clap_mangen", "clippy", "csv", + "openproject-client-lib", "serde", - "xdg", ] [[package]] name = "openproject-client-lib" version = "0.0.0" dependencies = [ + "cargo-zigbuild", + "chrono", "clippy", + "dirs 5.0.1", + "iso8601", "reqwest", + "serde", ] [[package]] @@ -700,6 +964,18 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "path-slash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -724,6 +1000,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "proc-macro2" version = "1.0.78" @@ -748,17 +1030,37 @@ version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +[[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_users" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" dependencies = [ - "getrandom", - "redox_syscall", + "getrandom 0.1.16", + "redox_syscall 0.1.57", "rust-argon2", ] +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom 0.2.12", + "libredox", + "thiserror", +] + [[package]] name = "reqwest" version = "0.11.24" @@ -799,6 +1101,12 @@ dependencies = [ "winreg", ] +[[package]] +name = "roff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316" + [[package]] name = "rust-argon2" version = "0.8.3" @@ -817,6 +1125,15 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.31" @@ -854,6 +1171,26 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "scroll" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "security-framework" version = "2.9.2" @@ -877,6 +1214,15 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +dependencies = [ + "serde", +] + [[package]] name = "serde" version = "1.0.197" @@ -920,6 +1266,12 @@ dependencies = [ "serde", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "slab" version = "0.4.9" @@ -983,6 +1335,12 @@ dependencies = [ "libc", ] +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + [[package]] name = "tempfile" version = "3.10.1" @@ -1002,10 +1360,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" dependencies = [ "byteorder", - "dirs", + "dirs 1.0.5", "winapi", ] +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "thiserror" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -1232,6 +1620,19 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "which" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fa5e0c10bf77f44aac573e498d1a82d5fbd5e91f6fc0a99e7be4b38e85e101c" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1254,6 +1655,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.4", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -1395,9 +1805,3 @@ dependencies = [ "cfg-if", "windows-sys 0.48.0", ] - -[[package]] -name = "xdg" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 4050c73..4d4a08e 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,9 +1,12 @@ [package] name = "openproject-client-cli" version = "0.0.0" -description = "Command line interface for OpenProject" -repository = "https://github.com/HMG-Software/op-rs" +description = "CLI for OpenProject" +documentation = "https://docs.rs/openproject-client-cli" homepage = "https://github.com/HMG-Software/op-rs/tree/master/cli" +repository = "https://github.com/HMG-Software/op-rs" +readme = "README.md" +resolve = "2" license = "GPL-3.0-only" authors = [ "Christoph Kröppl ", @@ -15,13 +18,25 @@ publish = false [dependencies] clap = { version = "4", features = ["cargo", "derive"] } clap_complete = "4" +clap_mangen = "0.2" +chrono = { version = "0.4", features = ["serde"] } csv = "1" serde = { version = "1", features = ["derive"] } -xdg = "2" + +[dependencies.lib] +features = ["cache", "config"] +package = "openproject-client-lib" +# version = "0.1.0" +path = "../lib" [dev-dependencies] clippy = "*" +[features] +cache = ["lib/cache"] +config = ["lib/config"] +default = ["cache", "config"] + [[bin]] name = "openproject-cli" path = "src/main.rs" @@ -31,3 +46,6 @@ codegen-units = 1 lto = true strip = true debug = false + +[target.universal2-apple-darwin.build-dependencies] +cargo-zigbuild = "0" diff --git a/cli/README.md b/cli/README.md new file mode 100644 index 0000000..f35f19a --- /dev/null +++ b/cli/README.md @@ -0,0 +1 @@ +future build value enums for TS activity (since they need to be fetched from OP) diff --git a/cli/src/cli/mod.rs b/cli/src/cli/mod.rs new file mode 100644 index 0000000..8cd089b --- /dev/null +++ b/cli/src/cli/mod.rs @@ -0,0 +1,47 @@ +use clap::{Parser, Subcommand}; +use std::path::PathBuf; + +const ABOUT: &str = env!("CARGO_PKG_DESCRIPTION"); +const NAME: &str = env!("CARGO_PKG_NAME"); + +#[derive(Parser)] +#[command( + about = ABOUT, + author, + name = NAME, + long_about = None, + version, +)] +#[command(propagate_version = true)] +pub(crate) struct Cli { + /// Provide an OpenProject API Token or store it in + /// `~/.op/token` + #[arg( + global = true, + hide_default_value = true, + long, + short = None, + value_name = "API-TOKEN", + )] + token: Option, + + #[command(subcommand)] + command: Option, +} + +// Add new subcommands here +#[derive(Subcommand)] +enum Commands { + #[command( + aliases = ["timesheet", "ts"], + arg_required_else_help = true, + subcommand, + )] + TimeEntry(crate::timesheet::command::TimeEntry), +} + +#[test] +fn verify_cli() { + use clap::CommandFactory; + Cli::command().debug_assert() +} diff --git a/cli/src/main.rs b/cli/src/main.rs index c3b97fe..b8f071e 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,44 +1,14 @@ -use clap::{Parser, Subcommand}; +mod cli; +mod timesheet; +use clap::Parser; -const AUTHOR: &str = env!("CARGO_PKG_AUTHORS"); -const DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION"); -const VERSION: &str = env!("CARGO_PKG_VERSION"); -const ABOUT: &str = &format!( - "{}\nPass `-h` for help", - DESCRIPTION, -); - - -#[derive(Parser)] -#[command( - about = ABOUT, - author = AUTHOR, - long_about = None, - version = VERSION, -)] -struct Args { - #[command(subcommand)] - cmd: Commands, - #[clap(long, short)] - token: Option, -} - -#[derive(Subcommand, Debug, Clone)] -enum Commands { - Timesheet, -} +use cli::Cli; fn main() { - let cli = Cli::parse(); - - match cli.cmd { - Commands::Get(value) => get_something(value), - Commands::Set{key, value, is_true} => set_something(key, value, is_true), - } + let _cli = Cli::parse(); } - // https://docs.rs/clap/latest/clap/_faq/index.html#when-should-i-use-the-builder-vs-derive-apis // https://docs.rs/clap_complete/latest/clap_complete/generator/fn.generate_to.html diff --git a/cli/src/timesheet/command.rs b/cli/src/timesheet/command.rs new file mode 100644 index 0000000..072db08 --- /dev/null +++ b/cli/src/timesheet/command.rs @@ -0,0 +1,144 @@ +use chrono::{DateTime, TimeDelta, Utc}; +use clap::{Args, Parser, Subcommand}; +use std::path::PathBuf; + +// const DURATION_REGEX: &str = r"(?:(?P\d*\.?\d+?)h)?(?:(?P\d*\.?\d+?)m)?"; + +/// Work with OpenProject Time Entries (Timesheets) +#[derive(Debug, Subcommand)] +pub(crate) enum TimeEntry { + #[command(alias = "c")] + Create(Create), + + #[command(alias = "f", arg_required_else_help = true)] + Fetch(Fetch), +} + +/// Create a new time entry +/// either by providing a CSV file or +/// or by providing the required args +#[derive(Args, Debug)] +pub(crate) struct Create { + /// Activity name or ID + #[arg( + long, + requires_all = &["hours", "work_package"], + short = 'a', + // value_parser = parse_activity, + )] + activity: Option, + + /// Comment describing the time entry + #[arg(long, short = 'm')] + comment: Option, + + /// Time spent on the work package + #[arg( + aliases = &["delta", "duration"], + long, + short = 'd', + value_name = "DURATION", + value_parser = parse_hours, + )] + hours: Option, + + #[arg( + long, + short = None, + value_name = "DATE_TIME", + )] + spent_on: Option>, + + #[arg( + default_value = Some(default_user()), + long, + requires_all = &["activity", "hours", "work_package"], + short = 'u', + value_name = "USER_ID", + )] + user: Option, + + #[arg( + aliases = &["wp", "ticket"], + long, + requires_all = &["activity", "hours"], + short = None, + value_name = "WORK_PACKAGE_ID", + )] + work_package: Option, + + /// Path to the CSV file + /// containing i or more time entries + #[arg( + // either provide a CSV file or the other (required or optional) args + exclusive = true, + long, + required = false, + short = 'f', + value_name = "CSV_FILE", + value_hint = clap::ValueHint::FilePath, + )] + csv_path: Option, +} + +#[derive(Args, Debug)] +pub(crate) struct Fetch { + #[arg( + default_value = default_user(), + long, + required = true, + short = None, + )] + user: Option, + + #[arg( + long, + required = true, + short = None, + )] + work_package: Option, +} + +/// parse a string into TimeDelta +/// +/// possible inputs: +/// - "45m" (45 minutes) +/// - "1" (1 hour) +/// - "1h" (1 hour) +/// - "1.5" (1.5 hours) +/// - "1.5h" (1.5 hours) +/// - "1h30" (1 hour, 30 minutes) +/// - "1h30m" (1 hour, 30 minutes) +/// - ".25" (45 minutes) +/// - ".75h" (45 minutes) +/// - ":30" (30 minutes) +/// - ":30m" (30 minutes) +/// - "1:30" (1 hour, 30 minutes) +pub(crate) fn parse_hours(s: &str) -> Result { + let seconds = 900; + + let delta = TimeDelta::new(seconds, 0).ok_or("Time delta is 0")?; + + #[cfg(feature = "config")] + validate_hours(delta)?; + + Ok(delta) +} + +pub(crate) fn default_user() -> &'static str { + #[cfg(feature = "config")] + { + println!("DEMO: get pre-defined user from config") + } + "me" +} + +#[cfg(feature = "config")] +fn validate_hours(delta: TimeDelta) -> Result<(), String> { + // if feature "config" is enabled, throw an error if time is not evenly divisible by 15 minutes + // (15 minutes == 900 seconds) + if delta.num_seconds() % 900 != 0 { + return Err("Time must be evenly divisible by 15 minutes".to_string()); + } + Ok(()) +} diff --git a/cli/src/timesheet/mod.rs b/cli/src/timesheet/mod.rs index e69de29..d5ef1bb 100644 --- a/cli/src/timesheet/mod.rs +++ b/cli/src/timesheet/mod.rs @@ -0,0 +1 @@ +pub(crate) mod command; diff --git a/lib/Cargo.toml b/lib/Cargo.toml index dfb0ba0..9f95479 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -2,8 +2,11 @@ name = "openproject-client-lib" version = "0.0.0" description = "OpenProject (C) client library" -repository = "https://github.com/HMG-Software/op-rs" +documentation = "https://docs.rs/openproject-client-lib" homepage = "https://github.com/HMG-Software/op-rs/tree/master/lib" +repository = "https://github.com/HMG-Software/op-rs" +readme = "README.md" +resolve = "2" license = "GPL-3.0-only" authors = [ "Christoph Kröppl ", @@ -15,11 +18,20 @@ keywords = ["abi", "ffi", "libopc", "op", "openproject"] categories = ["development-tools", "database"] [dependencies] +chrono = { version = "0.4", features = ["serde"] } +dirs = { version = "5", optional = true } +iso8601 = "0" reqwest = "0" +serde = "1" [dev-dependencies] clippy = "*" +[features] +default = [] +config = ["dep:dirs"] +cache = ["dep:dirs"] + [lib] crate-type = ["cdylib"] name = "opc" # "lib" gets prepended by cargo @@ -32,3 +44,6 @@ path = "src/lib.rs" codegen-units = 1 lto = true strip = true + +[target.universal2-apple-darwin.build-dependencies] +cargo-zigbuild = "0" diff --git a/lib/README.md b/lib/README.md new file mode 100644 index 0000000..e69de29 diff --git a/lib/src/cache/mod.rs b/lib/src/cache/mod.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/src/cache/mod.rs @@ -0,0 +1 @@ + diff --git a/lib/src/config/mod.rs b/lib/src/config/mod.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/src/config/mod.rs @@ -0,0 +1 @@ + diff --git a/lib/src/lib.rs b/lib/src/lib.rs index e7a11a9..0c4ffdf 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -1,3 +1,7 @@ -fn main() { - println!("Hello, world!"); -} +#[cfg(feature = "cache")] +pub mod cache; + +#[cfg(feature = "config")] +pub mod config; + +pub mod time_entry; diff --git a/lib/src/time_entry/activity.rs b/lib/src/time_entry/activity.rs index d6d3e64..02455c9 100644 --- a/lib/src/time_entry/activity.rs +++ b/lib/src/time_entry/activity.rs @@ -2,13 +2,12 @@ use serde::Deserialize; - #[derive(Debug, serde::Deserialize)] struct Timesheet { - id: i8, + id: isize, name: String, - position: i8, + position: isize, default: bool, - #[serde(with = "OpenProjectLinkTitle")] - projects: vec, + // #[serde(with = "OpenProjectLinkTitle")] + // projects: vec, } diff --git a/lib/src/time_entry/mod.rs b/lib/src/time_entry/mod.rs index e69de29..8c8919c 100644 --- a/lib/src/time_entry/mod.rs +++ b/lib/src/time_entry/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod activity; +pub(crate) mod time_entry; diff --git a/lib/src/time_entry/time_entry.rs b/lib/src/time_entry/time_entry.rs index a9447c3..2dfeecb 100644 --- a/lib/src/time_entry/time_entry.rs +++ b/lib/src/time_entry/time_entry.rs @@ -1,26 +1,24 @@ // https://www.openproject.org/docs/api/endpoints/time-entries/ +use chrono::{DateTime, Duration, TimeDelta, Utc}; use serde::Deserialize; - #[derive(Debug, serde::Deserialize)] struct Timesheet { activity: String, - #[serde(with = "ISO8601DateTime")] + // #[serde(with = "ISO8601DateTime")] created_at: DateTime, - #[serde(with = "OpenProjectText")] + // #[serde(with = "OpenProjectText")] comment: String, - #[serde(with = "ISO8601DateTime")] - pub hours: DateTime, - id: i8, - #[serde(with = "ISO8601DateTime")] + // #[serde(with = "ISO8601DateTime")] + //pub hours: Duration, + id: isize, + // #[serde(with = "ISO8601DateTime")] pub spent_on: DateTime, - ticket: i8, - #[serde(default="me")] + // #[serde(default="me")] user: String, - #[serde(with = "OpenProjectLinkTitle")] - work_package: String, + // #[serde(with = "OpenProjectLinkTitle")] + work_package: isize, } - // https://serde.rs/custom-date-format.html