diff --git a/CHANGELOG.md b/CHANGELOG.md index b969feb..a494d90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,48 +2,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -## Unreleased - -### Added -- Marks to show which span a reminder belongs to - -### Changed -- Birthdays for current day are now highlighted -- Default value for `--range` argument - -### Fixed -- `--date` accepting incomplete expressions - -## 0.2.0 - 2022-03-18 - -### Added -- `LOG` command and `today log` CLI command -- `CAPTURE` command and `today new` CLI command -- `REMIND` statement -- `CANCEL` statement for tasks -- One-letter aliases for `show`, `log`, `done` and `cancel` CLI commands -- `MOVE` can now move entries to a different time -- `--date` now accepts expressions like `today-3d` -- In `--range` and `--date`, `t` can be used as abbreviation for `today` -- `*` markers in output for days with logs and entries with descriptions - -### Changed -- Output is now colored -- Better error messages -- Overhauled `today show` format - - It can now show log entries for days - - It now displays the source command (file and line) of the entry -- When saving... - - Unchanged files are no longer overwritten - - Imports are now sorted alphabetically - - Done and cancel dates are now simplified where possible -- Always prints import-based path, not absolute path - -### Fixed -- Alignment in output -- Respect `TZDIR` environment variable -- Negative weekday deltas - ## 0.1.0 - 2021-12-20 ### Added diff --git a/Cargo.lock b/Cargo.lock index 4836ec3..a4a4cfc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,12 +3,12 @@ version = 3 [[package]] -name = "android_system_properties" -version = "0.1.5" +name = "ansi_term" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ - "libc", + "winapi", ] [[package]] @@ -17,16 +17,16 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi", "libc", "winapi", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "bitflags" @@ -36,18 +36,30 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ + "block-padding", + "byte-tools", + "byteorder", "generic-array", ] [[package]] -name = "bumpalo" -version = "3.12.0" +name = "block-padding" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" @@ -55,12 +67,6 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - [[package]] name = "cfg-if" version = "1.0.0" @@ -69,86 +75,30 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ - "iana-time-zone", - "js-sys", + "libc", "num-integer", "num-traits", "time", - "wasm-bindgen", "winapi", ] [[package]] name = "clap" -version = "4.1.4" +version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" -dependencies = [ - "bitflags", - "clap_derive", - "clap_lex", - "is-terminal", - "once_cell", - "strsim", - "termcolor", -] - -[[package]] -name = "clap_derive" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "clipboard-win" -version = "4.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" -dependencies = [ - "error-code", - "str-buf", - "winapi", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - -[[package]] -name = "colored" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ + "ansi_term", "atty", - "lazy_static", - "winapi", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", ] [[package]] @@ -158,82 +108,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9eaa21df489b1e50af464db43c1fc69f60492060ac7baae3996fe8a5f476790" [[package]] -name = "core-foundation-sys" -version = "0.8.3" +name = "digest" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ "generic-array", - "typenum", -] - -[[package]] -name = "cxx" -version = "1.0.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90d59d9acd2a682b4e40605a242f6670eaa58c5957471cbf85e8aa6a0b97a5e8" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfa40bda659dd5c864e65f4c9a2b0aff19bea56b017b9b77c73d3766a453a38" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457ce6757c5c70dc6ecdbda6925b958aae7f959bda7d8fb9bde889e34a09dc03" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebf883b7aacd7b2aeb2a7b338648ee19f57c140d4ee8e52c68979c6b2f7f2263" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer", - "crypto-common", ] [[package]] @@ -245,21 +125,11 @@ dependencies = [ "dirs-sys", ] -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - [[package]] name = "dirs-sys" -version = "0.3.7" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" dependencies = [ "libc", "redox_users", @@ -267,115 +137,39 @@ dependencies = [ ] [[package]] -name = "dirs-sys-next" +name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "edit" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c562aa71f7bc691fde4c6bf5f93ae5a5298b617c2eb44c76c87832299a17fbb4" -dependencies = [ - "tempfile", - "which", -] - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - -[[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 = "error-code" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" -dependencies = [ - "libc", - "str-buf", -] - -[[package]] -name = "fastrand" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" -dependencies = [ - "instant", -] - -[[package]] -name = "fd-lock" -version = "3.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ef1a30ae415c3a691a4f41afddc2dbcd6d70baf338368d85ebc1e8ed92cedb9" -dependencies = [ - "cfg-if", - "rustix", - "windows-sys", -] +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "generic-array" -version = "0.14.6" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" dependencies = [ "typenum", - "version_check", ] [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "heck" -version = "0.4.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "hermit-abi" @@ -386,76 +180,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "iana-time-zone" -version = "0.1.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "winapi", -] - -[[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 = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "is-terminal" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys", -] - -[[package]] -name = "js-sys" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -464,76 +188,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" [[package]] -name = "link-cplusplus" -version = "1.0.8" +name = "maplit" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - -[[package]] -name = "nix" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" -dependencies = [ - "bitflags", - "cc", - "cfg-if", - "libc", - "memoffset", -] +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ "autocfg", "num-traits", @@ -541,40 +210,33 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", ] [[package]] -name = "once_cell" -version = "1.17.0" +name = "opaque-debug" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" - -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "pest" -version = "2.5.5" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" dependencies = [ - "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.5.5" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac3922aac69a40733080f53c1ce7f91dcf57e1a5f6c52f421fadec7fbdc4b69" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" dependencies = [ "pest", "pest_generator", @@ -582,9 +244,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.5" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06646e185566b5961b4058dd107e0a7f56e77c3f484549fb119867773c0f202" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" dependencies = [ "pest", "pest_meta", @@ -595,13 +257,13 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.5.5" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f60b2ba541577e2a0c307c8f39d1439108120eb7903adeb6497fa880c59616" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" dependencies = [ - "once_cell", + "maplit", "pest", - "sha2", + "sha-1", ] [[package]] @@ -630,197 +292,117 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1" dependencies = [ - "unicode-ident", -] - -[[package]] -name = "promptly" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9acbc6c5a5b029fe58342f58445acb00ccfe24624e538894bc2f04ce112980ba" -dependencies = [ - "rustyline", + "unicode-xid", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" dependencies = [ "proc-macro2", ] -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" dependencies = [ "bitflags", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ "getrandom", "redox_syscall", - "thiserror", ] [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "sha-1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" dependencies = [ - "winapi", -] - -[[package]] -name = "rustix" -version = "0.36.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "rustyline" -version = "9.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7826789c0e25614b03e5a54a0717a86f9ff6e6e5247f92b369472869320039" -dependencies = [ - "bitflags", - "cfg-if", - "clipboard-win", - "dirs-next", - "fd-lock", - "libc", - "log", - "memchr", - "nix", - "radix_trie", - "scopeguard", - "smallvec", - "unicode-segmentation", - "unicode-width", - "utf8parse", - "winapi", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scratch" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" - -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if", - "cpufeatures", + "block-buffer", "digest", + "fake-simd", + "opaque-debug", ] -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "str-buf" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" - [[package]] name = "strsim" -version = "0.10.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] [[package]] name = "syn" -version = "1.0.107" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "unicode-xid", ] [[package]] -name = "tempfile" -version = "3.3.0" +name = "textwrap" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ - "cfg-if", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", + "unicode-width", ] [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", @@ -829,44 +411,40 @@ dependencies = [ [[package]] name = "time" -version = "0.1.45" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", - "wasi 0.10.0+wasi-snapshot-preview1", + "wasi", "winapi", ] [[package]] name = "today" -version = "0.2.0" +version = "0.1.0" dependencies = [ "chrono", - "clap", - "codespan-reporting", - "colored", "computus", "directories", - "edit", "pest", "pest_derive", - "promptly", - "termcolor", + "structopt", "thiserror", "tzfile", ] [[package]] name = "typenum" -version = "1.16.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" [[package]] name = "tzfile" version = "0.1.3" -source = "git+https://github.com/Garmelon/tzfile.git?branch=tzdir#a2a4775fdb2a24f33f616ca6289d715f647d53c2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f59c22c42a2537e4c7ad21a4007273bbc5bebed7f36bc93730a5780e22a4592e" dependencies = [ "byteorder", "chrono", @@ -874,39 +452,39 @@ dependencies = [ [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" - -[[package]] -name = "unicode-ident" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] -name = "utf8parse" -version = "0.2.0" +name = "unicode-xid" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "wasi" @@ -914,77 +492,6 @@ version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" - -[[package]] -name = "which" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" -dependencies = [ - "either", - "libc", - "once_cell", -] - [[package]] name = "winapi" version = "0.3.9" @@ -1001,83 +508,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" diff --git a/Cargo.toml b/Cargo.toml index 0ae8a9f..edc3e45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,35 +1,14 @@ [package] name = "today" -version = "0.2.0" +version = "0.1.0" edition = "2021" [dependencies] -chrono = "0.4.23" -clap = { version = "4.1.4", features = ["derive"] } -codespan-reporting = "0.11.1" -colored = "2.0.0" +chrono = "0.4.19" computus = "1.0.0" directories = "4.0.1" -edit = "0.1.4" -pest = "2.5.5" -pest_derive = "2.5.5" -promptly = "0.3.1" -termcolor = "1.2.0" -thiserror = "1.0.38" -tzfile = { git = "https://github.com/Garmelon/tzfile.git", branch = "tzdir" } - -[lints] -rust.unsafe_code = { level = "forbid", priority = 1 } -rust.future_incompatible = "warn" -rust.rust_2018_idioms = "warn" -rust.noop_method_call = "warn" -rust.single_use_lifetimes = "warn" -rust.trivial_numeric_casts = "warn" -rust.unused = "warn" -rust.unused_crate_dependencies = "warn" -rust.unused_extern_crates = "warn" -rust.unused_import_braces = "warn" -rust.unused_lifetimes = "warn" -rust.unused_qualifications = "warn" -clippy.all = "warn" -clippy.use_self = "warn" +pest = "2.1.3" +pest_derive = "2.1.0" +structopt = "0.3.25" +thiserror = "1.0.30" +tzfile = "0.1.3" diff --git a/src/cli.rs b/src/cli.rs index 1189420..25d064d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -3,100 +3,56 @@ use std::str::FromStr; use std::{process, result}; use chrono::{NaiveDate, NaiveDateTime}; -use clap::Parser; -use codespan_reporting::files::SimpleFile; use directories::ProjectDirs; +use structopt::StructOpt; -use crate::eval::{self, DateRange, Entry, EntryMode}; -use crate::files::cli::{CliDate, CliIdent, CliRange}; -use crate::files::{self, Files, ParseError}; +use crate::eval::{DateRange, Entry, EntryMode, SourceInfo}; +use crate::files::arguments::Range; +use crate::files::{self, Files}; -use self::error::{Error, Result}; +use self::error::Result; use self::layout::line::LineLayout; -mod cancel; mod done; mod error; mod layout; -mod log; -mod new; mod print; mod show; -mod util; -#[derive(Debug, clap::Parser)] +#[derive(Debug, StructOpt)] pub struct Opt { /// File to load - #[clap(short, long)] + #[structopt(short, long, parse(from_os_str))] file: Option, /// Overwrite the current date - #[clap(short, long, default_value = "t")] - date: String, - /// Range of days to focus on - #[clap(short, long, default_value = "t-2d--t+2w")] + #[structopt(short, long)] + date: Option, + /// The range days to focus on + #[structopt(short, long, default_value = "today-2d--today+13d")] range: String, - #[clap(subcommand)] + #[structopt(subcommand)] command: Option, } -#[derive(Debug, clap::Subcommand)] +#[derive(Debug, StructOpt)] pub enum Command { + #[allow(rustdoc::broken_intra_doc_links)] /// Shows individual entries in detail - #[clap(alias = "s")] Show { - /// Entries and days to show - #[clap(required = true)] - identifiers: Vec, - }, - /// Create a new entry based on a template - #[clap(alias = "n")] - New { - #[clap(subcommand)] - template: Template, + /// Entries to show + #[structopt(required = true)] + entries: Vec, }, /// Marks one or more entries as done - #[clap(alias = "d")] Done { /// Entries to mark as done - #[clap(required = true)] + #[structopt(required = true)] entries: Vec, }, - /// Marks one or more entries as canceled - #[clap(alias = "c")] - Cancel { - /// Entries to mark as done - #[clap(required = true)] - entries: Vec, - }, - /// Edits or creates a log entry - #[clap(alias = "l")] - Log { - #[clap(default_value = "t")] - date: String, - }, - /// Reformats all loaded files + /// Reformat all loaded files Fmt, } -#[derive(Debug, clap::Subcommand)] -pub enum Template { - /// Adds a task - #[clap(alias = "t")] - Task { - /// If specified, the task is dated to this date - date: Option, - }, - /// Adds a note - #[clap(alias = "n")] - Note { - /// If specified, the note is dated to this date - date: Option, - }, - /// Adds an undated task marked as done today - #[clap(alias = "d")] - Done, -} - fn default_file() -> PathBuf { ProjectDirs::from("", "", "today") .expect("could not determine config dir") @@ -104,9 +60,18 @@ fn default_file() -> PathBuf { .join("main.today") } -fn load_files(opt: &Opt, files: &mut Files) -> result::Result<(), files::Error> { +fn load_files(opt: &Opt) -> result::Result { let file = opt.file.clone().unwrap_or_else(default_file); - files.load(&file) + Files::load(&file) +} + +fn find_now(opt: &Opt, files: &Files) -> NaiveDateTime { + let now = files.now().naive_local(); + if let Some(date) = opt.date { + date.and_time(now.time()) + } else { + now + } } fn find_entries(files: &Files, range: DateRange) -> Result> { @@ -122,37 +87,6 @@ fn find_layout( layout::layout(files, entries, range, now) } -fn parse_eval_arg(name: &str, text: &str, eval: E) -> Result -where - T: FromStr>, - E: FnOnce(T) -> result::Result>, -{ - let value = T::from_str(text).map_err(|error| Error::ArgumentParse { - file: SimpleFile::new(name.to_string(), text.to_string()), - error, - })?; - eval(value).map_err(|error| Error::ArgumentEval { - file: SimpleFile::new(name.to_string(), text.to_string()), - error, - }) -} - -fn parse_eval_date(name: &str, text: &str, today: NaiveDate) -> Result { - parse_eval_arg(name, text, |date: CliDate| date.eval((), today)) -} - -fn parse_show_idents(identifiers: &[String], today: NaiveDate) -> Result> { - let mut idents = vec![]; - for ident in identifiers { - let ident = parse_eval_arg("identifier", ident, |ident: CliIdent| match ident { - CliIdent::Number(n) => Ok(show::Ident::Number(n)), - CliIdent::Date(d) => Ok(show::Ident::Date(d.eval((), today)?)), - })?; - idents.push(ident); - } - Ok(idents) -} - fn run_command(opt: &Opt, files: &mut Files, range: DateRange, now: NaiveDateTime) -> Result<()> { match &opt.command { None => { @@ -160,25 +94,11 @@ fn run_command(opt: &Opt, files: &mut Files, range: DateRange, now: NaiveDateTim let layout = find_layout(files, &entries, range, now); print::print(&layout); } - Some(Command::Show { identifiers }) => { + Some(Command::Show { entries: ns }) => { let entries = find_entries(files, range)?; let layout = find_layout(files, &entries, range, now); - let idents = parse_show_idents(identifiers, now.date())?; - show::show(files, &entries, &layout, &idents); + show::show(files, &entries, &layout, ns)?; } - Some(Command::New { template }) => match template { - Template::Task { date: Some(date) } => { - let date = parse_eval_date("date", date, now.date())?; - new::task(files, Some(date))? - } - Template::Task { date: None } => new::task(files, None)?, - Template::Note { date: Some(date) } => { - let date = parse_eval_date("date", date, now.date())?; - new::note(files, Some(date))? - } - Template::Note { date: None } => new::note(files, None)?, - Template::Done => new::done(files, now.date())?, - }, Some(Command::Done { entries: ns }) => { let entries = find_entries(files, range)?; let layout = find_layout(files, &entries, range, now); @@ -187,55 +107,50 @@ fn run_command(opt: &Opt, files: &mut Files, range: DateRange, now: NaiveDateTim let layout = find_layout(files, &entries, range, now); print::print(&layout); } - Some(Command::Cancel { entries: ns }) => { - let entries = find_entries(files, range)?; - let layout = find_layout(files, &entries, range, now); - cancel::cancel(files, &entries, &layout, ns, now)?; - let entries = find_entries(files, range)?; - let layout = find_layout(files, &entries, range, now); - print::print(&layout); - } - Some(Command::Log { date }) => { - let date = parse_eval_arg("date", date, |date: CliDate| date.eval((), now.date()))?; - log::log(files, date)? - } Some(Command::Fmt) => files.mark_all_dirty(), } Ok(()) } -fn run_with_files(opt: Opt, files: &mut Files) -> Result<()> { - let now = files.now().naive_local(); - let today = parse_eval_arg("--date", &opt.date, |date: CliDate| { - date.eval((), now.date()) - })?; - let now = today.and_time(now.time()); - - let range = parse_eval_arg("--range", &opt.range, |range: CliRange| { - range.eval((), now.date()) - })?; - - run_command(&opt, files, range, now)?; - - Ok(()) -} - pub fn run() { - let opt = Opt::parse(); + let opt = Opt::from_args(); - let mut files = Files::new(); - if let Err(e) = load_files(&opt, &mut files) { - crate::error::eprint_error(&files, &e); - process::exit(1); - } + let mut files = match load_files(&opt) { + Ok(result) => result, + Err(e) => { + e.print(); + process::exit(1); + } + }; - if let Err(e) = run_with_files(opt, &mut files) { - crate::error::eprint_error(&files, &e); + let now = find_now(&opt, &files); + + // Kinda ugly, but it can stay for now (until it grows at least). + let range = match Range::from_str(&opt.range) { + Ok(range) => match range.eval(0, now.date()) { + Ok(range) => range, + Err(e) => { + eprintln!("Failed to evaluate --range:"); + e.print(&[SourceInfo { + name: Some("--range".to_string()), + content: &opt.range, + }]); + process::exit(1) + } + }, + Err(e) => { + eprintln!("Failed to parse --range:\n{}", e.with_path("--range")); + process::exit(1) + } + }; + + if let Err(e) = run_command(&opt, &mut files, range, now) { + e.print(&files.sources()); process::exit(1); } if let Err(e) = files.save() { - crate::error::eprint_error(&files, &e); + e.print(); process::exit(1); } } diff --git a/src/cli/cancel.rs b/src/cli/cancel.rs deleted file mode 100644 index 62cc8ea..0000000 --- a/src/cli/cancel.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::vec; - -use chrono::NaiveDateTime; - -use crate::eval::Entry; -use crate::files::commands::{Done, DoneKind}; -use crate::files::Files; - -use super::error::{Error, Result}; -use super::layout::line::LineLayout; - -pub fn cancel( - files: &mut Files, - entries: &[Entry], - layout: &LineLayout, - numbers: &[usize], - now: NaiveDateTime, -) -> Result<()> { - let mut not_tasks = vec![]; - for &number in numbers { - let entry = &entries[layout.look_up_number(number)?]; - let done = Done { - kind: DoneKind::Canceled, - date: entry.dates.map(|dates| dates.into()), - done_at: now.date(), - }; - if !files.add_done(entry.source, done) { - not_tasks.push(number); - } - } - - if not_tasks.is_empty() { - Ok(()) - } else { - Err(Error::NotATask(not_tasks)) - } -} diff --git a/src/cli/done.rs b/src/cli/done.rs index 1d3d809..379d6ce 100644 --- a/src/cli/done.rs +++ b/src/cli/done.rs @@ -3,7 +3,7 @@ use std::vec; use chrono::NaiveDateTime; use crate::eval::Entry; -use crate::files::commands::{Done, DoneKind}; +use crate::files::commands::Done; use crate::files::Files; use super::error::{Error, Result}; @@ -20,7 +20,6 @@ pub fn done( for &number in numbers { let entry = &entries[layout.look_up_number(number)?]; let done = Done { - kind: DoneKind::Done, date: entry.dates.map(|dates| dates.into()), done_at: now.date(), }; diff --git a/src/cli/error.rs b/src/cli/error.rs index 05b239a..3bf054e 100644 --- a/src/cli/error.rs +++ b/src/cli/error.rs @@ -1,54 +1,23 @@ -use std::{io, result}; +use std::result; -use chrono::NaiveDate; -use codespan_reporting::files::{Files, SimpleFile}; -use codespan_reporting::term::Config; - -use crate::error::Eprint; -use crate::files::FileSource; -use crate::{eval, files}; +use crate::eval::{self, SourceInfo}; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("{0}")] - Eval(#[from] eval::Error), - #[error("{error}")] - ArgumentParse { - file: SimpleFile, - error: files::ParseError<()>, - }, - #[error("{error}")] - ArgumentEval { - file: SimpleFile, - error: eval::Error<()>, - }, + Eval(#[from] eval::Error), #[error("No entry with number {0}")] NoSuchEntry(usize), - #[error("No log for {0}")] - NoSuchLog(NaiveDate), #[error("Not a task")] NotATask(Vec), - #[error("No capture file found")] - NoCaptureFile, - #[error("Error editing: {0}")] - EditingIo(io::Error), } -pub type Result = result::Result; - -impl<'a, F> Eprint<'a, F> for Error -where - F: Files<'a, FileId = FileSource>, -{ - #[allow(single_use_lifetimes)] - fn eprint<'f: 'a>(&self, files: &'f F, config: &Config) { +impl Error { + pub fn print<'a>(&self, sources: &[SourceInfo<'a>]) { match self { - Self::Eval(e) => e.eprint(files, config), - Self::ArgumentParse { file, error } => error.eprint(file, config), - Self::ArgumentEval { file, error } => error.eprint(file, config), - Self::NoSuchEntry(n) => eprintln!("No entry with number {n}"), - Self::NoSuchLog(date) => eprintln!("No log for {date}"), - Self::NotATask(ns) => { + Error::Eval(e) => e.print(sources), + Error::NoSuchEntry(n) => eprintln!("No entry with number {}", n), + Error::NotATask(ns) => { if ns.is_empty() { eprintln!("Not a task."); } else if ns.len() == 1 { @@ -58,11 +27,8 @@ where eprintln!("{} are not tasks.", ns.join(", ")); } } - Self::NoCaptureFile => eprintln!("No capture file found"), - Self::EditingIo(error) => { - eprintln!("Error while editing:"); - eprintln!(" {error}"); - } } } } + +pub type Result = result::Result; diff --git a/src/cli/layout.rs b/src/cli/layout.rs index b063c44..76575eb 100644 --- a/src/cli/layout.rs +++ b/src/cli/layout.rs @@ -16,7 +16,7 @@ pub fn layout( now: NaiveDateTime, ) -> LineLayout { let mut day_layout = DayLayout::new(range, now); - day_layout.layout(entries); + day_layout.layout(files, entries); let mut line_layout = LineLayout::new(); line_layout.render(files, entries, &day_layout); diff --git a/src/cli/layout/day.rs b/src/cli/layout/day.rs index 044269c..0cb4c0b 100644 --- a/src/cli/layout/day.rs +++ b/src/cli/layout/day.rs @@ -8,7 +8,9 @@ use std::collections::HashMap; use chrono::{NaiveDate, NaiveDateTime}; use crate::eval::{DateRange, Dates, Entry, EntryKind}; +use crate::files::commands::Command; use crate::files::primitives::Time; +use crate::files::Files; #[derive(Debug)] pub enum DayEntry { @@ -46,13 +48,18 @@ impl DayLayout { } } - pub fn layout(&mut self, entries: &[Entry]) { + pub fn layout(&mut self, files: &Files, entries: &[Entry]) { self.insert(self.today, DayEntry::Now(self.time)); - let mut entries = entries.iter().enumerate().collect::>(); - Self::sort_entries(&mut entries); + let mut commands = entries + .iter() + .enumerate() + .map(|(i, e)| (i, e, files.command(e.source))) + .collect::>(); - for (index, entry) in entries { + Self::sort_entries(&mut commands); + + for (index, entry, _) in commands { self.layout_entry(index, entry); } @@ -66,9 +73,7 @@ impl DayLayout { fn layout_entry(&mut self, index: usize, entry: &Entry) { match entry.kind { EntryKind::Task => self.layout_task(index, entry), - EntryKind::TaskDone(at) | EntryKind::TaskCanceled(at) => { - self.layout_task_done(index, entry, at) - } + EntryKind::TaskDone(at) => self.layout_task_done(index, entry, at), EntryKind::Note | EntryKind::Birthday(_) => self.layout_note(index, entry), } } @@ -76,17 +81,10 @@ impl DayLayout { fn layout_task(&mut self, index: usize, entry: &Entry) { if let Some(dates) = entry.dates { let (start, end) = dates.sorted().dates(); - if self.today < self.range.from() || self.range.until() < self.today { - // If `self.today` is not in range, reminders won't be displayed - // (since they're always displayed on `self.today`) so there's - // no need to calculate them. - } else if self.today < start { - if let Some(remind) = entry.remind { - if remind <= self.today { - let days = (start - self.today).num_days(); - self.insert(self.today, DayEntry::ReminderUntil(index, days)); - } - } + if self.today < start && (start - self.today).num_days() < 7 { + // TODO Make this adjustable, maybe even per-command + let days = (start - self.today).num_days(); + self.insert(self.today, DayEntry::ReminderUntil(index, days)); } else if start < self.today && self.today < end { let days = (end - self.today).num_days(); self.insert(self.today, DayEntry::ReminderWhile(index, days)); @@ -117,18 +115,7 @@ impl DayLayout { fn layout_note(&mut self, index: usize, entry: &Entry) { if let Some(dates) = entry.dates { let (start, end) = dates.sorted().dates(); - if self.today < self.range.from() || self.range.until() < self.today { - // if `self.today` is not in range, reminders won't be displayed - // (since they're always displayed on `self.today`) so there's - // no need to calculate them. - } else if self.today < start { - if let Some(remind) = entry.remind { - if remind <= self.today { - let days = (start - self.today).num_days(); - self.insert(self.today, DayEntry::ReminderUntil(index, days)); - } - } - } else if start < self.range.from() && self.range.until() < end { + if start < self.range.from() && self.range.until() < end { // This note applies to the current day, but it won't appear if // we just layout it as a dated entry, so instead we add it as a // reminder. Since we are usually more interested in when @@ -136,8 +123,9 @@ impl DayLayout { // the end. let days = (end - self.today).num_days(); self.insert(self.today, DayEntry::ReminderWhile(index, days)); + } else { + self.layout_dated_entry(index, dates); } - self.layout_dated_entry(index, dates); } else { self.insert(self.today, DayEntry::Undated(index)); } @@ -189,7 +177,7 @@ impl DayLayout { } } - fn sort_entries(entries: &mut [(usize, &Entry)]) { + fn sort_entries(entries: &mut Vec<(usize, &Entry, &Command)>) { // Entries should be sorted by these factors, in descending order of // significance: // 1. Their start date, if any @@ -198,28 +186,28 @@ impl DayLayout { // 4. Their title // 4. - entries.sort_by_key(|(_, e)| &e.title); + entries.sort_by_key(|(_, _, c)| c.title()); // 3. - entries.sort_by_key(|(_, e)| match e.kind { + entries.sort_by_key(|(_, e, _)| match e.kind { EntryKind::Task => 0, - EntryKind::TaskDone(_) | EntryKind::TaskCanceled(_) => 1, + EntryKind::TaskDone(_) => 1, EntryKind::Birthday(_) => 2, EntryKind::Note => 3, }); // 2. - entries.sort_by(|(_, e1), (_, e2)| { + entries.sort_by(|(_, e1, _), (_, e2, _)| { let d1 = e1.dates.map(|d| d.sorted().other_with_time()); let d2 = e2.dates.map(|d| d.sorted().other_with_time()); d2.cmp(&d1) // Inverse comparison }); // 1. - entries.sort_by_key(|(_, e)| e.dates.map(|d| d.sorted().root_with_time())); + entries.sort_by_key(|(_, e, _)| e.dates.map(|d| d.sorted().root_with_time())); } - fn sort_day(day: &mut [DayEntry]) { + fn sort_day(day: &mut Vec) { // In a day, entries should be sorted into these categories: // 1. Untimed entries that end at the current day // 2. Timed entries, based on diff --git a/src/cli/layout/line.rs b/src/cli/layout/line.rs index 5f5d686..a0e74c6 100644 --- a/src/cli/layout/line.rs +++ b/src/cli/layout/line.rs @@ -11,44 +11,14 @@ use crate::eval::{Entry, EntryKind}; use crate::files::primitives::Time; use crate::files::Files; -use super::super::error::Error; +use super::super::error::{Error, Result}; use super::day::{DayEntry, DayLayout}; -#[derive(Debug, Clone, Copy)] -pub enum SpanStyle { - Solid, - Dashed, - Dotted, -} - -impl SpanStyle { - fn from_indentation(index: usize) -> Self { - match index % 3 { - 0 => Self::Solid, - 1 => Self::Dashed, - 2 => Self::Dotted, - _ => unreachable!(), - } - } -} - #[derive(Debug, Clone, Copy)] pub enum SpanSegment { - Start(SpanStyle), - Middle(SpanStyle), - Mark(SpanStyle), - End(SpanStyle), -} - -impl SpanSegment { - fn style(&self) -> SpanStyle { - match self { - Self::Start(s) => *s, - Self::Middle(s) => *s, - Self::Mark(s) => *s, - Self::End(s) => *s, - } - } + Start, + Middle, + End, } #[derive(Debug, Clone, Copy)] @@ -58,21 +28,10 @@ pub enum Times { FromTo(Time, Time), } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum LineKind { - Task, - Done, - Canceled, - Note, - Birthday, -} - pub enum LineEntry { Day { spans: Vec>, date: NaiveDate, - today: bool, - has_log: bool, }, Now { spans: Vec>, @@ -81,12 +40,8 @@ pub enum LineEntry { Entry { number: Option, spans: Vec>, - today: bool, time: Times, - kind: LineKind, text: String, - has_desc: bool, - extra: Option, }, } @@ -124,18 +79,12 @@ impl LineLayout { self.step_spans(); for day in layout.range.days() { - let today = day == layout.today; let spans = self.spans_for_line(); - self.line(LineEntry::Day { - spans, - date: day, - today, - has_log: files.log(day).is_some(), - }); + self.line(LineEntry::Day { spans, date: day }); let layout_entries = layout.days.get(&day).expect("got nonexisting day"); for layout_entry in layout_entries { - self.render_layout_entry(entries, layout_entry, today); + self.render_layout_entry(files, entries, layout_entry); } } } @@ -152,7 +101,7 @@ impl LineLayout { &self.lines } - pub fn look_up_number(&self, number: usize) -> Result { + pub fn look_up_number(&self, number: usize) -> Result { self.numbers .iter() .filter(|(_, n)| **n == number) @@ -161,11 +110,12 @@ impl LineLayout { .ok_or(Error::NoSuchEntry(number)) } - fn render_layout_entry(&mut self, entries: &[Entry], l_entry: &DayEntry, today: bool) { + fn render_layout_entry(&mut self, files: &Files, entries: &[Entry], l_entry: &DayEntry) { match l_entry { DayEntry::End(i) => { self.stop_span(*i); - self.line_entry(entries, *i, today, Times::Untimed, None); + let text = Self::format_entry(files, entries, *i); + self.line_entry(Some(*i), Times::Untimed, text); } DayEntry::Now(t) => self.line(LineEntry::Now { spans: self.spans_for_line(), @@ -173,95 +123,89 @@ impl LineLayout { }), DayEntry::TimedEnd(i, t) => { self.stop_span(*i); - self.line_entry(entries, *i, today, Times::At(*t), None); + let text = Self::format_entry(files, entries, *i); + self.line_entry(Some(*i), Times::At(*t), text); } DayEntry::TimedAt(i, t, t2) => { - let time = t2.map(|t2| Times::FromTo(*t, t2)).unwrap_or(Times::At(*t)); - self.line_entry(entries, *i, today, time, None); + let time = t2 + .map(|t2| Times::FromTo(*t, t2)) + .unwrap_or_else(|| Times::At(*t)); + let text = Self::format_entry(files, entries, *i); + self.line_entry(Some(*i), time, text); } DayEntry::TimedStart(i, t) => { self.start_span(*i); - self.line_entry(entries, *i, today, Times::At(*t), None); + let text = Self::format_entry(files, entries, *i); + self.line_entry(Some(*i), Times::At(*t), text); } DayEntry::ReminderSince(i, d) => { - let extra = if *d == 1 { - "yesterday".to_string() + let text = Self::format_entry(files, entries, *i); + let text = if *d == 1 { + format!("{} (yesterday)", text) } else { - format!("{d} days ago") + format!("{} ({} days ago)", text, d) }; - self.line_entry(entries, *i, today, Times::Untimed, Some(extra)); + self.line_entry(Some(*i), Times::Untimed, text); } DayEntry::At(i) => { - self.line_entry(entries, *i, today, Times::Untimed, None); + let text = Self::format_entry(files, entries, *i); + self.line_entry(Some(*i), Times::Untimed, text); } DayEntry::ReminderWhile(i, d) => { + let text = Self::format_entry(files, entries, *i); let plural = if *d == 1 { "" } else { "s" }; - let extra = format!("{d} day{plural} left"); - self.mark_span(*i); - self.line_entry(entries, *i, today, Times::Untimed, Some(extra)); + let text = format!("{} ({} day{} left)", text, i, plural); + self.line_entry(Some(*i), Times::Untimed, text); } DayEntry::Undated(i) => { - self.line_entry(entries, *i, today, Times::Untimed, None); + let text = Self::format_entry(files, entries, *i); + self.line_entry(Some(*i), Times::Untimed, text); } DayEntry::Start(i) => { self.start_span(*i); - self.line_entry(entries, *i, today, Times::Untimed, None); + let text = Self::format_entry(files, entries, *i); + self.line_entry(Some(*i), Times::Untimed, text); } DayEntry::ReminderUntil(i, d) => { - let extra = if *d == 1 { - "tomorrow".to_string() + let text = Self::format_entry(files, entries, *i); + let text = if *d == 1 { + format!("{} (tomorrow)", text) } else { - format!("in {d} days") + format!("{} (in {} days)", text, d) }; - self.line_entry(entries, *i, today, Times::Untimed, Some(extra)); + self.line_entry(Some(*i), Times::Untimed, text); } } } - pub fn entry_kind(entry: &Entry) -> LineKind { + fn format_entry(files: &Files, entries: &[Entry], index: usize) -> String { + let entry = entries[index]; + let command = files.command(entry.source); match entry.kind { - EntryKind::Task => LineKind::Task, - EntryKind::TaskDone(_) => LineKind::Done, - EntryKind::TaskCanceled(_) => LineKind::Canceled, - EntryKind::Note => LineKind::Note, - EntryKind::Birthday(_) => LineKind::Birthday, - } - } - - fn entry_title(entry: &Entry) -> String { - match entry.kind { - EntryKind::Birthday(Some(age)) => format!("{} ({})", entry.title, age), - _ => entry.title.clone(), + EntryKind::Task => format!("T {}", command.title()), + EntryKind::TaskDone(_) => format!("D {}", command.title()), + EntryKind::Note => format!("N {}", command.title()), + EntryKind::Birthday(Some(age)) => format!("B {} ({})", command.title(), age), + EntryKind::Birthday(None) => format!("B {}", command.title()), } } fn start_span(&mut self, index: usize) { - for (i, span) in self.spans.iter_mut().enumerate() { + for span in self.spans.iter_mut() { if span.is_none() { - let style = SpanStyle::from_indentation(i); - *span = Some((index, SpanSegment::Start(style))); + *span = Some((index, SpanSegment::Start)); return; } } // Not enough space, we need another column - let style = SpanStyle::from_indentation(self.spans.len()); - self.spans.push(Some((index, SpanSegment::Start(style)))); - } - - fn mark_span(&mut self, index: usize) { - for span in self.spans.iter_mut() { - match span { - Some((i, s)) if *i == index => *s = SpanSegment::Mark(s.style()), - _ => {} - } - } + self.spans.push(Some((index, SpanSegment::Start))); } fn stop_span(&mut self, index: usize) { for span in self.spans.iter_mut() { match span { - Some((i, s)) if *i == index => *s = SpanSegment::End(s.style()), + Some((i, s)) if *i == index => *s = SpanSegment::End, _ => {} } } @@ -270,10 +214,8 @@ impl LineLayout { fn step_spans(&mut self) { for span in self.spans.iter_mut() { match span { - Some((_, s @ (SpanSegment::Start(_) | SpanSegment::Mark(_)))) => { - *s = SpanSegment::Middle(s.style()) - } - Some((_, SpanSegment::End(_))) => *span = None, + Some((_, s @ SpanSegment::Start)) => *s = SpanSegment::Middle, + Some((_, SpanSegment::End)) => *span = None, _ => {} } } @@ -291,34 +233,24 @@ impl LineLayout { self.step_spans(); } - fn line_entry( - &mut self, - entries: &[Entry], - index: usize, - today: bool, - time: Times, - extra: Option, - ) { - let entry = &entries[index]; - - let number = match self.numbers.get(&index) { - Some(number) => *number, - None => { - self.last_number += 1; - self.numbers.insert(index, self.last_number); - self.last_number - } + fn line_entry(&mut self, index: Option, time: Times, text: String) { + let number = match index { + Some(index) => Some(match self.numbers.get(&index) { + Some(number) => *number, + None => { + self.last_number += 1; + self.numbers.insert(index, self.last_number); + self.last_number + } + }), + None => None, }; self.line(LineEntry::Entry { - number: Some(number), + number, spans: self.spans_for_line(), - today, time, - kind: Self::entry_kind(entry), - text: Self::entry_title(entry), - has_desc: entry.has_description, - extra, + text, }); } } diff --git a/src/cli/log.rs b/src/cli/log.rs deleted file mode 100644 index 3703cef..0000000 --- a/src/cli/log.rs +++ /dev/null @@ -1,24 +0,0 @@ -use chrono::NaiveDate; - -use crate::files::Files; - -use super::error::Error; -use super::util; - -pub fn log(files: &mut Files, date: NaiveDate) -> Result<(), Error> { - let desc = files - .log(date) - .map(|log| log.value.desc.join("\n")) - .unwrap_or_default(); - - let edited = util::edit_with_suffix(&desc, ".md")?; - - let edited = edited - .lines() - .map(|line| line.to_string()) - .collect::>(); - - files.set_log(date, edited); - - Ok(()) -} diff --git a/src/cli/new.rs b/src/cli/new.rs deleted file mode 100644 index ecd040f..0000000 --- a/src/cli/new.rs +++ /dev/null @@ -1,114 +0,0 @@ -use std::result; -use std::str::FromStr; - -use chrono::NaiveDate; -use codespan_reporting::files::SimpleFile; - -use crate::files::cli::CliCommand; -use crate::files::commands::{Command, DateSpec, Done, DoneKind, Note, Spec, Statement, Task}; -use crate::files::{Files, ParseError}; - -use super::error::{Error, Result}; -use super::util; - -fn edit(name: &str, mut text: String, validate: F) -> Result> -where - R: FromStr>, - F: Fn(&R) -> result::Result<(), &str>, -{ - Ok(loop { - text = util::edit(&text)?; - match text.parse() { - Ok(command) => match validate(&command) { - Ok(()) => break Some(command), - Err(msg) => eprintln!("{msg}"), - }, - Err(e) => crate::error::eprint_error(&SimpleFile::new(name, &text), &e), - } - if !matches!( - promptly::prompt_default("Continue editing?", true), - Ok(true) - ) { - println!("Aborting"); - break None; - } - }) -} - -fn is_task_or_note(command: &CliCommand) -> result::Result<(), &str> { - match command.0 { - Command::Task(_) | Command::Note(_) => Ok(()), - _ => Err("Only TASK and NOTE are allowed"), - } -} - -fn new_command(files: &mut Files, command: Command) -> Result<()> { - let capture = files.capture().ok_or(Error::NoCaptureFile)?; - - let command = edit("new command", format!("{command}"), is_task_or_note)?; - if let Some(command) = command { - files.insert(capture, command.0) - } - - Ok(()) -} - -pub fn task(files: &mut Files, date: Option) -> Result<()> { - let statements = match date { - Some(date) => vec![Statement::Date(Spec::Date(DateSpec { - start: date, - start_delta: None, - start_time: None, - end: None, - end_delta: None, - end_time: None, - repeat: None, - }))], - None => vec![], - }; - let command = Command::Task(Task { - title: String::new(), - statements, - done: vec![], - desc: vec![], - }); - - new_command(files, command) -} - -pub fn note(files: &mut Files, date: Option) -> Result<()> { - let statements = match date { - Some(date) => vec![Statement::Date(Spec::Date(DateSpec { - start: date, - start_delta: None, - start_time: None, - end: None, - end_delta: None, - end_time: None, - repeat: None, - }))], - None => vec![], - }; - let command = Command::Note(Note { - title: String::new(), - statements, - desc: vec![], - }); - - new_command(files, command) -} - -pub fn done(files: &mut Files, date: NaiveDate) -> Result<()> { - let command = Command::Task(Task { - title: String::new(), - statements: vec![], - done: vec![Done { - kind: DoneKind::Done, - date: None, - done_at: date, - }], - desc: vec![], - }); - - new_command(files, command) -} diff --git a/src/cli/print.rs b/src/cli/print.rs index cabba63..cd04776 100644 --- a/src/cli/print.rs +++ b/src/cli/print.rs @@ -1,12 +1,10 @@ use std::cmp; use chrono::{Datelike, NaiveDate}; -use colored::{ColoredString, Colorize}; use crate::files::primitives::{Time, Weekday}; -use super::layout::line::{LineEntry, LineKind, LineLayout, SpanSegment, SpanStyle, Times}; -use super::util; +use super::layout::line::{LineEntry, LineLayout, SpanSegment, Times}; struct ShowLines { num_width: usize, @@ -25,156 +23,86 @@ impl ShowLines { fn display_line(&mut self, line: &LineEntry) { match line { - LineEntry::Day { - spans, - date, - today, - has_log, - } => self.display_line_date(spans, *date, *today, *has_log), + LineEntry::Day { spans, date } => self.display_line_date(spans, *date), LineEntry::Now { spans, time } => self.display_line_now(spans, *time), LineEntry::Entry { number, spans, - today, time, - kind, text, - has_desc, - extra, - } => self - .display_line_entry(*number, spans, *today, *time, *kind, text, *has_desc, extra), + } => self.display_line_entry(*number, spans, *time, text), } } - fn display_line_date( - &mut self, - spans: &[Option], - date: NaiveDate, - today: bool, - has_log: bool, - ) { + fn display_line_date(&mut self, spans: &[Option], date: NaiveDate) { let weekday: Weekday = date.weekday().into(); let weekday = weekday.full_name(); - - let styled = |s: &str| { - if today { - s.bright_cyan().bold() - } else { - s.cyan() - } - }; - - // '=' symbols before the spans start - let p1 = styled(&format!("{:=], time: Time) { self.push(&format!( - "{:>nw$} {} {}\n", - "now".bright_cyan().bold(), - self.display_spans(spans, " ".into()), - Self::display_time(Times::At(time)), + "{:, spans: &[Option], - today: bool, time: Times, - kind: LineKind, text: &str, - has_desc: bool, - extra: &Option, ) { let num = match number { - Some(n) => format!("{n}"), + Some(n) => format!("{}", n), None => "".to_string(), }; - let text = if kind == LineKind::Birthday && today { - util::display_current_birthday_text(text) - } else { - text.into() + let time = match time { + Times::Untimed => "".to_string(), + Times::At(t) => format!("{} ", t), + Times::FromTo(t1, t2) => format!("{}--{} ", t1, t2), }; self.push(&format!( - "{:>nw$} {} {}{} {}{}{}\n", - num.bright_black(), - self.display_spans(spans, " ".into()), - util::display_kind(kind), - Self::display_time(time), + "{:>nw$} {:sw$} {}{}\n", + num, + Self::display_spans(spans, ' '), + time, text, - Self::display_marker(has_desc, ""), - Self::display_extra(extra), nw = self.num_width, + sw = self.span_width )) } - fn display_spans(&self, spans: &[Option], empty: ColoredString) -> String { + fn display_spans(spans: &[Option], empty: char) -> String { let mut result = String::new(); - for i in 0..self.span_width { - if let Some(Some(segment)) = spans.get(i) { - let colored_str = match segment { - SpanSegment::Start(_) => "┌".bright_black(), - SpanSegment::Middle(SpanStyle::Solid) => "│".bright_black(), - SpanSegment::Middle(SpanStyle::Dashed) => "╎".bright_black(), - SpanSegment::Middle(SpanStyle::Dotted) => "┊".bright_black(), - SpanSegment::Mark(_) => "┝".bright_black(), - SpanSegment::End(_) => "└".bright_black(), - }; - result.push_str(&format!("{colored_str}")); - } else { - result.push_str(&format!("{empty}")); - } + for segment in spans { + result.push(match segment { + Some(SpanSegment::Start) => '┌', + Some(SpanSegment::Middle) => '│', + Some(SpanSegment::End) => '└', + None => empty, + }); } result } - fn display_time(time: Times) -> ColoredString { - match time { - Times::Untimed => "".into(), - Times::At(t) => format!(" {t}").bright_black(), - Times::FromTo(t1, t2) => format!(" {t1}--{t2}").bright_black(), - } - } - - fn display_marker(marker: bool, otherwise: &str) -> ColoredString { - if marker { - "*".bright_yellow() - } else { - otherwise.into() - } - } - - fn display_extra(extra: &Option) -> ColoredString { - match extra { - None => "".into(), - Some(extra) => format!(" ({extra})").bright_black(), - } - } - fn push(&mut self, line: &str) { self.result.push_str(line); } diff --git a/src/cli/show.rs b/src/cli/show.rs index 12bcc31..1b20413 100644 --- a/src/cli/show.rs +++ b/src/cli/show.rs @@ -1,108 +1,61 @@ -use chrono::NaiveDate; -use codespan_reporting::files::Files as CsFiles; -use colored::Colorize; - use crate::eval::{Entry, EntryKind}; -use crate::files::commands::{Command, Log}; -use crate::files::primitives::Spanned; -use crate::files::{Files, Sourced}; +use crate::files::Files; -use super::error::Error; +use super::error::Result; use super::layout::line::LineLayout; -use super::util; - -fn fmt_where(files: &Files, command: &Sourced<'_, Spanned>) -> String { - let name = files.name(command.source.file()).expect("file exists"); - let line = files - .line_index(command.source.file(), command.value.span.start) - .expect("file exists and line is valid"); - let line = line + 1; // 1-indexed for human consumption - format!("Line {line} in {name}") -} - -fn print_desc(command: &Sourced<'_, Spanned>) { - let desc: &[String] = match &command.value.value { - Command::Task(task) => &task.desc, - Command::Note(note) => ¬e.desc, - Command::Log(log) => &log.desc, - _ => &[], - }; - if !desc.is_empty() { - println!(); - for line in desc { - println!("{}", line); - } - } -} fn show_entry(files: &Files, entry: &Entry) { let command = files.command(entry.source); - let kind = util::display_kind(LineLayout::entry_kind(entry)); - println!("{} {} {}", "Title:".bright_black(), kind, entry.title); + match entry.kind { + EntryKind::Task => println!("TASK {}", command.title()), + EntryKind::TaskDone(when) => { + println!("DONE {}", command.title()); + println!("DONE AT {}", when); + } + EntryKind::Note => println!("NOTE {}", command.title()), + EntryKind::Birthday(Some(age)) => { + println!("BIRTHDAY {}", command.title()); + println!("AGE {}", age); + } + EntryKind::Birthday(None) => { + println!("BIRTHDAY {}", command.title()); + println!("AGE UNKNOWN"); + } + } - let what = match entry.kind { - EntryKind::Task => "Task".to_string(), - EntryKind::TaskDone(date) => format!("Task, done {date}"), - EntryKind::TaskCanceled(date) => format!("Task, canceled {date}"), - EntryKind::Note => "Note".to_string(), - EntryKind::Birthday(None) => "Birthday, age unknown".to_string(), - EntryKind::Birthday(Some(age)) => format!("Birthday, age {age}"), - }; - println!("{} {}", "What:".bright_black(), what); + if let Some(dates) = entry.dates { + println!("DATE {}", dates.sorted()); + } else { + println!("NO DATE"); + } - let when = match entry.dates { - None => "no date".to_string(), - Some(date) => format!("{}", date.sorted()), - }; - println!("{} {}", "When:".bright_black(), when); - - println!("{} {}", "Where:".bright_black(), fmt_where(files, &command)); - - print_desc(&command); -} - -fn show_log(files: &Files, log: Sourced<'_, Log>) { - let command = files.command(log.source); - - println!("{} Log entry", "What:".bright_black()); - println!("{} {}", "When:".bright_black(), log.value.date); - - println!("{} {}", "Where:".bright_black(), fmt_where(files, &command)); - - print_desc(&command); -} - -fn show_ident(files: &Files, entries: &[Entry], layout: &LineLayout, ident: Ident) { - match ident { - Ident::Number(n) => match layout.look_up_number(n) { - Ok(index) => show_entry(files, &entries[index]), - Err(e) => println!("{e}"), - }, - Ident::Date(date) => match files.log(date) { - Some(log) => show_log(files, log), - None => println!("{}", Error::NoSuchLog(date)), - }, + for line in command.desc() { + println!("# {}", line); } } -#[derive(Debug, Clone, Copy)] -pub enum Ident { - Number(usize), - Date(NaiveDate), -} - -pub fn show(files: &Files, entries: &[Entry], layout: &LineLayout, idents: &[Ident]) { - if idents.is_empty() { +pub fn show( + files: &Files, + entries: &[Entry], + layout: &LineLayout, + numbers: &[usize], +) -> Result<()> { + if numbers.is_empty() { // Nothing to do - return; + return Ok(()); } - show_ident(files, entries, layout, idents[0]); - for &ident in idents.iter().skip(1) { + let indices = numbers + .iter() + .map(|n| layout.look_up_number(*n)) + .collect::>>()?; + + show_entry(files, &entries[indices[0]]); + for &index in indices.iter().skip(1) { println!(); - println!(); - println!(); - show_ident(files, entries, layout, ident); + show_entry(files, &entries[index]); } + + Ok(()) } diff --git a/src/cli/util.rs b/src/cli/util.rs deleted file mode 100644 index e862088..0000000 --- a/src/cli/util.rs +++ /dev/null @@ -1,28 +0,0 @@ -use colored::{ColoredString, Colorize}; - -use super::error::{Error, Result}; -use super::layout::line::LineKind; - -pub fn display_kind(kind: LineKind) -> ColoredString { - match kind { - LineKind::Task => "T".magenta().bold(), - LineKind::Done => "D".green().bold(), - LineKind::Canceled => "C".red().bold(), - LineKind::Note => "N".blue().bold(), - LineKind::Birthday => "B".yellow().bold(), - } -} - -pub fn display_current_birthday_text(text: &str) -> ColoredString { - text.yellow() -} - -pub fn edit(input: &str) -> Result { - edit::edit(input).map_err(Error::EditingIo) -} - -pub fn edit_with_suffix(input: &str, suffix: &str) -> Result { - let mut builder = edit::Builder::new(); - builder.suffix(suffix); - edit::edit_with_builder(input, &builder).map_err(Error::EditingIo) -} diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 2cee250..0000000 --- a/src/error.rs +++ /dev/null @@ -1,31 +0,0 @@ -use codespan_reporting::diagnostic::Diagnostic; -use codespan_reporting::files::Files; -use codespan_reporting::term::{self, Config}; -use termcolor::StandardStream; - -pub trait Eprint<'a, F: Files<'a>> { - #[allow(single_use_lifetimes)] - fn eprint_diagnostic<'f: 'a>( - files: &'f F, - config: &Config, - diagnostic: &Diagnostic, - ) { - let mut out = StandardStream::stderr(termcolor::ColorChoice::Auto); - if let Err(e) = term::emit(&mut out, config, files, diagnostic) { - panic!("Error while reporting error: {e}"); - } - } - - #[allow(single_use_lifetimes)] - fn eprint<'f: 'a>(&self, files: &'f F, config: &Config); -} - -#[allow(single_use_lifetimes)] -pub fn eprint_error<'a, 'f: 'a, F, E>(files: &'f F, e: &E) -where - F: Files<'a>, - E: Eprint<'a, F>, -{ - let config = Config::default(); - e.eprint(files, &config); -} diff --git a/src/eval.rs b/src/eval.rs index 24859af..e596ab1 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1,14 +1,14 @@ use chrono::NaiveDate; -use crate::files::cli::{CliDate, CliDatum, CliRange}; -use crate::files::{FileSource, Files}; +use crate::files::arguments::{Range, RangeDate}; +use crate::files::Files; -use self::command::{CommandState, EvalCommand}; +use self::command::CommandState; pub use self::date::Dates; use self::delta::Delta; use self::entry::Entries; pub use self::entry::{Entry, EntryKind, EntryMode}; -pub use self::error::Error; +pub use self::error::{Error, Result, SourceInfo}; pub use self::range::DateRange; mod command; @@ -20,41 +20,22 @@ mod range; mod util; impl Files { - pub fn eval(&self, mode: EntryMode, range: DateRange) -> Result, Error> { + pub fn eval(&self, mode: EntryMode, range: DateRange) -> Result> { let mut entries = Entries::new(mode, range); for command in self.commands() { - let source = command.source; - if let Some(command) = EvalCommand::new(&command.value.value) { - for entry in CommandState::new(command, source, range).eval()?.entries() { - entries.add(entry); - } + for entry in CommandState::new(command, range).eval()?.entries() { + entries.add(entry); } } Ok(entries.entries()) } } -impl CliDate { - pub fn eval(&self, index: S, today: NaiveDate) -> Result> { - let mut date = match self.datum { - CliDatum::Date(d) => d, - CliDatum::Today => today, - }; - - if let Some(delta) = &self.delta { - let delta: Delta = delta.into(); - date = delta.apply_date(index, date)?; - } - - Ok(date) - } -} - -impl CliRange { - pub fn eval(&self, index: S, today: NaiveDate) -> Result> { +impl Range { + pub fn eval(&self, index: usize, today: NaiveDate) -> Result { let mut start = match self.start { - CliDatum::Date(d) => d, - CliDatum::Today => today, + RangeDate::Date(d) => d, + RangeDate::Today => today, }; if let Some(delta) = &self.start_delta { @@ -65,8 +46,8 @@ impl CliRange { let mut end = start; match self.end { - Some(CliDatum::Date(d)) => end = d, - Some(CliDatum::Today) => end = today, + Some(RangeDate::Date(d)) => end = d, + Some(RangeDate::Today) => end = today, None => {} } diff --git a/src/eval/command.rs b/src/eval/command.rs index 0bec665..cec8e2d 100644 --- a/src/eval/command.rs +++ b/src/eval/command.rs @@ -1,100 +1,31 @@ use std::collections::HashMap; -use chrono::{Duration, NaiveDate}; +use chrono::NaiveDate; -use crate::files::commands::{ - self, BirthdaySpec, Command, Done, DoneDate, DoneKind, Note, Spec, Statement, Task, -}; -use crate::files::primitives::{Span, Spanned, Time}; -use crate::files::{FileSource, Source}; +use crate::files::commands::{BirthdaySpec, Command, Done, DoneDate, Note, Spec, Statement, Task}; +use crate::files::primitives::Span; +use crate::files::SourcedCommand; use super::date::Dates; -use super::delta::Delta; -use super::{DateRange, Entry, EntryKind, Error}; +use super::{DateRange, Entry, EntryKind, Error, Result}; mod birthday; mod date; mod formula; -/// A command that can be evaluated. -pub enum EvalCommand<'a> { - Task(&'a Task), - Note(&'a Note), -} - -impl<'a> EvalCommand<'a> { - pub fn new(command: &'a Command) -> Option { - match command { - Command::Task(task) => Some(Self::Task(task)), - Command::Note(note) => Some(Self::Note(note)), - _ => None, - } - } - - fn statements(&self) -> &[Statement] { - match self { - Self::Task(task) => &task.statements, - Self::Note(note) => ¬e.statements, - } - } - - fn kind(&self) -> EntryKind { - match self { - Self::Task(_) => EntryKind::Task, - Self::Note(_) => EntryKind::Note, - } - } - - fn title(&self) -> String { - match self { - Self::Task(task) => task.title.clone(), - Self::Note(note) => note.title.clone(), - } - } - - fn has_description(&self) -> bool { - match self { - Self::Task(task) => !task.desc.is_empty(), - Self::Note(note) => !note.desc.is_empty(), - } - } - - /// Last root date mentioned in any `DONE`. - fn last_done_root(&self) -> Option { - match self { - Self::Task(task) => task - .done - .iter() - .filter_map(|done| done.date.map(DoneDate::root)) - .max(), - Self::Note(_) => None, - } - } - - /// Last completion date mentioned in any `DONE`. - fn last_done_completion(&self) -> Option { - match self { - Self::Task(task) => task.done.iter().map(|done| done.done_at).max(), - Self::Note(_) => None, - } - } -} - pub struct CommandState<'a> { - command: EvalCommand<'a>, - source: Source, + command: SourcedCommand<'a>, range: DateRange, from: Option, until: Option, - remind: Option>, dated: HashMap, undated: Vec, } impl<'a> CommandState<'a> { - pub fn new(command: EvalCommand<'a>, source: Source, mut range: DateRange) -> Self { + pub fn new(command: SourcedCommand<'a>, mut range: DateRange) -> Self { // If we don't calculate entries for the source of the move command, it // fails even though the user did nothing wrong. Also, move commands (or // chains thereof) may move an initially out-of-range entry into range. @@ -102,28 +33,26 @@ impl<'a> CommandState<'a> { // To fix this, we just expand the range to contain all move command // sources. This is a quick fix, but until it becomes a performance // issue (if ever), it's probably fine. - for statement in command.statements() { + for statement in command.command.statements() { if let Statement::Move { from, .. } = statement { range = range.containing(*from) } } Self { - command, - source, range, + command, from: None, until: None, - remind: None, dated: HashMap::new(), undated: Vec::new(), } } - pub fn eval(mut self) -> Result> { - match self.command { - EvalCommand::Task(task) => self.eval_task(task)?, - EvalCommand::Note(note) => self.eval_note(note)?, + pub fn eval(mut self) -> Result { + match self.command.command { + Command::Task(task) => self.eval_task(task)?, + Command::Note(note) => self.eval_note(note)?, } Ok(self) } @@ -137,10 +66,30 @@ impl<'a> CommandState<'a> { // Helper functions - fn range_with_remind(&self) -> DateRange { - match &self.remind { - None => self.range, - Some(delta) => self.range.expand_by(&delta.value), + fn kind(&self) -> EntryKind { + match self.command.command { + Command::Task(_) => EntryKind::Task, + Command::Note(_) => EntryKind::Note, + } + } + + /// Last root date mentioned in any `DONE`. + fn last_done_root(&self) -> Option { + match self.command.command { + Command::Task(task) => task + .done + .iter() + .filter_map(|done| done.date.map(DoneDate::root)) + .max(), + Command::Note(_) => None, + } + } + + /// Last completion date mentioned in any `DONE`. + fn last_done_completion(&self) -> Option { + match self.command.command { + Command::Task(task) => task.done.iter().map(|done| done.done_at).max(), + Command::Note(_) => None, } } @@ -164,42 +113,11 @@ impl<'a> CommandState<'a> { } } - fn entry_with_remind( - &self, - kind: EntryKind, - dates: Option, - ) -> Result> { - let remind = if let (Some(dates), Some(delta)) = (dates, &self.remind) { - let index = self.source.file(); - let start = dates.sorted().root(); - let remind = delta.value.apply_date(index, dates.sorted().root())?; - if remind >= start { - return Err(Error::RemindDidNotMoveBackwards { - index, - span: delta.span, - from: start, - to: remind, - }); - } - Some(remind) - } else { - None - }; - - Ok(Entry::new( - self.source, - kind, - self.command.title(), - self.command.has_description(), - dates, - remind, - )) - } - /// Add an entry, respecting [`Self::from`] and [`Self::until`]. Does not /// overwrite existing entries if a root date is specified. - fn add(&mut self, entry: Entry) { - if let Some(dates) = entry.dates { + fn add(&mut self, kind: EntryKind, dates: Option) { + let entry = Entry::new(self.command.source, kind, dates); + if let Some(dates) = dates { let root = dates.root(); if let Some(from) = self.from { if root < from { @@ -219,8 +137,9 @@ impl<'a> CommandState<'a> { /// Add an entry, ignoring [`Self::from`] and [`Self::until`]. Always /// overwrites existing entries if a root date is specified. - fn add_forced(&mut self, entry: Entry) { - if let Some(dates) = entry.dates { + fn add_forced(&mut self, kind: EntryKind, dates: Option) { + let entry = Entry::new(self.command.source, kind, dates); + if let Some(dates) = dates { self.dated.insert(dates.root(), entry); } else { self.undated.push(entry); @@ -235,53 +154,47 @@ impl<'a> CommandState<'a> { .any(|s| matches!(s, Statement::Date(_) | Statement::BDate(_))) } - fn eval_task(&mut self, task: &Task) -> Result<(), Error> { + fn eval_task(&mut self, task: &Task) -> Result<()> { if Self::has_date_stmt(&task.statements) { for statement in &task.statements { self.eval_statement(statement)?; } } else if task.done.is_empty() { - self.add(self.entry_with_remind(self.command.kind(), None)?); + self.add(self.kind(), None); } for done in &task.done { - self.eval_done(done)?; + self.eval_done(done); } Ok(()) } - fn eval_note(&mut self, note: &Note) -> Result<(), Error> { + fn eval_note(&mut self, note: &Note) -> Result<()> { if Self::has_date_stmt(¬e.statements) { for statement in ¬e.statements { self.eval_statement(statement)?; } } else { - self.add(self.entry_with_remind(self.command.kind(), None)?); + self.add(self.kind(), None); } Ok(()) } - fn eval_statement(&mut self, statement: &Statement) -> Result<(), Error> { + fn eval_statement(&mut self, statement: &Statement) -> Result<()> { match statement { Statement::Date(spec) => self.eval_date(spec)?, - Statement::BDate(spec) => self.eval_bdate(spec)?, + Statement::BDate(spec) => self.eval_bdate(spec), Statement::From(date) => self.from = *date, Statement::Until(date) => self.until = *date, Statement::Except(date) => self.eval_except(*date), - Statement::Move { - span, - from, - to, - to_time, - } => self.eval_move(*span, *from, *to, *to_time)?, - Statement::Remind(delta) => self.eval_remind(delta), + Statement::Move { span, from, to } => self.eval_move(*span, *from, *to)?, } Ok(()) } - fn eval_date(&mut self, spec: &Spec) -> Result<(), Error> { + fn eval_date(&mut self, spec: &Spec) -> Result<()> { match spec { Spec::Date(spec) => self.eval_date_spec(spec.into()), Spec::Weekday(spec) => self.eval_formula_spec(spec.into()), @@ -289,69 +202,34 @@ impl<'a> CommandState<'a> { } } - fn eval_bdate(&mut self, spec: &BirthdaySpec) -> Result<(), Error> { - self.eval_birthday_spec(spec) + fn eval_bdate(&mut self, spec: &BirthdaySpec) { + self.eval_birthday_spec(spec); } fn eval_except(&mut self, date: NaiveDate) { - // TODO Error if nothing is removed? self.dated.remove(&date); } - fn eval_move( - &mut self, - span: Span, - from: NaiveDate, - to: Option, - to_time: Option>, - ) -> Result<(), Error> { + fn eval_move(&mut self, span: Span, from: NaiveDate, to: NaiveDate) -> Result<()> { if let Some(mut entry) = self.dated.remove(&from) { - let mut dates = entry.dates.expect("comes from self.dated"); - - // Determine delta - let mut delta = Duration::zero(); - if let Some(to) = to { - delta = delta + (to - dates.root()); + if let Some(dates) = entry.dates { + let delta = to - from; + entry.dates = Some(dates.move_by(delta)); } - if let Some(to_time) = to_time { - if let Some((root, _)) = dates.times() { - delta = delta + Duration::minutes(root.minutes_to(to_time.value)); - } else { - return Err(Error::TimedMoveWithoutTime { - index: self.source.file(), - span: to_time.span, - }); - } - } - - dates = dates.move_by(delta); - entry.dates = Some(dates); - self.dated.insert(dates.root(), entry); - + self.dated.insert(to, entry); Ok(()) } else { Err(Error::MoveWithoutSource { - index: self.source.file(), + index: self.command.source.file(), span, }) } } - fn eval_remind(&mut self, delta: &Option>) { - if let Some(delta) = delta { - self.remind = Some(Spanned::new(delta.span, (&delta.value).into())); - } else { - self.remind = None; - } - } - - fn eval_done(&mut self, done: &Done) -> Result<(), Error> { - let kind = match done.kind { - DoneKind::Done => EntryKind::TaskDone(done.done_at), - DoneKind::Canceled => EntryKind::TaskCanceled(done.done_at), - }; - let dates = done.date.map(|date| date.into()); - self.add_forced(self.entry_with_remind(kind, dates)?); - Ok(()) + fn eval_done(&mut self, done: &Done) { + self.add_forced( + EntryKind::TaskDone(done.done_at), + done.date.map(|date| date.into()), + ); } } diff --git a/src/eval/command/birthday.rs b/src/eval/command/birthday.rs index d6c05c7..829160e 100644 --- a/src/eval/command/birthday.rs +++ b/src/eval/command/birthday.rs @@ -1,18 +1,16 @@ use chrono::{Datelike, NaiveDate}; use crate::files::commands::BirthdaySpec; -use crate::files::FileSource; use super::super::command::CommandState; use super::super::date::Dates; -use super::super::error::Error; use super::super::EntryKind; -impl CommandState<'_> { - pub fn eval_birthday_spec(&mut self, spec: &BirthdaySpec) -> Result<(), Error> { - let range = match self.limit_from_until(self.range_with_remind()) { +impl<'a> CommandState<'a> { + pub fn eval_birthday_spec(&mut self, spec: &BirthdaySpec) { + let range = match self.limit_from_until(self.range) { Some(range) => range, - None => return Ok(()), + None => return, }; for year in range.years() { @@ -28,19 +26,15 @@ impl CommandState<'_> { let kind = EntryKind::Birthday(age); if let Some(date) = spec.date.with_year(year) { - self.add( - self.entry_with_remind(EntryKind::Birthday(age), Some(Dates::new(date, date)))?, - ); + self.add(EntryKind::Birthday(age), Some(Dates::new(date, date))); } else { assert_eq!(spec.date.month(), 2); assert_eq!(spec.date.day(), 29); - let first = NaiveDate::from_ymd_opt(year, 2, 28).unwrap(); - let second = NaiveDate::from_ymd_opt(year, 3, 1).unwrap(); - self.add(self.entry_with_remind(kind, Some(Dates::new(first, second)))?); + let first = NaiveDate::from_ymd(year, 2, 28); + let second = NaiveDate::from_ymd(year, 3, 1); + self.add(kind, Some(Dates::new(first, second))); } } - - Ok(()) } } diff --git a/src/eval/command/date.rs b/src/eval/command/date.rs index 58095f8..9fe1292 100644 --- a/src/eval/command/date.rs +++ b/src/eval/command/date.rs @@ -1,14 +1,12 @@ use chrono::NaiveDate; -use crate::files::commands; +use crate::files::commands::{self, Command}; use crate::files::primitives::{Spanned, Time}; -use crate::files::FileSource; use super::super::command::CommandState; use super::super::date::Dates; use super::super::delta::{Delta, DeltaStep}; -use super::super::{DateRange, Error}; -use super::EvalCommand; +use super::super::{DateRange, Error, Result}; pub struct DateSpec { pub start: NaiveDate, @@ -74,30 +72,28 @@ impl DateSpec { /// `start` date itself should be skipped (and thus not result in an entry). /// This may be necessary if [`Self::start_at_done`] is set. fn start_and_range(&self, s: &CommandState<'_>) -> Option<(NaiveDate, bool, DateRange)> { - let (start, skip, range) = match s.command { - EvalCommand::Task(_) => { + let (start, skip, range) = match s.command.command { + Command::Task(_) => { let (start, skip) = s - .command .last_done_completion() .map(|start| (start, true)) .filter(|_| self.start_at_done) .unwrap_or((self.start, false)); let range_from = s - .command .last_done_root() - .map(|date| date.succ_opt().unwrap()) + .map(|date| date.succ()) .unwrap_or(self.start); let range = s - .range_with_remind() + .range .expand_by(&self.end_delta) .move_by(&self.start_delta) .with_from(range_from)?; (start, skip, range) } - EvalCommand::Note(_) => { + Command::Note(_) => { let start = self.start; let range = s - .range_with_remind() + .range .expand_by(&self.end_delta) .move_by(&self.start_delta); (start, false, range) @@ -107,11 +103,7 @@ impl DateSpec { Some((start, skip, range)) } - fn step( - index: FileSource, - from: NaiveDate, - repeat: &Spanned, - ) -> Result> { + fn step(index: usize, from: NaiveDate, repeat: &Spanned) -> Result { let to = repeat.value.apply_date(index, from)?; if to > from { Ok(to) @@ -125,7 +117,7 @@ impl DateSpec { } } - fn dates(&self, index: FileSource, start: NaiveDate) -> Result> { + fn dates(&self, index: usize, start: NaiveDate) -> Result { let root = self.start_delta.apply_date(index, start)?; Ok(if let Some(root_time) = self.start_time { let (other, other_time) = self.end_delta.apply_date_time(index, root, root_time)?; @@ -137,9 +129,9 @@ impl DateSpec { } } -impl CommandState<'_> { - pub fn eval_date_spec(&mut self, spec: DateSpec) -> Result<(), Error> { - let index = self.source.file(); +impl<'a> CommandState<'a> { + pub fn eval_date_spec(&mut self, spec: DateSpec) -> Result<()> { + let index = self.command.source.file(); if let Some(repeat) = &spec.repeat { if let Some((mut start, skip, range)) = spec.start_and_range(self) { if skip { @@ -150,13 +142,13 @@ impl CommandState<'_> { } while start <= range.until() { let dates = spec.dates(index, start)?; - self.add(self.entry_with_remind(self.command.kind(), Some(dates))?); + self.add(self.kind(), Some(dates)); start = DateSpec::step(index, start, repeat)?; } } } else { let dates = spec.dates(index, spec.start)?; - self.add(self.entry_with_remind(self.command.kind(), Some(dates))?); + self.add(self.kind(), Some(dates)); } Ok(()) } diff --git a/src/eval/command/formula.rs b/src/eval/command/formula.rs index 138d3ce..43f7d21 100644 --- a/src/eval/command/formula.rs +++ b/src/eval/command/formula.rs @@ -1,14 +1,12 @@ use chrono::{Datelike, Duration, NaiveDate}; -use crate::files::commands; +use crate::files::commands::{self, Command}; use crate::files::primitives::{Span, Spanned, Time, Weekday}; -use crate::files::FileSource; use super::super::command::CommandState; use super::super::date::Dates; use super::super::delta::{Delta, DeltaStep}; -use super::super::{util, DateRange, Error}; -use super::EvalCommand; +use super::super::{util, DateRange, Error, Result}; fn b2i(b: bool) -> i64 { if b { @@ -49,61 +47,58 @@ pub enum Var { } impl Var { - fn eval(self, index: S, date: NaiveDate) -> Result> { + fn eval(self, index: usize, date: NaiveDate) -> Result { Ok(match self { - Self::JulianDay => date.num_days_from_ce().into(), - Self::Year => date.year().into(), - Self::YearLength => util::year_length(date.year()).into(), - Self::YearDay => date.ordinal().into(), - Self::YearDayReverse => (util::year_length(date.year()) - date.ordinal0()).into(), - Self::YearWeek => (date.ordinal0().div_euclid(7) + 1).into(), - Self::YearWeekReverse => { + Var::JulianDay => date.num_days_from_ce().into(), + Var::Year => date.year().into(), + Var::YearLength => util::year_length(date.year()).into(), + Var::YearDay => date.ordinal().into(), + Var::YearDayReverse => (util::year_length(date.year()) - date.ordinal0()).into(), + Var::YearWeek => (date.ordinal0().div_euclid(7) + 1).into(), + Var::YearWeekReverse => { #[allow(non_snake_case)] let yD = util::year_length(date.year()) - date.ordinal(); (yD.div_euclid(7) + 1).into() } - Self::Month => date.month().into(), - Self::MonthLength => util::month_length(date.year(), date.month()).into(), - Self::MonthWeek => (date.day0().div_euclid(7) + 1).into(), - Self::MonthWeekReverse => { + Var::Month => date.month().into(), + Var::MonthLength => util::month_length(date.year(), date.month()).into(), + Var::MonthWeek => (date.day0().div_euclid(7) + 1).into(), + Var::MonthWeekReverse => { #[allow(non_snake_case)] let mD = util::month_length(date.year(), date.month()) - date.day(); (mD.div_euclid(7) + 1).into() } - Self::Day => date.day().into(), - Self::DayReverse => { + Var::Day => date.day().into(), + Var::DayReverse => { let ml = util::month_length(date.year(), date.month()); (ml - date.day0()).into() } - Self::IsoYear => date.iso_week().year().into(), - Self::IsoYearLength => util::iso_year_length(date.iso_week().year()).into(), - Self::IsoWeek => date.iso_week().week().into(), - Self::Weekday => { + Var::IsoYear => date.iso_week().year().into(), + Var::IsoYearLength => util::iso_year_length(date.iso_week().year()).into(), + Var::IsoWeek => date.iso_week().week().into(), + Var::Weekday => { let wd: Weekday = date.weekday().into(); wd.num().into() } - Self::Easter(span) => { + Var::Easter(span) => { let e = computus::gregorian(date.year()).map_err(|e| Error::Easter { index, span, date, msg: e, })?; - NaiveDate::from_ymd_opt(e.year, e.month, e.day) - .unwrap() - .ordinal() - .into() + NaiveDate::from_ymd(e.year, e.month, e.day).ordinal().into() } - Self::IsWeekday => { + Var::IsWeekday => { let wd: Weekday = date.weekday().into(); b2i(!wd.is_weekend()) } - Self::IsWeekend => { + Var::IsWeekend => { let wd: Weekday = date.weekday().into(); b2i(wd.is_weekend()) } - Self::IsLeapYear => b2i(util::is_leap_year(date.year())), - Self::IsIsoLeapYear => b2i(util::is_iso_leap_year(date.year())), + Var::IsLeapYear => b2i(util::is_leap_year(date.year())), + Var::IsIsoLeapYear => b2i(util::is_iso_leap_year(date.year())), }) } } @@ -207,15 +202,15 @@ impl From for Expr { } impl Expr { - fn eval(&self, index: S, date: NaiveDate) -> Result> { + fn eval(&self, index: usize, date: NaiveDate) -> Result { Ok(match self { - Self::Lit(l) => *l, - Self::Var(v) => v.eval(index, date)?, - Self::Neg(e) => -e.eval(index, date)?, - Self::Add(a, b) => a.eval(index, date)? + b.eval(index, date)?, - Self::Sub(a, b) => a.eval(index, date)? - b.eval(index, date)?, - Self::Mul(a, b) => a.eval(index, date)? * b.eval(index, date)?, - Self::Div(a, b, span) => { + Expr::Lit(l) => *l, + Expr::Var(v) => v.eval(index, date)?, + Expr::Neg(e) => -e.eval(index, date)?, + Expr::Add(a, b) => a.eval(index, date)? + b.eval(index, date)?, + Expr::Sub(a, b) => a.eval(index, date)? - b.eval(index, date)?, + Expr::Mul(a, b) => a.eval(index, date)? * b.eval(index, date)?, + Expr::Div(a, b, span) => { let b = b.eval(index, date)?; if b == 0 { return Err(Error::DivByZero { @@ -226,7 +221,7 @@ impl Expr { } a.eval(index, date)?.div_euclid(b) } - Self::Mod(a, b, span) => { + Expr::Mod(a, b, span) => { let b = b.eval(index, date)?; if b == 0 { return Err(Error::ModByZero { @@ -237,16 +232,16 @@ impl Expr { } a.eval(index, date)?.rem_euclid(b) } - Self::Eq(a, b) => b2i(a.eval(index, date)? == b.eval(index, date)?), - Self::Neq(a, b) => b2i(a.eval(index, date)? != b.eval(index, date)?), - Self::Lt(a, b) => b2i(a.eval(index, date)? < b.eval(index, date)?), - Self::Lte(a, b) => b2i(a.eval(index, date)? <= b.eval(index, date)?), - Self::Gt(a, b) => b2i(a.eval(index, date)? > b.eval(index, date)?), - Self::Gte(a, b) => b2i(a.eval(index, date)? >= b.eval(index, date)?), - Self::Not(e) => b2i(!i2b(e.eval(index, date)?)), - Self::And(a, b) => b2i(i2b(a.eval(index, date)?) && i2b(b.eval(index, date)?)), - Self::Or(a, b) => b2i(i2b(a.eval(index, date)?) || i2b(b.eval(index, date)?)), - Self::Xor(a, b) => b2i(i2b(a.eval(index, date)?) ^ i2b(b.eval(index, date)?)), + Expr::Eq(a, b) => b2i(a.eval(index, date)? == b.eval(index, date)?), + Expr::Neq(a, b) => b2i(a.eval(index, date)? != b.eval(index, date)?), + Expr::Lt(a, b) => b2i(a.eval(index, date)? < b.eval(index, date)?), + Expr::Lte(a, b) => b2i(a.eval(index, date)? <= b.eval(index, date)?), + Expr::Gt(a, b) => b2i(a.eval(index, date)? > b.eval(index, date)?), + Expr::Gte(a, b) => b2i(a.eval(index, date)? >= b.eval(index, date)?), + Expr::Not(e) => b2i(!i2b(e.eval(index, date)?)), + Expr::And(a, b) => b2i(i2b(a.eval(index, date)?) && i2b(b.eval(index, date)?)), + Expr::Or(a, b) => b2i(i2b(a.eval(index, date)?) || i2b(b.eval(index, date)?)), + Expr::Xor(a, b) => b2i(i2b(a.eval(index, date)?) ^ i2b(b.eval(index, date)?)), }) } } @@ -329,16 +324,16 @@ impl From<&commands::WeekdaySpec> for FormulaSpec { impl FormulaSpec { fn range(&self, s: &CommandState<'_>) -> Option { let mut range = s - .range_with_remind() + .range .expand_by(&self.end_delta) .move_by(&self.start_delta); - if let EvalCommand::Task(_) = s.command { - if let Some(last_done_root) = s.command.last_done_root() { - range = range.with_from(last_done_root.succ_opt().unwrap())?; + if let Command::Task(_) = s.command.command { + if let Some(last_done_root) = s.last_done_root() { + range = range.with_from(last_done_root.succ())?; } else if let Some(from) = s.from { range = range.with_from(from)?; - } else if matches!(s.command, EvalCommand::Task(_)) { + } else if matches!(s.command.command, Command::Task(_)) { // We have no idea if we missed any tasks since the user hasn't // specified a `FROM`, so we just just look back one year. Any // task older than a year is probably not important anyways... @@ -349,7 +344,7 @@ impl FormulaSpec { s.limit_from_until(range) } - fn dates(&self, index: FileSource, start: NaiveDate) -> Result> { + fn dates(&self, index: usize, start: NaiveDate) -> Result { let root = self.start_delta.apply_date(index, start)?; Ok(if let Some(root_time) = self.start_time { let (other, other_time) = self.end_delta.apply_date_time(index, root, root_time)?; @@ -360,19 +355,19 @@ impl FormulaSpec { }) } - fn eval(&self, index: FileSource, date: NaiveDate) -> Result> { + fn eval(&self, index: usize, date: NaiveDate) -> Result { Ok(i2b(self.start.eval(index, date)?)) } } -impl CommandState<'_> { - pub fn eval_formula_spec(&mut self, spec: FormulaSpec) -> Result<(), Error> { +impl<'a> CommandState<'a> { + pub fn eval_formula_spec(&mut self, spec: FormulaSpec) -> Result<()> { if let Some(range) = spec.range(self) { - let index = self.source.file(); + let index = self.command.source.file(); for day in range.days() { if spec.eval(index, day)? { let dates = spec.dates(index, day)?; - self.add(self.entry_with_remind(self.command.kind(), Some(dates))?); + self.add(self.kind(), Some(dates)); } } } @@ -391,31 +386,21 @@ mod tests { use super::{Expr, Var}; fn expr(expr: &Expr, date: NaiveDate, target: i64) { - if let Ok(result) = expr.eval((), date) { + if let Ok(result) = expr.eval(0, date) { assert_eq!(result, target); } else { - panic!("formula produced error for day {date}"); + panic!("formula produced error for day {}", date); } } - fn expr_ymd(e: &Expr, ymd: (i32, u32, u32), target: i64) { - let (y, m, d) = ymd; - expr(e, NaiveDate::from_ymd_opt(y, m, d).unwrap(), target); - } - - fn expr_yo(e: &Expr, yo: (i32, u32), target: i64) { - let (y, o) = yo; - expr(e, NaiveDate::from_yo_opt(y, o).unwrap(), target); - } - #[test] fn julian_day() { let e = Expr::Var(Var::JulianDay); for delta in -1000..1000 { - let d1 = NaiveDate::from_ymd_opt(2021, 12, 19).unwrap(); + let d1 = NaiveDate::from_ymd(2021, 12, 19); let d2 = d1 + Duration::days(delta); - assert_eq!(e.eval((), d2).unwrap() - e.eval((), d1).unwrap(), delta); + assert_eq!(e.eval(0, d2).unwrap() - e.eval(0, d1).unwrap(), delta); } } @@ -424,21 +409,21 @@ mod tests { let e = Expr::Var(Var::Year); for y in -3000..=3000 { - expr_ymd(&e, (y, 2, 19), y.into()); + expr(&e, NaiveDate::from_ymd(y, 2, 19), y.into()); } - expr_ymd(&e, (2021, 1, 1), 2021); - expr_ymd(&e, (2021, 12, 31), 2021); + expr(&e, NaiveDate::from_ymd(2021, 1, 1), 2021); + expr(&e, NaiveDate::from_ymd(2021, 12, 31), 2021); } #[test] fn year_length() { let e = Expr::Var(Var::YearLength); - expr_ymd(&e, (2000, 12, 19), 366); - expr_ymd(&e, (2019, 12, 19), 365); - expr_ymd(&e, (2020, 12, 19), 366); - expr_ymd(&e, (2021, 12, 19), 365); + expr(&e, NaiveDate::from_ymd(2000, 12, 19), 366); + expr(&e, NaiveDate::from_ymd(2019, 12, 19), 365); + expr(&e, NaiveDate::from_ymd(2020, 12, 19), 366); + expr(&e, NaiveDate::from_ymd(2021, 12, 19), 365); } #[test] @@ -446,11 +431,11 @@ mod tests { let e = Expr::Var(Var::YearDay); for i in 1..=365 { - expr_yo(&e, (2020, i), i.into()); - expr_yo(&e, (2021, i), i.into()); + expr(&e, NaiveDate::from_yo(2020, i), i.into()); + expr(&e, NaiveDate::from_yo(2021, i), i.into()); } - expr_yo(&e, (2020, 366), 366); + expr(&e, NaiveDate::from_yo(2020, 366), 366); } #[test] @@ -458,14 +443,14 @@ mod tests { let e = Expr::Var(Var::YearDayReverse); for i in 1..=365 { - expr_yo(&e, (2020, i), (366 - i + 1).into()); - expr_yo(&e, (2021, i), (365 - i + 1).into()); + expr(&e, NaiveDate::from_yo(2020, i), (366 - i + 1).into()); + expr(&e, NaiveDate::from_yo(2021, i), (365 - i + 1).into()); } - expr_ymd(&e, (2020, 1, 1), 366); - expr_ymd(&e, (2021, 1, 1), 365); - expr_ymd(&e, (2020, 12, 31), 1); - expr_ymd(&e, (2021, 12, 31), 1); + expr(&e, NaiveDate::from_ymd(2020, 1, 1), 366); + expr(&e, NaiveDate::from_ymd(2021, 1, 1), 365); + expr(&e, NaiveDate::from_ymd(2020, 12, 31), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 31), 1); } #[test] @@ -473,32 +458,32 @@ mod tests { let e = Expr::Var(Var::YearWeek); for y in 1000..3000 { - expr_ymd(&e, (y, 1, 1), 1); - expr_ymd(&e, (y, 1, 2), 1); - expr_ymd(&e, (y, 1, 3), 1); - expr_ymd(&e, (y, 1, 4), 1); - expr_ymd(&e, (y, 1, 5), 1); - expr_ymd(&e, (y, 1, 6), 1); - expr_ymd(&e, (y, 1, 7), 1); - expr_ymd(&e, (y, 1, 8), 2); - expr_ymd(&e, (y, 1, 9), 2); - expr_ymd(&e, (y, 1, 10), 2); - expr_ymd(&e, (y, 1, 11), 2); - expr_ymd(&e, (y, 1, 12), 2); - expr_ymd(&e, (y, 1, 13), 2); - expr_ymd(&e, (y, 1, 14), 2); - expr_ymd(&e, (y, 1, 15), 3); + expr(&e, NaiveDate::from_ymd(y, 1, 1), 1); + expr(&e, NaiveDate::from_ymd(y, 1, 2), 1); + expr(&e, NaiveDate::from_ymd(y, 1, 3), 1); + expr(&e, NaiveDate::from_ymd(y, 1, 4), 1); + expr(&e, NaiveDate::from_ymd(y, 1, 5), 1); + expr(&e, NaiveDate::from_ymd(y, 1, 6), 1); + expr(&e, NaiveDate::from_ymd(y, 1, 7), 1); + expr(&e, NaiveDate::from_ymd(y, 1, 8), 2); + expr(&e, NaiveDate::from_ymd(y, 1, 9), 2); + expr(&e, NaiveDate::from_ymd(y, 1, 10), 2); + expr(&e, NaiveDate::from_ymd(y, 1, 11), 2); + expr(&e, NaiveDate::from_ymd(y, 1, 12), 2); + expr(&e, NaiveDate::from_ymd(y, 1, 13), 2); + expr(&e, NaiveDate::from_ymd(y, 1, 14), 2); + expr(&e, NaiveDate::from_ymd(y, 1, 15), 3); } - expr_ymd(&e, (2020, 12, 28), 52); - expr_ymd(&e, (2020, 12, 29), 52); - expr_ymd(&e, (2020, 12, 30), 53); - expr_ymd(&e, (2020, 12, 31), 53); + expr(&e, NaiveDate::from_ymd(2020, 12, 28), 52); + expr(&e, NaiveDate::from_ymd(2020, 12, 29), 52); + expr(&e, NaiveDate::from_ymd(2020, 12, 30), 53); + expr(&e, NaiveDate::from_ymd(2020, 12, 31), 53); - expr_ymd(&e, (2021, 12, 28), 52); - expr_ymd(&e, (2021, 12, 29), 52); - expr_ymd(&e, (2021, 12, 30), 52); - expr_ymd(&e, (2021, 12, 31), 53); + expr(&e, NaiveDate::from_ymd(2021, 12, 28), 52); + expr(&e, NaiveDate::from_ymd(2021, 12, 29), 52); + expr(&e, NaiveDate::from_ymd(2021, 12, 30), 52); + expr(&e, NaiveDate::from_ymd(2021, 12, 31), 53); } #[test] @@ -506,32 +491,32 @@ mod tests { let e = Expr::Var(Var::YearWeekReverse); for y in 1000..3000 { - expr_ymd(&e, (y, 12, 31), 1); - expr_ymd(&e, (y, 12, 30), 1); - expr_ymd(&e, (y, 12, 29), 1); - expr_ymd(&e, (y, 12, 28), 1); - expr_ymd(&e, (y, 12, 27), 1); - expr_ymd(&e, (y, 12, 26), 1); - expr_ymd(&e, (y, 12, 25), 1); - expr_ymd(&e, (y, 12, 24), 2); - expr_ymd(&e, (y, 12, 23), 2); - expr_ymd(&e, (y, 12, 22), 2); - expr_ymd(&e, (y, 12, 21), 2); - expr_ymd(&e, (y, 12, 20), 2); - expr_ymd(&e, (y, 12, 19), 2); - expr_ymd(&e, (y, 12, 18), 2); - expr_ymd(&e, (y, 12, 17), 3); + expr(&e, NaiveDate::from_ymd(y, 12, 31), 1); + expr(&e, NaiveDate::from_ymd(y, 12, 30), 1); + expr(&e, NaiveDate::from_ymd(y, 12, 29), 1); + expr(&e, NaiveDate::from_ymd(y, 12, 28), 1); + expr(&e, NaiveDate::from_ymd(y, 12, 27), 1); + expr(&e, NaiveDate::from_ymd(y, 12, 26), 1); + expr(&e, NaiveDate::from_ymd(y, 12, 25), 1); + expr(&e, NaiveDate::from_ymd(y, 12, 24), 2); + expr(&e, NaiveDate::from_ymd(y, 12, 23), 2); + expr(&e, NaiveDate::from_ymd(y, 12, 22), 2); + expr(&e, NaiveDate::from_ymd(y, 12, 21), 2); + expr(&e, NaiveDate::from_ymd(y, 12, 20), 2); + expr(&e, NaiveDate::from_ymd(y, 12, 19), 2); + expr(&e, NaiveDate::from_ymd(y, 12, 18), 2); + expr(&e, NaiveDate::from_ymd(y, 12, 17), 3); } - expr_ymd(&e, (2020, 1, 1), 53); - expr_ymd(&e, (2020, 1, 2), 53); - expr_ymd(&e, (2020, 1, 3), 52); - expr_ymd(&e, (2020, 1, 4), 52); + expr(&e, NaiveDate::from_ymd(2020, 1, 1), 53); + expr(&e, NaiveDate::from_ymd(2020, 1, 2), 53); + expr(&e, NaiveDate::from_ymd(2020, 1, 3), 52); + expr(&e, NaiveDate::from_ymd(2020, 1, 4), 52); - expr_ymd(&e, (2021, 1, 1), 53); - expr_ymd(&e, (2021, 1, 2), 52); - expr_ymd(&e, (2021, 1, 3), 52); - expr_ymd(&e, (2021, 1, 4), 52); + expr(&e, NaiveDate::from_ymd(2021, 1, 1), 53); + expr(&e, NaiveDate::from_ymd(2021, 1, 2), 52); + expr(&e, NaiveDate::from_ymd(2021, 1, 3), 52); + expr(&e, NaiveDate::from_ymd(2021, 1, 4), 52); } #[test] @@ -539,7 +524,7 @@ mod tests { let e = Expr::Var(Var::Month); for y in -1000..=3000 { for m in 1..=12 { - expr_ymd(&e, (y, m, 13), m.into()); + expr(&e, NaiveDate::from_ymd(y, m, 13), m.into()); } } } @@ -548,96 +533,96 @@ mod tests { fn month_length() { let e = Expr::Var(Var::MonthLength); - expr_ymd(&e, (2021, 1, 5), 31); - expr_ymd(&e, (2021, 2, 5), 28); - expr_ymd(&e, (2021, 3, 5), 31); - expr_ymd(&e, (2021, 4, 5), 30); - expr_ymd(&e, (2021, 5, 5), 31); - expr_ymd(&e, (2021, 6, 5), 30); - expr_ymd(&e, (2021, 7, 5), 31); - expr_ymd(&e, (2021, 8, 5), 31); - expr_ymd(&e, (2021, 9, 5), 30); - expr_ymd(&e, (2021, 10, 5), 31); - expr_ymd(&e, (2021, 11, 5), 30); - expr_ymd(&e, (2021, 12, 5), 31); + expr(&e, NaiveDate::from_ymd(2021, 1, 5), 31); + expr(&e, NaiveDate::from_ymd(2021, 2, 5), 28); + expr(&e, NaiveDate::from_ymd(2021, 3, 5), 31); + expr(&e, NaiveDate::from_ymd(2021, 4, 5), 30); + expr(&e, NaiveDate::from_ymd(2021, 5, 5), 31); + expr(&e, NaiveDate::from_ymd(2021, 6, 5), 30); + expr(&e, NaiveDate::from_ymd(2021, 7, 5), 31); + expr(&e, NaiveDate::from_ymd(2021, 8, 5), 31); + expr(&e, NaiveDate::from_ymd(2021, 9, 5), 30); + expr(&e, NaiveDate::from_ymd(2021, 10, 5), 31); + expr(&e, NaiveDate::from_ymd(2021, 11, 5), 30); + expr(&e, NaiveDate::from_ymd(2021, 12, 5), 31); - expr_ymd(&e, (2020, 2, 5), 29); - expr_ymd(&e, (2019, 2, 5), 28); - expr_ymd(&e, (2000, 2, 5), 29); + expr(&e, NaiveDate::from_ymd(2020, 2, 5), 29); + expr(&e, NaiveDate::from_ymd(2019, 2, 5), 28); + expr(&e, NaiveDate::from_ymd(2000, 2, 5), 29); } #[test] fn month_week() { let e = Expr::Var(Var::MonthWeek); - expr_ymd(&e, (2021, 12, 1), 1); - expr_ymd(&e, (2021, 12, 2), 1); - expr_ymd(&e, (2021, 12, 3), 1); - expr_ymd(&e, (2021, 12, 4), 1); - expr_ymd(&e, (2021, 12, 5), 1); - expr_ymd(&e, (2021, 12, 6), 1); - expr_ymd(&e, (2021, 12, 7), 1); - expr_ymd(&e, (2021, 12, 8), 2); - expr_ymd(&e, (2021, 12, 9), 2); - expr_ymd(&e, (2021, 12, 10), 2); - expr_ymd(&e, (2021, 12, 11), 2); - expr_ymd(&e, (2021, 12, 12), 2); - expr_ymd(&e, (2021, 12, 13), 2); - expr_ymd(&e, (2021, 12, 14), 2); - expr_ymd(&e, (2021, 12, 15), 3); - expr_ymd(&e, (2021, 12, 16), 3); - expr_ymd(&e, (2021, 12, 17), 3); - expr_ymd(&e, (2021, 12, 18), 3); - expr_ymd(&e, (2021, 12, 19), 3); - expr_ymd(&e, (2021, 12, 20), 3); - expr_ymd(&e, (2021, 12, 21), 3); - expr_ymd(&e, (2021, 12, 22), 4); - expr_ymd(&e, (2021, 12, 23), 4); - expr_ymd(&e, (2021, 12, 24), 4); - expr_ymd(&e, (2021, 12, 25), 4); - expr_ymd(&e, (2021, 12, 26), 4); - expr_ymd(&e, (2021, 12, 27), 4); - expr_ymd(&e, (2021, 12, 28), 4); - expr_ymd(&e, (2021, 12, 29), 5); - expr_ymd(&e, (2021, 12, 30), 5); - expr_ymd(&e, (2021, 12, 31), 5); + expr(&e, NaiveDate::from_ymd(2021, 12, 1), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 2), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 3), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 4), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 5), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 6), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 7), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 8), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 9), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 10), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 11), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 12), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 13), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 14), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 15), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 16), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 17), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 18), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 19), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 20), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 21), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 22), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 23), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 24), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 25), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 26), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 27), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 28), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 29), 5); + expr(&e, NaiveDate::from_ymd(2021, 12, 30), 5); + expr(&e, NaiveDate::from_ymd(2021, 12, 31), 5); } #[test] fn month_week_reverse() { let e = Expr::Var(Var::MonthWeekReverse); - expr_ymd(&e, (2021, 12, 1), 5); - expr_ymd(&e, (2021, 12, 2), 5); - expr_ymd(&e, (2021, 12, 3), 5); - expr_ymd(&e, (2021, 12, 4), 4); - expr_ymd(&e, (2021, 12, 5), 4); - expr_ymd(&e, (2021, 12, 6), 4); - expr_ymd(&e, (2021, 12, 7), 4); - expr_ymd(&e, (2021, 12, 8), 4); - expr_ymd(&e, (2021, 12, 9), 4); - expr_ymd(&e, (2021, 12, 10), 4); - expr_ymd(&e, (2021, 12, 11), 3); - expr_ymd(&e, (2021, 12, 12), 3); - expr_ymd(&e, (2021, 12, 13), 3); - expr_ymd(&e, (2021, 12, 14), 3); - expr_ymd(&e, (2021, 12, 15), 3); - expr_ymd(&e, (2021, 12, 16), 3); - expr_ymd(&e, (2021, 12, 17), 3); - expr_ymd(&e, (2021, 12, 18), 2); - expr_ymd(&e, (2021, 12, 19), 2); - expr_ymd(&e, (2021, 12, 20), 2); - expr_ymd(&e, (2021, 12, 21), 2); - expr_ymd(&e, (2021, 12, 22), 2); - expr_ymd(&e, (2021, 12, 23), 2); - expr_ymd(&e, (2021, 12, 24), 2); - expr_ymd(&e, (2021, 12, 25), 1); - expr_ymd(&e, (2021, 12, 26), 1); - expr_ymd(&e, (2021, 12, 27), 1); - expr_ymd(&e, (2021, 12, 28), 1); - expr_ymd(&e, (2021, 12, 29), 1); - expr_ymd(&e, (2021, 12, 30), 1); - expr_ymd(&e, (2021, 12, 31), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 1), 5); + expr(&e, NaiveDate::from_ymd(2021, 12, 2), 5); + expr(&e, NaiveDate::from_ymd(2021, 12, 3), 5); + expr(&e, NaiveDate::from_ymd(2021, 12, 4), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 5), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 6), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 7), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 8), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 9), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 10), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 11), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 12), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 13), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 14), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 15), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 16), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 17), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 18), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 19), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 20), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 21), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 22), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 23), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 24), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 25), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 26), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 27), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 28), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 29), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 30), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 31), 1); } #[test] @@ -645,78 +630,78 @@ mod tests { let e = Expr::Var(Var::Day); for d in 1..=31 { - expr_ymd(&e, (2020, 1, d), d.into()); - expr_ymd(&e, (2020, 3, d), d.into()); - expr_ymd(&e, (2020, 5, d), d.into()); - expr_ymd(&e, (2020, 7, d), d.into()); - expr_ymd(&e, (2020, 8, d), d.into()); - expr_ymd(&e, (2020, 10, d), d.into()); - expr_ymd(&e, (2020, 12, d), d.into()); + expr(&e, NaiveDate::from_ymd(2020, 1, d), d.into()); + expr(&e, NaiveDate::from_ymd(2020, 3, d), d.into()); + expr(&e, NaiveDate::from_ymd(2020, 5, d), d.into()); + expr(&e, NaiveDate::from_ymd(2020, 7, d), d.into()); + expr(&e, NaiveDate::from_ymd(2020, 8, d), d.into()); + expr(&e, NaiveDate::from_ymd(2020, 10, d), d.into()); + expr(&e, NaiveDate::from_ymd(2020, 12, d), d.into()); - expr_ymd(&e, (2021, 1, d), d.into()); - expr_ymd(&e, (2021, 3, d), d.into()); - expr_ymd(&e, (2021, 5, d), d.into()); - expr_ymd(&e, (2021, 7, d), d.into()); - expr_ymd(&e, (2021, 8, d), d.into()); - expr_ymd(&e, (2021, 10, d), d.into()); - expr_ymd(&e, (2021, 12, d), d.into()); + expr(&e, NaiveDate::from_ymd(2021, 1, d), d.into()); + expr(&e, NaiveDate::from_ymd(2021, 3, d), d.into()); + expr(&e, NaiveDate::from_ymd(2021, 5, d), d.into()); + expr(&e, NaiveDate::from_ymd(2021, 7, d), d.into()); + expr(&e, NaiveDate::from_ymd(2021, 8, d), d.into()); + expr(&e, NaiveDate::from_ymd(2021, 10, d), d.into()); + expr(&e, NaiveDate::from_ymd(2021, 12, d), d.into()); } for d in 1..=30 { - expr_ymd(&e, (2020, 4, d), d.into()); - expr_ymd(&e, (2020, 6, d), d.into()); - expr_ymd(&e, (2020, 9, d), d.into()); - expr_ymd(&e, (2020, 11, d), d.into()); + expr(&e, NaiveDate::from_ymd(2020, 4, d), d.into()); + expr(&e, NaiveDate::from_ymd(2020, 6, d), d.into()); + expr(&e, NaiveDate::from_ymd(2020, 9, d), d.into()); + expr(&e, NaiveDate::from_ymd(2020, 11, d), d.into()); - expr_ymd(&e, (2021, 4, d), d.into()); - expr_ymd(&e, (2021, 6, d), d.into()); - expr_ymd(&e, (2021, 9, d), d.into()); - expr_ymd(&e, (2021, 11, d), d.into()); + expr(&e, NaiveDate::from_ymd(2021, 4, d), d.into()); + expr(&e, NaiveDate::from_ymd(2021, 6, d), d.into()); + expr(&e, NaiveDate::from_ymd(2021, 9, d), d.into()); + expr(&e, NaiveDate::from_ymd(2021, 11, d), d.into()); } for d in 1..=28 { - expr_ymd(&e, (2020, 2, d), d.into()); - expr_ymd(&e, (2021, 2, d), d.into()); + expr(&e, NaiveDate::from_ymd(2020, 2, d), d.into()); + expr(&e, NaiveDate::from_ymd(2021, 2, d), d.into()); } - expr_ymd(&e, (2020, 2, 29), 29); + expr(&e, NaiveDate::from_ymd(2020, 2, 29), 29); } #[test] fn day_reverse() { let e = Expr::Var(Var::DayReverse); - expr_ymd(&e, (2021, 12, 1), 31); - expr_ymd(&e, (2021, 12, 2), 30); - expr_ymd(&e, (2021, 12, 3), 29); - expr_ymd(&e, (2021, 12, 4), 28); - expr_ymd(&e, (2021, 12, 5), 27); - expr_ymd(&e, (2021, 12, 6), 26); - expr_ymd(&e, (2021, 12, 7), 25); - expr_ymd(&e, (2021, 12, 8), 24); - expr_ymd(&e, (2021, 12, 9), 23); - expr_ymd(&e, (2021, 12, 10), 22); - expr_ymd(&e, (2021, 12, 11), 21); - expr_ymd(&e, (2021, 12, 12), 20); - expr_ymd(&e, (2021, 12, 13), 19); - expr_ymd(&e, (2021, 12, 14), 18); - expr_ymd(&e, (2021, 12, 15), 17); - expr_ymd(&e, (2021, 12, 16), 16); - expr_ymd(&e, (2021, 12, 17), 15); - expr_ymd(&e, (2021, 12, 18), 14); - expr_ymd(&e, (2021, 12, 19), 13); - expr_ymd(&e, (2021, 12, 20), 12); - expr_ymd(&e, (2021, 12, 21), 11); - expr_ymd(&e, (2021, 12, 22), 10); - expr_ymd(&e, (2021, 12, 23), 9); - expr_ymd(&e, (2021, 12, 24), 8); - expr_ymd(&e, (2021, 12, 25), 7); - expr_ymd(&e, (2021, 12, 26), 6); - expr_ymd(&e, (2021, 12, 27), 5); - expr_ymd(&e, (2021, 12, 28), 4); - expr_ymd(&e, (2021, 12, 29), 3); - expr_ymd(&e, (2021, 12, 30), 2); - expr_ymd(&e, (2021, 12, 31), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 1), 31); + expr(&e, NaiveDate::from_ymd(2021, 12, 2), 30); + expr(&e, NaiveDate::from_ymd(2021, 12, 3), 29); + expr(&e, NaiveDate::from_ymd(2021, 12, 4), 28); + expr(&e, NaiveDate::from_ymd(2021, 12, 5), 27); + expr(&e, NaiveDate::from_ymd(2021, 12, 6), 26); + expr(&e, NaiveDate::from_ymd(2021, 12, 7), 25); + expr(&e, NaiveDate::from_ymd(2021, 12, 8), 24); + expr(&e, NaiveDate::from_ymd(2021, 12, 9), 23); + expr(&e, NaiveDate::from_ymd(2021, 12, 10), 22); + expr(&e, NaiveDate::from_ymd(2021, 12, 11), 21); + expr(&e, NaiveDate::from_ymd(2021, 12, 12), 20); + expr(&e, NaiveDate::from_ymd(2021, 12, 13), 19); + expr(&e, NaiveDate::from_ymd(2021, 12, 14), 18); + expr(&e, NaiveDate::from_ymd(2021, 12, 15), 17); + expr(&e, NaiveDate::from_ymd(2021, 12, 16), 16); + expr(&e, NaiveDate::from_ymd(2021, 12, 17), 15); + expr(&e, NaiveDate::from_ymd(2021, 12, 18), 14); + expr(&e, NaiveDate::from_ymd(2021, 12, 19), 13); + expr(&e, NaiveDate::from_ymd(2021, 12, 20), 12); + expr(&e, NaiveDate::from_ymd(2021, 12, 21), 11); + expr(&e, NaiveDate::from_ymd(2021, 12, 22), 10); + expr(&e, NaiveDate::from_ymd(2021, 12, 23), 9); + expr(&e, NaiveDate::from_ymd(2021, 12, 24), 8); + expr(&e, NaiveDate::from_ymd(2021, 12, 25), 7); + expr(&e, NaiveDate::from_ymd(2021, 12, 26), 6); + expr(&e, NaiveDate::from_ymd(2021, 12, 27), 5); + expr(&e, NaiveDate::from_ymd(2021, 12, 28), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 29), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 30), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 31), 1); } #[test] @@ -725,32 +710,32 @@ mod tests { // From https://en.wikipedia.org/wiki/ISO_week_date - expr_ymd(&e, (1977, 1, 1), 1976); - expr_ymd(&e, (1977, 1, 2), 1976); - expr_ymd(&e, (1977, 1, 3), 1977); + expr(&e, NaiveDate::from_ymd(1977, 1, 1), 1976); + expr(&e, NaiveDate::from_ymd(1977, 1, 2), 1976); + expr(&e, NaiveDate::from_ymd(1977, 1, 3), 1977); - expr_ymd(&e, (1977, 12, 31), 1977); - expr_ymd(&e, (1978, 1, 1), 1977); - expr_ymd(&e, (1978, 1, 2), 1978); + expr(&e, NaiveDate::from_ymd(1977, 12, 31), 1977); + expr(&e, NaiveDate::from_ymd(1978, 1, 1), 1977); + expr(&e, NaiveDate::from_ymd(1978, 1, 2), 1978); - expr_ymd(&e, (1978, 12, 31), 1978); - expr_ymd(&e, (1979, 1, 1), 1979); + expr(&e, NaiveDate::from_ymd(1978, 12, 31), 1978); + expr(&e, NaiveDate::from_ymd(1979, 1, 1), 1979); - expr_ymd(&e, (1979, 12, 30), 1979); - expr_ymd(&e, (1979, 12, 31), 1980); - expr_ymd(&e, (1980, 1, 1), 1980); + expr(&e, NaiveDate::from_ymd(1979, 12, 30), 1979); + expr(&e, NaiveDate::from_ymd(1979, 12, 31), 1980); + expr(&e, NaiveDate::from_ymd(1980, 1, 1), 1980); - expr_ymd(&e, (1980, 12, 28), 1980); - expr_ymd(&e, (1980, 12, 29), 1981); - expr_ymd(&e, (1980, 12, 30), 1981); - expr_ymd(&e, (1980, 12, 31), 1981); - expr_ymd(&e, (1981, 1, 1), 1981); + expr(&e, NaiveDate::from_ymd(1980, 12, 28), 1980); + expr(&e, NaiveDate::from_ymd(1980, 12, 29), 1981); + expr(&e, NaiveDate::from_ymd(1980, 12, 30), 1981); + expr(&e, NaiveDate::from_ymd(1980, 12, 31), 1981); + expr(&e, NaiveDate::from_ymd(1981, 1, 1), 1981); - expr_ymd(&e, (1981, 12, 31), 1981); - expr_ymd(&e, (1982, 1, 1), 1981); - expr_ymd(&e, (1982, 1, 2), 1981); - expr_ymd(&e, (1982, 1, 3), 1981); - expr_ymd(&e, (1982, 1, 4), 1982); + expr(&e, NaiveDate::from_ymd(1981, 12, 31), 1981); + expr(&e, NaiveDate::from_ymd(1982, 1, 1), 1981); + expr(&e, NaiveDate::from_ymd(1982, 1, 2), 1981); + expr(&e, NaiveDate::from_ymd(1982, 1, 3), 1981); + expr(&e, NaiveDate::from_ymd(1982, 1, 4), 1982); } #[test] @@ -758,37 +743,37 @@ mod tests { let e = Expr::Var(Var::IsoYearLength); // August 1st is definitely in the same year in both systems - expr_ymd(&e, (2000, 8, 1), 52 * 7); - expr_ymd(&e, (2001, 8, 1), 52 * 7); - expr_ymd(&e, (2002, 8, 1), 52 * 7); - expr_ymd(&e, (2003, 8, 1), 52 * 7); - expr_ymd(&e, (2004, 8, 1), 52 * 7 + 7); - expr_ymd(&e, (2005, 8, 1), 52 * 7); - expr_ymd(&e, (2006, 8, 1), 52 * 7); - expr_ymd(&e, (2007, 8, 1), 52 * 7); - expr_ymd(&e, (2008, 8, 1), 52 * 7); - expr_ymd(&e, (2009, 8, 1), 52 * 7 + 7); - expr_ymd(&e, (2010, 8, 1), 52 * 7); - expr_ymd(&e, (2011, 8, 1), 52 * 7); - expr_ymd(&e, (2012, 8, 1), 52 * 7); - expr_ymd(&e, (2013, 8, 1), 52 * 7); - expr_ymd(&e, (2014, 8, 1), 52 * 7); - expr_ymd(&e, (2015, 8, 1), 52 * 7 + 7); - expr_ymd(&e, (2016, 8, 1), 52 * 7); - expr_ymd(&e, (2017, 8, 1), 52 * 7); - expr_ymd(&e, (2018, 8, 1), 52 * 7); - expr_ymd(&e, (2019, 8, 1), 52 * 7); - expr_ymd(&e, (2020, 8, 1), 52 * 7 + 7); - expr_ymd(&e, (2021, 8, 1), 52 * 7); - expr_ymd(&e, (2022, 8, 1), 52 * 7); - expr_ymd(&e, (2023, 8, 1), 52 * 7); - expr_ymd(&e, (2024, 8, 1), 52 * 7); - expr_ymd(&e, (2025, 8, 1), 52 * 7); - expr_ymd(&e, (2026, 8, 1), 52 * 7 + 7); - expr_ymd(&e, (2027, 8, 1), 52 * 7); - expr_ymd(&e, (2028, 8, 1), 52 * 7); - expr_ymd(&e, (2029, 8, 1), 52 * 7); - expr_ymd(&e, (2030, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2000, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2001, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2002, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2003, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2004, 8, 1), 52 * 7 + 7); + expr(&e, NaiveDate::from_ymd(2005, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2006, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2007, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2008, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2009, 8, 1), 52 * 7 + 7); + expr(&e, NaiveDate::from_ymd(2010, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2011, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2012, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2013, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2014, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2015, 8, 1), 52 * 7 + 7); + expr(&e, NaiveDate::from_ymd(2016, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2017, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2018, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2019, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2020, 8, 1), 52 * 7 + 7); + expr(&e, NaiveDate::from_ymd(2021, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2022, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2023, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2024, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2025, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2026, 8, 1), 52 * 7 + 7); + expr(&e, NaiveDate::from_ymd(2027, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2028, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2029, 8, 1), 52 * 7); + expr(&e, NaiveDate::from_ymd(2030, 8, 1), 52 * 7); } #[test] @@ -797,52 +782,52 @@ mod tests { // From https://en.wikipedia.org/wiki/ISO_week_date - expr_ymd(&e, (1977, 1, 1), 53); - expr_ymd(&e, (1977, 1, 2), 53); - expr_ymd(&e, (1977, 1, 3), 1); + expr(&e, NaiveDate::from_ymd(1977, 1, 1), 53); + expr(&e, NaiveDate::from_ymd(1977, 1, 2), 53); + expr(&e, NaiveDate::from_ymd(1977, 1, 3), 1); - expr_ymd(&e, (1977, 12, 31), 52); - expr_ymd(&e, (1978, 1, 1), 52); - expr_ymd(&e, (1978, 1, 2), 1); + expr(&e, NaiveDate::from_ymd(1977, 12, 31), 52); + expr(&e, NaiveDate::from_ymd(1978, 1, 1), 52); + expr(&e, NaiveDate::from_ymd(1978, 1, 2), 1); - expr_ymd(&e, (1978, 12, 31), 52); - expr_ymd(&e, (1979, 1, 1), 1); + expr(&e, NaiveDate::from_ymd(1978, 12, 31), 52); + expr(&e, NaiveDate::from_ymd(1979, 1, 1), 1); - expr_ymd(&e, (1979, 12, 30), 52); - expr_ymd(&e, (1979, 12, 31), 1); - expr_ymd(&e, (1980, 1, 1), 1); + expr(&e, NaiveDate::from_ymd(1979, 12, 30), 52); + expr(&e, NaiveDate::from_ymd(1979, 12, 31), 1); + expr(&e, NaiveDate::from_ymd(1980, 1, 1), 1); - expr_ymd(&e, (1980, 12, 28), 52); - expr_ymd(&e, (1980, 12, 29), 1); - expr_ymd(&e, (1980, 12, 30), 1); - expr_ymd(&e, (1980, 12, 31), 1); - expr_ymd(&e, (1981, 1, 1), 1); + expr(&e, NaiveDate::from_ymd(1980, 12, 28), 52); + expr(&e, NaiveDate::from_ymd(1980, 12, 29), 1); + expr(&e, NaiveDate::from_ymd(1980, 12, 30), 1); + expr(&e, NaiveDate::from_ymd(1980, 12, 31), 1); + expr(&e, NaiveDate::from_ymd(1981, 1, 1), 1); - expr_ymd(&e, (1981, 12, 31), 53); - expr_ymd(&e, (1982, 1, 1), 53); - expr_ymd(&e, (1982, 1, 2), 53); - expr_ymd(&e, (1982, 1, 3), 53); - expr_ymd(&e, (1982, 1, 4), 1); + expr(&e, NaiveDate::from_ymd(1981, 12, 31), 53); + expr(&e, NaiveDate::from_ymd(1982, 1, 1), 53); + expr(&e, NaiveDate::from_ymd(1982, 1, 2), 53); + expr(&e, NaiveDate::from_ymd(1982, 1, 3), 53); + expr(&e, NaiveDate::from_ymd(1982, 1, 4), 1); } #[test] fn weekday() { let e = Expr::Var(Var::Weekday); - expr_ymd(&e, (2021, 12, 18), 6); - expr_ymd(&e, (2021, 12, 19), 7); - expr_ymd(&e, (2021, 12, 20), 1); - expr_ymd(&e, (2021, 12, 21), 2); - expr_ymd(&e, (2021, 12, 22), 3); - expr_ymd(&e, (2021, 12, 23), 4); - expr_ymd(&e, (2021, 12, 24), 5); - expr_ymd(&e, (2021, 12, 25), 6); - expr_ymd(&e, (2021, 12, 26), 7); - expr_ymd(&e, (2021, 12, 27), 1); - expr_ymd(&e, (2021, 12, 28), 2); - expr_ymd(&e, (2021, 12, 29), 3); - expr_ymd(&e, (2021, 12, 30), 4); - expr_ymd(&e, (2021, 12, 31), 5); + expr(&e, NaiveDate::from_ymd(2021, 12, 18), 6); + expr(&e, NaiveDate::from_ymd(2021, 12, 19), 7); + expr(&e, NaiveDate::from_ymd(2021, 12, 20), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 21), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 22), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 23), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 24), 5); + expr(&e, NaiveDate::from_ymd(2021, 12, 25), 6); + expr(&e, NaiveDate::from_ymd(2021, 12, 26), 7); + expr(&e, NaiveDate::from_ymd(2021, 12, 27), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 28), 2); + expr(&e, NaiveDate::from_ymd(2021, 12, 29), 3); + expr(&e, NaiveDate::from_ymd(2021, 12, 30), 4); + expr(&e, NaiveDate::from_ymd(2021, 12, 31), 5); } #[test] @@ -902,10 +887,10 @@ mod tests { ]; for (y, m, d) in dates { - expr_ymd( + expr( &e, - (y, 1, 1), - NaiveDate::from_ymd_opt(y, m, d).unwrap().ordinal().into(), + NaiveDate::from_ymd(y, 1, 1), + NaiveDate::from_ymd(y, m, d).ordinal().into(), ); } } @@ -914,77 +899,77 @@ mod tests { fn is_weekday() { let e = Expr::Var(Var::IsWeekday); - expr_ymd(&e, (2021, 12, 18), 0); - expr_ymd(&e, (2021, 12, 19), 0); - expr_ymd(&e, (2021, 12, 20), 1); - expr_ymd(&e, (2021, 12, 21), 1); - expr_ymd(&e, (2021, 12, 22), 1); - expr_ymd(&e, (2021, 12, 23), 1); - expr_ymd(&e, (2021, 12, 24), 1); - expr_ymd(&e, (2021, 12, 25), 0); - expr_ymd(&e, (2021, 12, 26), 0); - expr_ymd(&e, (2021, 12, 27), 1); - expr_ymd(&e, (2021, 12, 28), 1); - expr_ymd(&e, (2021, 12, 29), 1); - expr_ymd(&e, (2021, 12, 30), 1); - expr_ymd(&e, (2021, 12, 31), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 18), 0); + expr(&e, NaiveDate::from_ymd(2021, 12, 19), 0); + expr(&e, NaiveDate::from_ymd(2021, 12, 20), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 21), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 22), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 23), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 24), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 25), 0); + expr(&e, NaiveDate::from_ymd(2021, 12, 26), 0); + expr(&e, NaiveDate::from_ymd(2021, 12, 27), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 28), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 29), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 30), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 31), 1); } #[test] fn is_weekend() { let e = Expr::Var(Var::IsWeekend); - expr_ymd(&e, (2021, 12, 18), 1); - expr_ymd(&e, (2021, 12, 19), 1); - expr_ymd(&e, (2021, 12, 20), 0); - expr_ymd(&e, (2021, 12, 21), 0); - expr_ymd(&e, (2021, 12, 22), 0); - expr_ymd(&e, (2021, 12, 23), 0); - expr_ymd(&e, (2021, 12, 24), 0); - expr_ymd(&e, (2021, 12, 25), 1); - expr_ymd(&e, (2021, 12, 26), 1); - expr_ymd(&e, (2021, 12, 27), 0); - expr_ymd(&e, (2021, 12, 28), 0); - expr_ymd(&e, (2021, 12, 29), 0); - expr_ymd(&e, (2021, 12, 30), 0); - expr_ymd(&e, (2021, 12, 31), 0); + expr(&e, NaiveDate::from_ymd(2021, 12, 18), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 19), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 20), 0); + expr(&e, NaiveDate::from_ymd(2021, 12, 21), 0); + expr(&e, NaiveDate::from_ymd(2021, 12, 22), 0); + expr(&e, NaiveDate::from_ymd(2021, 12, 23), 0); + expr(&e, NaiveDate::from_ymd(2021, 12, 24), 0); + expr(&e, NaiveDate::from_ymd(2021, 12, 25), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 26), 1); + expr(&e, NaiveDate::from_ymd(2021, 12, 27), 0); + expr(&e, NaiveDate::from_ymd(2021, 12, 28), 0); + expr(&e, NaiveDate::from_ymd(2021, 12, 29), 0); + expr(&e, NaiveDate::from_ymd(2021, 12, 30), 0); + expr(&e, NaiveDate::from_ymd(2021, 12, 31), 0); } #[test] fn is_leap_year() { let e = Expr::Var(Var::IsLeapYear); - expr_ymd(&e, (2000, 1, 1), 1); - expr_ymd(&e, (2001, 1, 1), 0); - expr_ymd(&e, (2002, 1, 1), 0); - expr_ymd(&e, (2003, 1, 1), 0); - expr_ymd(&e, (2004, 1, 1), 1); - expr_ymd(&e, (2005, 1, 1), 0); - expr_ymd(&e, (2006, 1, 1), 0); - expr_ymd(&e, (2007, 1, 1), 0); - expr_ymd(&e, (2008, 1, 1), 1); - expr_ymd(&e, (2009, 1, 1), 0); - expr_ymd(&e, (2010, 1, 1), 0); - expr_ymd(&e, (2011, 1, 1), 0); - expr_ymd(&e, (2012, 1, 1), 1); - expr_ymd(&e, (2013, 1, 1), 0); - expr_ymd(&e, (2014, 1, 1), 0); - expr_ymd(&e, (2015, 1, 1), 0); - expr_ymd(&e, (2016, 1, 1), 1); - expr_ymd(&e, (2017, 1, 1), 0); - expr_ymd(&e, (2018, 1, 1), 0); - expr_ymd(&e, (2019, 1, 1), 0); - expr_ymd(&e, (2020, 1, 1), 1); - expr_ymd(&e, (2021, 1, 1), 0); - expr_ymd(&e, (2022, 1, 1), 0); - expr_ymd(&e, (2023, 1, 1), 0); - expr_ymd(&e, (2024, 1, 1), 1); - expr_ymd(&e, (2025, 1, 1), 0); - expr_ymd(&e, (2026, 1, 1), 0); - expr_ymd(&e, (2027, 1, 1), 0); - expr_ymd(&e, (2028, 1, 1), 1); - expr_ymd(&e, (2029, 1, 1), 0); - expr_ymd(&e, (2030, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2000, 1, 1), 1); + expr(&e, NaiveDate::from_ymd(2001, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2002, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2003, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2004, 1, 1), 1); + expr(&e, NaiveDate::from_ymd(2005, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2006, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2007, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2008, 1, 1), 1); + expr(&e, NaiveDate::from_ymd(2009, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2010, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2011, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2012, 1, 1), 1); + expr(&e, NaiveDate::from_ymd(2013, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2014, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2015, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2016, 1, 1), 1); + expr(&e, NaiveDate::from_ymd(2017, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2018, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2019, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2020, 1, 1), 1); + expr(&e, NaiveDate::from_ymd(2021, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2022, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2023, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2024, 1, 1), 1); + expr(&e, NaiveDate::from_ymd(2025, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2026, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2027, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2028, 1, 1), 1); + expr(&e, NaiveDate::from_ymd(2029, 1, 1), 0); + expr(&e, NaiveDate::from_ymd(2030, 1, 1), 0); } #[test] @@ -992,36 +977,36 @@ mod tests { let e = Expr::Var(Var::IsIsoLeapYear); // August 1st is definitely in the same year in both systems - expr_ymd(&e, (2000, 8, 1), 0); - expr_ymd(&e, (2001, 8, 1), 0); - expr_ymd(&e, (2002, 8, 1), 0); - expr_ymd(&e, (2003, 8, 1), 0); - expr_ymd(&e, (2004, 8, 1), 1); - expr_ymd(&e, (2005, 8, 1), 0); - expr_ymd(&e, (2006, 8, 1), 0); - expr_ymd(&e, (2007, 8, 1), 0); - expr_ymd(&e, (2008, 8, 1), 0); - expr_ymd(&e, (2009, 8, 1), 1); - expr_ymd(&e, (2010, 8, 1), 0); - expr_ymd(&e, (2011, 8, 1), 0); - expr_ymd(&e, (2012, 8, 1), 0); - expr_ymd(&e, (2013, 8, 1), 0); - expr_ymd(&e, (2014, 8, 1), 0); - expr_ymd(&e, (2015, 8, 1), 1); - expr_ymd(&e, (2016, 8, 1), 0); - expr_ymd(&e, (2017, 8, 1), 0); - expr_ymd(&e, (2018, 8, 1), 0); - expr_ymd(&e, (2019, 8, 1), 0); - expr_ymd(&e, (2020, 8, 1), 1); - expr_ymd(&e, (2021, 8, 1), 0); - expr_ymd(&e, (2022, 8, 1), 0); - expr_ymd(&e, (2023, 8, 1), 0); - expr_ymd(&e, (2024, 8, 1), 0); - expr_ymd(&e, (2025, 8, 1), 0); - expr_ymd(&e, (2026, 8, 1), 1); - expr_ymd(&e, (2027, 8, 1), 0); - expr_ymd(&e, (2028, 8, 1), 0); - expr_ymd(&e, (2029, 8, 1), 0); - expr_ymd(&e, (2030, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2000, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2001, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2002, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2003, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2004, 8, 1), 1); + expr(&e, NaiveDate::from_ymd(2005, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2006, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2007, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2008, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2009, 8, 1), 1); + expr(&e, NaiveDate::from_ymd(2010, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2011, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2012, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2013, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2014, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2015, 8, 1), 1); + expr(&e, NaiveDate::from_ymd(2016, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2017, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2018, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2019, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2020, 8, 1), 1); + expr(&e, NaiveDate::from_ymd(2021, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2022, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2023, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2024, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2025, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2026, 8, 1), 1); + expr(&e, NaiveDate::from_ymd(2027, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2028, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2029, 8, 1), 0); + expr(&e, NaiveDate::from_ymd(2030, 8, 1), 0); } } diff --git a/src/eval/date.rs b/src/eval/date.rs index 54eb2e1..61b2908 100644 --- a/src/eval/date.rs +++ b/src/eval/date.rs @@ -103,24 +103,11 @@ impl Dates { } pub fn move_by(&self, delta: Duration) -> Self { - let mut result = *self; - - // Modify dates - result.root += delta; - result.other += delta; - - // Modify times if necessary (may further modify dates) - const MINUTES_PER_DAY: i64 = 24 * 60; - let minutes = delta.num_minutes() % MINUTES_PER_DAY; // May be negative - if let Some(times) = self.times { - let (root_days, root) = times.root.add_minutes(minutes); - let (other_days, other) = times.other.add_minutes(minutes); - result.root += Duration::days(root_days); - result.other += Duration::days(other_days); - result.times = Some(Times { root, other }); + Self { + root: self.root + delta, + other: self.other + delta, + times: self.times, } - - result } } @@ -149,16 +136,32 @@ impl From for Dates { impl From for DoneDate { fn from(dates: Dates) -> Self { - let (root, other) = dates.dates(); - match dates.times() { - Some((root_time, other_time)) => Self::DateTimeToDateTime { - root, - root_time, - other, - other_time, - }, - None => Self::DateToDate { root, other }, + if dates.root == dates.other { + match dates.times { + Some(times) if times.root == times.other => Self::DateTime { + root: dates.root, + root_time: times.root, + }, + Some(times) => Self::DateTimeToTime { + root: dates.root, + root_time: times.root, + other_time: times.other, + }, + None => Self::Date { root: dates.root }, + } + } else { + match dates.times { + Some(times) => Self::DateTimeToDateTime { + root: dates.root, + root_time: times.root, + other: dates.other, + other_time: times.other, + }, + None => Self::DateToDate { + root: dates.root, + other: dates.other, + }, + } } - .simplified() } } diff --git a/src/eval/delta.rs b/src/eval/delta.rs index b42d8a7..ecde59b 100644 --- a/src/eval/delta.rs +++ b/src/eval/delta.rs @@ -5,7 +5,7 @@ use chrono::{Datelike, Duration, NaiveDate}; use crate::files::commands; use crate::files::primitives::{Span, Spanned, Time, Weekday}; -use super::{util, Error}; +use super::{util, Error, Result}; /// Like [`commands::DeltaStep`] but includes a new constructor, /// [`DeltaStep::Time`]. @@ -43,84 +43,84 @@ impl DeltaStep { /// A lower bound on days fn lower_bound(&self) -> i32 { match self { - Self::Year(n) => { + DeltaStep::Year(n) => { if *n < 0 { *n * 366 } else { *n * 365 } } - Self::Month(n) | Self::MonthReverse(n) => { + DeltaStep::Month(n) | DeltaStep::MonthReverse(n) => { if *n < 0 { *n * 31 } else { *n * 28 } } - Self::Day(n) => *n, - Self::Week(n) => *n * 7, - Self::Hour(n) => { + DeltaStep::Day(n) => *n, + DeltaStep::Week(n) => *n * 7, + DeltaStep::Hour(n) => { if *n < 0 { *n / 24 + (*n % 24).signum() } else { *n / 24 } } - Self::Minute(n) => { + DeltaStep::Minute(n) => { if *n < 0 { *n / (24 * 60) + (*n % (24 * 60)).signum() } else { *n / (24 * 60) } } - Self::Weekday(n, _) => match n.cmp(&0) { + DeltaStep::Weekday(n, _) => match n.cmp(&0) { Ordering::Less => *n * 7 - 1, Ordering::Equal => 0, Ordering::Greater => *n * 7 - 7, }, - Self::Time(_) => 0, + DeltaStep::Time(_) => 0, } } /// An upper bound on days fn upper_bound(&self) -> i32 { match self { - Self::Year(n) => { + DeltaStep::Year(n) => { if *n > 0 { *n * 366 } else { *n * 365 } } - Self::Month(n) | Self::MonthReverse(n) => { + DeltaStep::Month(n) | DeltaStep::MonthReverse(n) => { if *n > 0 { *n * 31 } else { *n * 28 } } - Self::Day(n) => *n, - Self::Week(n) => *n * 7, - Self::Hour(n) => { + DeltaStep::Day(n) => *n, + DeltaStep::Week(n) => *n * 7, + DeltaStep::Hour(n) => { if *n > 0 { *n / 24 + (*n % 24).signum() } else { *n / 24 } } - Self::Minute(n) => { + DeltaStep::Minute(n) => { if *n > 0 { *n / (24 * 60) + (*n % (24 * 60)).signum() } else { *n / (24 * 60) } } - Self::Weekday(n, _) => match n.cmp(&0) { + DeltaStep::Weekday(n, _) => match n.cmp(&0) { Ordering::Less => *n * 7 - 7, Ordering::Equal => 0, Ordering::Greater => *n * 7 - 1, }, - Self::Time(_) => 1, + DeltaStep::Time(_) => 1, } } } @@ -142,16 +142,16 @@ impl From<&commands::Delta> for Delta { } } -struct DeltaEval { - index: I, +struct DeltaEval { + index: usize, start: NaiveDate, start_time: Option