diff --git a/Cargo.lock b/Cargo.lock index 0ddd1a1..a642323 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,16 +1,22 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] +[[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" @@ -20,117 +26,159 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + [[package]] name = "anyhow" -version = "1.0.69" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "assert_cmd" -version = "2.0.8" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9834fcc22e0874394a010230586367d4a3e9f11b560f469262678547e1d2575e" +checksum = "2bd389a4b2970a01282ee455294913c0a43724daedcd1a24c3eb0ec1c1320b66" dependencies = [ + "anstyle", "bstr", "doc-comment", + "libc", "predicates", "predicates-core", "predicates-tree", "wait-timeout", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "autocfg" -version = "1.1.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bstr" -version = "1.3.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", - "once_cell", "regex-automata", "serde", ] [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "cc" -version = "1.0.79" +version = "1.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", - "time", "wasm-bindgen", - "winapi", + "windows-link", ] [[package]] name = "clap" -version = "4.1.8" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ - "bitflags", + "clap_builder", "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +dependencies = [ + "anstream", + "anstyle", "clap_lex", - "is-terminal", - "once_cell", "strsim", - "termcolor", ] [[package]] name = "clap_derive" -version = "4.1.8" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", "syn", @@ -138,83 +186,30 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.2" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" -dependencies = [ - "os_str_bytes", -] +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] -name = "codespan-reporting" -version = "0.11.1" +name = "colorchoice" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "colored" -version = "2.0.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" dependencies = [ - "atty", - "lazy_static", - "winapi", + "windows-sys", ] [[package]] name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "cxx" -version = "1.0.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.92" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "difflib" @@ -228,62 +223,20 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "float-cmp" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" dependencies = [ "num-traits", ] [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hrs" @@ -301,109 +254,67 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", - "winapi", + "windows-core", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" -dependencies = [ - "cxx", - "cxx-build", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "is-terminal" -version = "0.4.4" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys", + "cc", ] [[package]] -name = "itertools" -version = "0.10.5" +name = "is_terminal_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.140" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" - -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - -[[package]] -name = "linux-raw-sys" -version = "0.1.4" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "log" -version = "0.4.17" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "normalize-line-endings" @@ -411,46 +322,36 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" -version = "1.17.1" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] -name = "os_str_bytes" -version = "6.4.1" +name = "once_cell_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "predicates" -version = "2.1.5" +version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" dependencies = [ + "anstyle", "difflib", "float-cmp", - "itertools", "normalize-line-endings", "predicates-core", "regex", @@ -458,206 +359,163 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" [[package]] name = "predicates-tree" -version = "1.0.7" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" dependencies = [ "predicates-core", "termtree", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "regex" -version = "1.7.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", + "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.1.10" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] -name = "rustix" -version = "0.36.9" +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "serde" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys", + "serde_derive", ] [[package]] -name = "scratch" -version = "1.0.5" +name = "serde_derive" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "serde" -version = "1.0.155" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71f2b4817415c6d4210bfe1c7bfcf4801b2d904cb4d0e1a8fdb651013c9e86b8" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "1.0.109" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - [[package]] name = "termtree" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" - -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi", - "winapi", -] +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "unicode-ident" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" - -[[package]] -name = "unicode-width" -version = "0.1.10" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] -name = "version_check" -version = "0.9.4" +name = "utf8parse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "wait-timeout" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" dependencies = [ "libc", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -666,9 +524,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -676,9 +534,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -689,59 +547,91 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] -name = "winapi" -version = "0.3.9" +name = "windows-core" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "windows-implement" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "winapi-util" -version = "0.1.5" +name = "windows-interface" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ - "winapi", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] [[package]] name = "windows-sys" -version = "0.45.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", + "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", @@ -750,42 +640,48 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml index e76ff07..1cdb2a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,13 +6,13 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -anyhow = "1.0" -chrono = "0.4.23" -clap = { version = "4.0", features = ["derive"] } -colored = "2.0.0" -lazy_static = "1.4.0" -regex = "1.7.1" +anyhow = "1.0.98" +chrono = "0.4.35" +clap = { version = "4.5.0", features = ["derive"] } +colored = "3.0.0" +lazy_static = "1.5.0" +regex = "1.10.3" [dev-dependencies] -assert_cmd = "2.0.7" -predicates = "2.1.5" +assert_cmd = "2.0.14" +predicates = "3.1.0" diff --git a/src/lib.rs b/src/lib.rs index ff84ee4..10358a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,9 @@ use regex::Regex; use std::collections::HashMap; use std::fmt; +pub mod output; +pub use output::{print_processed_lines, print_summary, print_total_and_diff}; + pub fn find_and_collect_day<'a>(content: &'a str, date: &str) -> Vec<&'a str> { let mut in_day = false; let mut lines_in_day = Vec::new(); @@ -28,23 +31,25 @@ pub fn find_and_collect_day<'a>(content: &'a str, date: &str) -> Vec<&'a str> { lines_in_day } -pub fn process_lines( - lines: Vec<&str>, - durations_by_tag: &mut HashMap>, - mut writer: impl std::io::Write, -) { +pub fn process_lines<'a>( + lines: Vec<&'a str>, +) -> (Vec<(Duration, &'a str)>, HashMap>) { + let mut durations_by_tag: HashMap> = HashMap::new(); let mut prev_tag: Option = None; + let mut processed_lines = Vec::new(); for line in lines { - process_line(line, &mut prev_tag, durations_by_tag, &mut writer); + if let Some(duration) = process_line(line, &mut prev_tag, &mut durations_by_tag) { + processed_lines.push((duration, line)); + } } + (processed_lines, durations_by_tag) } fn process_line( line: &str, prev_tag: &mut Option, durations_by_tag: &mut HashMap>, - mut writer: impl std::io::Write, -) { +) -> Option { lazy_static! { static ref LINE_RE: Regex = Regex::new(r"^([0-9\.]{1,5})-([0-9\.]{1,5})\s+(\[.*?\])?.*$").unwrap(); @@ -53,12 +58,12 @@ fn process_line( let caps = LINE_RE.captures(line); if let Some(caps) = caps { - let line = &caps[0]; + let line_match = &caps[0]; let start = &caps[1]; let end = &caps[2]; let mut tag = match caps.get(3) { Some(tag) => tag.as_str(), - None => &line[format!("{}-{} ", start, end).len()..], + None => &line_match[format!("{}-{} ", start, end).len()..], } .to_owned(); if let Some(ref pt) = prev_tag { @@ -90,98 +95,38 @@ fn process_line( let durations = durations_by_tag.entry(tag).or_insert(Vec::new()); durations.push(duration); - writeln!(writer, "{} {}", HumanDuration(duration).line(), &line); + Some(duration) + } else { + None } } -pub fn write_durations_collect_total( +pub fn summarize_durations( durations_by_tag: &HashMap>, - mut writer: impl std::io::Write, -) -> Duration { +) -> (Vec<(String, Duration)>, Duration) { let mut durations_by_tag: Vec<_> = durations_by_tag.iter().collect(); durations_by_tag.sort_by(|a, b| a.0.cmp(b.0)); let mut duration_total = Duration::minutes(0); + let mut summary = Vec::new(); for (tag, durations) in durations_by_tag { let mut duration = Duration::minutes(0); for d in durations { duration = duration + *d; } duration_total = duration_total + duration; - writeln!(writer, "{} {}", HumanDuration(duration).tag(), tag); + summary.push((tag.clone(), duration)); } - duration_total + (summary, duration_total) } -pub fn write_total(total: Duration, mut writer: impl std::io::Write) { - let full_day = Duration::hours(7) + Duration::minutes(30); - let diff = total - full_day; - - if diff == Duration::zero() { - writeln!(writer, "{}", HumanDuration(total).total()); - return; - } - writeln!( - writer, - "{} {}", - HumanDuration(total).total(), - HumanDuration(diff).diff() - ); -} - -struct HumanDuration(Duration); -impl HumanDuration { - fn plain(&self) -> String { - format!("{}", self) - } - fn line(&self) -> ColoredString { - self.plain().bold().green() - } - fn tag(&self) -> ColoredString { - self.plain().bold().blue() - } - fn total(&self) -> ColoredString { - self.plain().bold().white() - } - fn diff(&self) -> ColoredString { - if self.0 < Duration::zero() { - format!("-{}", self).red() - } else { - format!("+{}", self).green() - } - } -} -impl fmt::Display for HumanDuration { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{:02}:{:02}", - self.0.num_hours().abs(), - self.0.num_minutes().abs() % 60 - ) - } -} #[cfg(test)] mod tests { use super::*; - #[test] - fn test_human_duration() { - assert_eq!(HumanDuration(Duration::minutes(1)).plain(), "00:01"); - assert_eq!(HumanDuration(Duration::minutes(15)).plain(), "00:15"); - assert_eq!(HumanDuration(Duration::hours(1)).plain(), "01:00"); - assert_eq!(HumanDuration(Duration::minutes(135)).plain(), "02:15"); - assert_eq!(HumanDuration(Duration::hours(10)).plain(), "10:00"); - - let hour = (Duration::hours(1), "01:00"); - assert_eq!(HumanDuration(hour.0).line(), hour.1.bold().green()); - assert_eq!(HumanDuration(hour.0).tag(), hour.1.bold().blue()); - assert_eq!(HumanDuration(hour.0).total(), hour.1.bold().white()); - assert_eq!(HumanDuration(hour.0).diff(), format!("+{}", hour.1).green()); - assert_eq!(HumanDuration(-hour.0).diff(), format!("-{}", hour.1).red()); - } + #[test] fn test_find_and_collect_day() { @@ -219,78 +164,71 @@ baz #[test] fn test_process_line() { - let mut out = Vec::new(); let mut prev_tag = None; let mut durations_by_tag = HashMap::new(); macro_rules! line { ($line:tt) => { - process_line($line, &mut prev_tag, &mut durations_by_tag, &mut out); + process_line($line, &mut prev_tag, &mut durations_by_tag).unwrap() }; } - let expected_output = |d: Duration, line: &str| { - format!("{} {}\n", HumanDuration(d).line(), line).into_bytes() - }; let line = "8-9 desc without tag 1"; - line!(line); - assert_eq!(out, expected_output(Duration::hours(1), line)); - assert_eq!(prev_tag, Some(line[4..].to_owned())); - assert_eq!(durations_by_tag[&line[4..]], vec![Duration::hours(1)]); - - out.clear(); + let duration = line!(line); + assert_eq!(duration, Duration::hours(1)); + assert_eq!(prev_tag, Some("desc without tag 1".to_owned())); + assert_eq!( + durations_by_tag["desc without tag 1"], + vec![Duration::hours(1)] + ); let line = "9-9.30 [tag1] desc"; - line!(line); - assert_eq!(out, expected_output(Duration::minutes(30), line)); + let duration = line!(line); + assert_eq!(duration, Duration::minutes(30)); assert_eq!(prev_tag, Some("[tag1]".to_owned())); - assert_eq!(durations_by_tag["[tag1]"], vec![Duration::minutes(30)]); - - out.clear(); + assert_eq!( + durations_by_tag["[tag1]"], + vec![Duration::minutes(30)] + ); let line = "9.45-10 -\"-"; - line!(line); - assert_eq!(out, expected_output(Duration::minutes(15), line)); + let duration = line!(line); + assert_eq!(duration, Duration::minutes(15)); assert_eq!(prev_tag, Some("[tag1]".to_owned())); assert_eq!( durations_by_tag["[tag1]"], vec![Duration::minutes(30), Duration::minutes(15)] ); - out.clear(); - let line = "10-10.30 desc without tag 2"; - line!(line); - assert_eq!(out, expected_output(Duration::minutes(30), line)); - assert_eq!(prev_tag, Some(line[9..].to_owned())); - assert_eq!(durations_by_tag[&line[9..]], vec![Duration::minutes(30)]); - - out.clear(); + let duration = line!(line); + assert_eq!(duration, Duration::minutes(30)); + assert_eq!(prev_tag, Some("desc without tag 2".to_owned())); + assert_eq!( + durations_by_tag["desc without tag 2"], + vec![Duration::minutes(30)] + ); - line!("10.45-11 -\"-"); - assert_eq!(out, expected_output(Duration::minutes(15), "10.45-11 -\"-")); - assert_eq!(prev_tag, Some(line[9..].to_owned())); + let duration = line!("10.45-11 -\"-"); + assert_eq!(duration, Duration::minutes(15)); + assert_eq!(prev_tag, Some("desc without tag 2".to_owned())); assert_eq!( - durations_by_tag[&line[9..]], + durations_by_tag["desc without tag 2"], vec![Duration::minutes(30), Duration::minutes(15)] ); - out.clear(); - let line = "12-14.15 desc without tag 1"; - line!(line); - assert_eq!(out, expected_output(Duration::minutes(135), line)); - assert_eq!(prev_tag, Some(line[9..].to_owned())); + let duration = line!(line); + assert_eq!(duration, Duration::minutes(135)); + assert_eq!(prev_tag, Some("desc without tag 1".to_owned())); assert_eq!( - durations_by_tag[&line[9..]], + durations_by_tag["desc without tag 1"], vec![Duration::hours(1), Duration::minutes(135)] ); - out.clear(); - let line = "14.15-16 [tag1] desc, with some additinal info"; - line!(line); - assert_eq!(out, expected_output(Duration::minutes(105), line)); + let duration = line!(line); + assert_eq!(duration, Duration::minutes(105)); assert_eq!(prev_tag, Some("[tag1]".to_owned())); assert_eq!( durations_by_tag["[tag1]"], @@ -301,11 +239,9 @@ baz ] ); - out.clear(); - let line = "16-17 [tag1] NOTE: whitespace before tag"; - line!(line); - assert_eq!(out, expected_output(Duration::minutes(60), line)); + let duration = line!(line); + assert_eq!(duration, Duration::minutes(60)); assert_eq!(prev_tag, Some("[tag1]".to_owned())); assert_eq!( durations_by_tag["[tag1]"], @@ -319,8 +255,7 @@ baz } #[test] - fn test_write_duration_collect_total() { - let mut out = Vec::new(); + fn test_summarize_durations() { let durations_by_tag = HashMap::from([ ( "desc without tag 2".to_owned(), @@ -347,54 +282,32 @@ baz fn sum(durations: &Vec) -> Duration { durations.iter().fold(Duration::zero(), |acc, &x| acc + x) } - macro_rules! sum_by_tag { - ($tag:literal) => { - HumanDuration(sum(&durations_by_tag[$tag])).tag() - }; - } - - let total = write_durations_collect_total(&durations_by_tag, &mut out); - let expected_out = vec![ - format!("{} [tag1]\n", sum_by_tag!("[tag1]")), - format!("{} [tag2]\n", sum_by_tag!("[tag2]")), - format!("{} desc without tag 1\n", sum_by_tag!("desc without tag 1")), - format!("{} desc without tag 2\n", sum_by_tag!("desc without tag 2")), - ] - .join(""); - assert_eq!(out, expected_out.as_bytes()); - assert_eq!( - total, - sum(&durations_by_tag.into_values().flatten().collect()) - ); - } - - #[test] - fn test_write_total() { - let mut out = Vec::new(); - - let total = HumanDuration(Duration::hours(7)); - let diff = HumanDuration(Duration::minutes(-30)); - write_total(total.0, &mut out); - assert_eq!( - out, - format!("{} {}\n", total.total(), diff.diff()).as_bytes() - ); - out.clear(); + let (summary, total) = summarize_durations(&durations_by_tag); - let total = HumanDuration(Duration::hours(7) + Duration::minutes(30)); - let diff = HumanDuration(Duration::zero()); - write_total(total.0, &mut out); - assert_eq!(out, format!("{}\n", total.total()).as_bytes()); - - out.clear(); + let expected_summary = vec![ + ( + "[tag1]".to_owned(), + sum(&durations_by_tag["[tag1]"]), + ), + ( + "[tag2]".to_owned(), + sum(&durations_by_tag["[tag2]"]), + ), + ( + "desc without tag 1".to_owned(), + sum(&durations_by_tag["desc without tag 1"]), + ), + ( + "desc without tag 2".to_owned(), + sum(&durations_by_tag["desc without tag 2"]), + ), + ]; - let total = HumanDuration(Duration::hours(8)); - let diff = HumanDuration(Duration::minutes(30)); - write_total(total.0, &mut out); + assert_eq!(summary, expected_summary); assert_eq!( - out, - format!("{} {}\n", total.total(), diff.diff()).as_bytes() + total, + sum(&durations_by_tag.into_values().flatten().collect()) ); } } diff --git a/src/main.rs b/src/main.rs index 7b9014c..beaddad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,9 +6,9 @@ use clap::Parser; use colored::Colorize; use std::collections::HashMap; -use std::io::Write; +use std::io::{self, Write}; -use hrs::{find_and_collect_day, process_lines, write_durations_collect_total, write_total}; +use hrs::{find_and_collect_day, process_lines, summarize_durations, output::{HumanDuration, print_processed_lines, print_summary, print_total_and_diff}}; #[derive(Parser)] struct Cli { @@ -22,16 +22,19 @@ fn main() -> Result<()> { .with_context(|| format!("could not read file `{}`", args.path.display()))?; let lines_in_day = find_and_collect_day(&content, &args.date); + let (processed_lines, durations_by_tag) = process_lines(lines_in_day); + let (summary, duration_total) = summarize_durations(&durations_by_tag); - let mut durations_by_tag: HashMap> = HashMap::new(); let mut out = std::io::stdout(); - - writeln!(out, "----"); - process_lines(lines_in_day, &mut durations_by_tag, &out); - writeln!(out, "----"); - let duration_total = write_durations_collect_total(&durations_by_tag, &out); - writeln!(out, "----"); - write_total(duration_total, &out); + print_processed_lines(&mut out, processed_lines)?; + print_summary(&mut out, summary)?; + print_total_and_diff(&mut out, duration_total)?; Ok(()) } + + + + + + diff --git a/src/output.rs b/src/output.rs new file mode 100644 index 0000000..423f426 --- /dev/null +++ b/src/output.rs @@ -0,0 +1,203 @@ +use anyhow::Result; +use chrono::Duration; +use colored::{ColoredString, Colorize}; +use std::fmt; +use std::io::Write; + +pub struct HumanDuration(pub Duration); +impl HumanDuration { + pub fn plain(&self) -> String { + format!("{}", self) + } + pub fn line(&self) -> ColoredString { + self.plain().bold().green() + } + pub fn tag(&self) -> ColoredString { + self.plain().bold().blue() + } + pub fn total(&self) -> ColoredString { + self.plain().bold().white() + } + pub fn diff(&self) -> ColoredString { + if self.0 < Duration::zero() { + format!("-{}", self).red() + } else { + format!("+{}", self).green() + } + } +} +impl fmt::Display for HumanDuration { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{:02}:{:02}", + self.0.num_hours().abs(), + self.0.num_minutes().abs() % 60 + ) + } +} + +pub fn print_total_and_diff( + mut writer: impl std::io::Write, + total: chrono::Duration, +) -> Result<()> { + writeln!(writer, "----"); + let full_day = Duration::hours(7) + Duration::minutes(30); + let diff = total - full_day; + + if diff == Duration::zero() { + writeln!(writer, "{}", HumanDuration(total).total())?; + } else { + writeln!( + writer, + "{} {}", + HumanDuration(total).total(), + HumanDuration(diff).diff() + )?; + } + Ok(()) +} + +pub fn print_summary( + mut writer: impl std::io::Write, + summary: Vec<(String, chrono::Duration)>, +) -> Result<()> { + writeln!(writer, "----"); + for (tag, duration) in summary { + writeln!(writer, "{} {}", HumanDuration(duration).tag(), tag)?; + } + Ok(()) +} + +pub fn print_processed_lines( + mut writer: impl std::io::Write, + processed_lines: Vec<(chrono::Duration, &str)>, +) -> Result<()> { + writeln!(writer, "----"); + for (duration, line) in processed_lines { + writeln!(writer, "{} {}", HumanDuration(duration).line(), &line)?; + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_human_duration() -> Result<()> { + assert_eq!(HumanDuration(Duration::minutes(1)).plain(), "00:01"); + assert_eq!(HumanDuration(Duration::minutes(15)).plain(), "00:15"); + assert_eq!(HumanDuration(Duration::hours(1)).plain(), "01:00"); + assert_eq!(HumanDuration(Duration::minutes(135)).plain(), "02:15"); + assert_eq!(HumanDuration(Duration::hours(10)).plain(), "10:00"); + + let hour = (Duration::hours(1), "01:00"); + assert_eq!(HumanDuration(hour.0).line(), hour.1.bold().green()); + assert_eq!(HumanDuration(hour.0).tag(), hour.1.bold().blue()); + assert_eq!(HumanDuration(hour.0).total(), hour.1.bold().white()); + assert_eq!(HumanDuration(hour.0).diff(), format!("+{}", hour.1).green()); + assert_eq!(HumanDuration(-hour.0).diff(), format!("-{}", hour.1).red()); + Ok(()) + } + + #[test] + fn test_print_processed_lines() -> Result<()> { + let mut out = Vec::new(); + let processed_lines = vec![ + (Duration::hours(1), "8-9 [TAG] desc"), + (Duration::minutes(30), "9-9.30 tagless"), + ]; + + print_processed_lines(&mut out, processed_lines.clone())?; + + assert_eq!( + out, + format!( + "----\n{} {}\n{} {}\n", + HumanDuration(processed_lines[0].0).line(), + processed_lines[0].1, + HumanDuration(processed_lines[1].0).line(), + processed_lines[1].1 + ) + .into_bytes(), + ); + Ok(()) + } + + #[test] + fn test_print_summary() -> Result<()> { + let mut out = Vec::new(); + let summary = vec![ + ("TAG1".to_owned(), Duration::hours(2)), + ("TAG2".to_owned(), Duration::minutes(45)), + ]; + + print_summary(&mut out, summary.clone())?; + + assert_eq!( + out, + format!( + "----\n{} {}\n{} {}\n", + HumanDuration(summary[0].1).tag(), + summary[0].0, + HumanDuration(summary[1].1).tag(), + summary[1].0 + ) + .into_bytes() + ); + Ok(()) + } + + #[test] + fn test_print_total_and_diff() -> Result<()> { + let mut out = Vec::new(); + let total = Duration::hours(8); + + print_total_and_diff(&mut out, total)?; + + assert_eq!( + out, + format!( + "----\n{} {}\n", + HumanDuration(total).total(), + HumanDuration(Duration::minutes(30)).diff() + ) + .into_bytes() + ); + Ok(()) + } + + #[test] + fn test_print_total_and_diff_zero() -> Result<()> { + let mut out = Vec::new(); + let total = Duration::hours(7) + Duration::minutes(30); + + print_total_and_diff(&mut out, total)?; + + assert_eq!( + out, + format!("----\n{}\n", HumanDuration(total).total()).into_bytes() + ); + Ok(()) + } + + #[test] + fn test_print_total_and_diff_negative() -> Result<()> { + let mut out = Vec::new(); + let total = Duration::hours(6); + + print_total_and_diff(&mut out, total)?; + + assert_eq!( + out, + format!( + "----\n{} {}\n", + HumanDuration(total).total(), + HumanDuration(Duration::hours(-1) + Duration::minutes(-30)).diff() + ) + .into_bytes() + ); + Ok(()) + } +}