Compare commits

...

245 Commits

Author SHA1 Message Date
Bel LaPointe 060a8dfb3b new tests 2025-12-02 17:47:12 -07:00
Bel LaPointe 3d7ebcf9bc drop one debug 2025-12-02 17:42:36 -07:00
Bel LaPointe 51f10b7944 shush warn 2025-12-02 17:40:25 -07:00
Bel LaPointe 9ed6b48806 shhhh warning 2025-12-02 17:36:29 -07:00
Bel LaPointe a867809cb8 use to shush warn 2025-12-02 17:35:57 -07:00
Bel LaPointe 053071f4be hrm an enqueu eoperator is hard because a cron fires many times but looks removed but isnt and hrm 2025-12-02 17:34:04 -07:00
Bel LaPointe eccaa06d98 can add, add past, add future 2025-12-02 17:30:29 -07:00
Bel LaPointe ee9377d6da reconcile on add too 2025-12-02 17:21:51 -07:00
Bel LaPointe 7da6aa8ae9 still unsure about triggered vs snapshot time... 2025-12-02 17:17:31 -07:00
Bel LaPointe a5553d75f4 add w schedule via ts after now 2025-12-02 17:03:06 -07:00
Bel LaPointe fe8a55b4c1 WIP test add-scheduled, snapshot, scheduled-due 2025-12-01 18:26:41 -07:00
Bel LaPointe 0a7e6873a3 can -s 2025-01-02 2025-12-01 17:39:17 -07:00
Bel LaPointe 1a61701c53 panik todo 2025-12-01 17:35:18 -07:00
Bel LaPointe f5b47c4e74 reverse for last snapshot 2025-12-01 17:32:50 -07:00
Bel LaPointe 8728867cc3 testdata to new NOT jsonpatch 2025-12-01 17:24:41 -07:00
Bel LaPointe bb64b87752 all tests pass woo 2025-12-01 17:23:23 -07:00
Bel LaPointe 4ad0b7d2ff no double persist snapshot 2025-12-01 17:05:07 -07:00
Bel LaPointe 700b2a22cc persist delta of last snapshot and stage 2025-12-01 17:03:56 -07:00
Bel LaPointe 97caaebc09 drop nonfuture 2025-12-01 16:51:36 -07:00
Bel LaPointe 72eb29d766 step 1 2025-11-20 14:29:01 -07:00
Bel LaPointe 56d0628ece gr 2025-11-13 16:04:26 -07:00
Bel LaPointe 4884d551e2 oh almost there 2025-11-13 09:45:40 -07:00
Bel LaPointe b37e61a223 closer 2025-11-12 15:17:21 -07:00
Bel LaPointe c06091d576 test schedule can pass 2025-11-12 14:44:35 -07:00
Bel LaPointe fa7b537106 schedule herlp Due 2025-11-12 14:10:02 -07:00
Bel LaPointe 4ca5ed4d7c can find due after ts and Task w schedule 2025-11-12 14:08:32 -07:00
Bel LaPointe 89185f5016 drop debug 2025-11-12 14:00:08 -07:00
Bel LaPointe b57618889e parse not crons 2025-11-12 13:59:46 -07:00
Bel LaPointe c2f1255406 lint 2025-11-12 13:06:48 -07:00
Bel LaPointe 9e63b5b1dd todo: SCHEDULE 2025-11-12 13:02:05 -07:00
Bel LaPointe 1a9f052396 new tests with just add-remove and persist_stage collapses finding missed persists 2025-11-12 13:01:35 -07:00
Bel LaPointe c1a5934215 todo 2025-11-11 20:17:48 -07:00
Bel LaPointe 7c79b20797 solution: old logs < stage mod time < logs after mod time 2025-11-11 20:02:26 -07:00
Bel LaPointe a13fa68599 hmmm cannot enqueue adds because snapshot is source of truth but then scratch adding would make noise for laptop... 2025-11-11 19:53:27 -07:00
Bel LaPointe b0b962277a a todo 2025-11-11 19:51:11 -07:00
Bel LaPointe 65bc42c246 vim 2025-11-11 19:49:13 -07:00
Bel LaPointe bef9ef1a2a ooo tests pass 2025-11-11 19:34:00 -07:00
Bel LaPointe 5bfd9e52c3 some test edit 2025-11-11 19:24:21 -07:00
Bel LaPointe 2076ebd6f4 some test edit 2025-11-11 19:22:19 -07:00
Bel LaPointe dc70c3ca7a gettin closer 2025-11-11 19:13:34 -07:00
Bel LaPointe be632d3da3 testing reveals the stage as source of truth IS angry 2025-11-11 17:31:44 -07:00
Bel LaPointe 90de4a0cfd tests pass 2025-11-11 17:29:31 -07:00
Bel LaPointe 3c64bb27de some tests 2025-11-11 17:18:03 -07:00
Bel LaPointe bcbcd6fbb7 test helpers 2025-11-11 17:09:42 -07:00
Bel LaPointe b5b794817c more test 2025-11-11 16:40:38 -07:00
Bel LaPointe c34ad15ba1 todo 2025-11-11 11:46:00 -07:00
Bel LaPointe 60ddbc673c wip 2025-11-11 11:41:07 -07:00
Bel LaPointe 7d36455bad 1 test 2025-11-11 11:29:04 -07:00
Bel LaPointe 5d2441ce0c o no more panics 2025-11-11 11:17:45 -07:00
Bel LaPointe 30d3875e09 ooo can add 2025-11-11 11:17:28 -07:00
Bel LaPointe 52fdf79d65 after add, stage 2025-11-11 11:16:58 -07:00
Bel LaPointe 06d1144c85 stage 2025-11-11 11:16:10 -07:00
Bel LaPointe e3f0d816de oooo 2025-11-11 11:05:50 -07:00
Bel LaPointe bcd479ceae narrow delta 2025-11-11 11:03:50 -07:00
Bel LaPointe 1dc9b6149c impl File.stage_persisted 2025-11-11 11:03:05 -07:00
Bel LaPointe 9242d41a0f wheeee 2025-11-11 10:39:50 -07:00
Bel LaPointe a899241c28 oooo 2025-11-11 10:23:13 -07:00
Bel LaPointe 07e2750db5 rename 2025-11-11 10:21:58 -07:00
Bel LaPointe e48338ff0e ooo 2025-11-11 10:21:09 -07:00
Bel LaPointe c174181435 can read deltas 2025-11-11 10:12:11 -07:00
bel 001712ed1c TODO snapshot from ALL log files, reconcile delta of staging to HOSTNAME file by TS 2025-11-11 00:15:46 -07:00
bel c9ee9d9a27 like this 2025-11-10 23:48:48 -07:00
bel 4067bd75fe can json diff 2025-11-10 23:20:01 -07:00
Bel LaPointe 56d5f59daf wheeee 2025-11-10 21:32:18 -07:00
bel 7a23fba2ae pttodoest 2025-11-09 21:21:35 -07:00
bel 263482f607 Revert "original.yaml has just todo, merge against .original.yaml so mobile edits still get a history"
This reverts commit 242ad3e127.
2025-07-13 08:51:21 -06:00
bel 871e75f8a8 schedule cron in scheduled 2025-07-13 08:38:42 -06:00
Bel LaPointe 242ad3e127 original.yaml has just todo, merge against .original.yaml so mobile edits still get a history 2025-07-03 17:44:24 -06:00
Bel LaPointe 29bc6a06b4 gr 2025-01-14 21:19:39 -07:00
Bel LaPointe 44488aec2d better err 2025-01-14 21:12:11 -07:00
bel 45b2060083 add -dedupe to move dupe TODO and SCHEDULED to DONE 2024-12-31 10:21:36 -07:00
bel 1413d02764 accept -archive to push -f Done into -archive Done 2024-12-31 10:09:55 -07:00
bel 284b57b9c4 accept -g to merge files 2024-12-31 10:03:37 -07:00
bel 9539d54145 oopoop 2024-12-20 20:39:55 -07:00
bel c17e61a6ca oop 2024-12-20 20:39:39 -07:00
bel def5422482 Merge branch 'master' of https://gitea.inhome.blapointe.com/gogs/pttodo 2024-12-20 20:38:21 -07:00
bel f72cb35e79 instead of os.rename, io.WriteFile and os.Remove 2024-12-20 20:38:11 -07:00
Bel LaPointe 7344c660ed todos as failing tests 2024-08-10 09:30:06 -06:00
Bel LaPointe 301fb6046f tests yay 2024-08-09 17:22:08 -06:00
Bel LaPointe b067d0fb8e failing test 2024-08-09 17:07:25 -06:00
Bel LaPointe e05509f28f tests are good 2024-08-09 15:48:10 -07:00
Bel LaPointe 33d318edd4 o that swap wasnt too bd 2024-08-08 13:25:38 -07:00
Bel LaPointe a5f5013ef6 ew 2024-08-08 13:17:32 -07:00
Bel LaPointe 52977ff0a7 insert reveal i cannot handle no src db 2024-08-08 13:14:26 -07:00
Bel LaPointe 452b692c6a wipper 2024-08-08 12:58:21 -07:00
Bel LaPointe 5c74ec0c15 todo 2024-08-08 12:53:15 -07:00
Bel LaPointe 602939b25c revert testdata 2024-08-04 13:36:08 -06:00
Bel LaPointe 7bf1d15835 need tests for all the edits--like adding a scheduled, deleting a scheduled should add a _doned 2024-08-04 13:35:48 -06:00
Bel LaPointe e4834d8a11 gr 2024-08-04 13:35:29 -06:00
Bel LaPointe 2425f22b56 grr edit dupes 2024-08-04 11:44:22 -06:00
Bel LaPointe 50ea3258b7 edit implemented maybe 2024-08-04 11:42:09 -06:00
Bel LaPointe 6eba05895f comment 2024-06-14 09:59:49 -06:00
Bel LaPointe 5a45cd1410 comment THE PLAN 2024-06-14 09:54:46 -06:00
Bel LaPointe 7832d09fe8 ok vim runs that was ez 2024-06-14 09:49:22 -06:00
Bel LaPointe aa9e032c31 tempdir ty 2024-06-14 09:27:01 -06:00
Bel LaPointe b949502869 now it gets gross ew 2024-06-14 09:26:26 -06:00
Bel LaPointe 41cab74028 eprint dont err on conflict 2024-06-14 09:24:05 -06:00
Bel LaPointe e389f34bd6 impl save and dry run 2024-06-14 09:16:36 -06:00
Bel LaPointe 3cfaf1f1a0 when adding w schedule then set ts to now 2024-06-14 08:42:58 -06:00
Bel LaPointe b1d1c5d2b2 todo 2024-05-23 16:41:13 -04:00
Bel LaPointe 4d8fd0597b okcool can read and even doesnt wrap so i like that some 2024-05-23 16:36:05 -04:00
Bel LaPointe 8f8202282f parent level DB has multiple tasks that each have a file and file modified timestamp associated 2024-05-23 16:18:06 -04:00
Bel LaPointe e81f221910 gathering info on read 2024-05-23 16:11:56 -04:00
Bel LaPointe 7ad6dad214 gathering info on read 2024-05-23 16:11:35 -04:00
Bel LaPointe c7163268d4 stub 2024-05-23 15:45:01 -04:00
Bel LaPointe e90cf02664 revive test func 2024-05-23 15:37:50 -04:00
Bel LaPointe 7129723442 accept flags but need to accept directories for multifile as buckets 2024-05-22 15:34:04 -04:00
Bel LaPointe 74b02118bc parse go times with tz 2024-05-22 15:21:11 -04:00
Bel LaPointe c7b0fc8dba less usable key 2024-05-22 14:33:28 -04:00
Bel LaPointe 50e9c80473 ok got Tasks has due func 2024-05-22 14:30:05 -04:00
Bel LaPointe c5ed06b76c okcool 2024-05-22 14:14:14 -04:00
Bel LaPointe 039c4dad04 okcool 2024-05-22 14:04:38 -04:00
Bel LaPointe 3c132528e3 ok that was fun 2024-05-15 23:33:38 -04:00
Bel LaPointe 1639286189 so close tho 2024-05-15 23:27:03 -04:00
Bel LaPointe 2a04a030f7 functions good 2024-05-15 23:16:26 -04:00
Bel LaPointe 931edec4a4 wip 2024-05-15 23:15:09 -04:00
Bel LaPointe 4154e1aad8 Task has crud 2024-05-15 23:05:34 -04:00
Bel LaPointe 4f4e7fd907 oooo tests pass 2024-05-15 22:56:40 -04:00
Bel LaPointe a627223e8a oop 2024-05-15 22:54:37 -04:00
Bel LaPointe 00813e39a9 close tho 2024-05-15 22:53:34 -04:00
Bel LaPointe 66f533a210 okeydoke 2024-05-15 22:27:16 -04:00
Bel LaPointe 233967e177 okeydoke 2024-05-15 22:24:21 -04:00
Bel LaPointe 282f653c75 pttodoer 2024-05-15 21:57:05 -04:00
bel 1d958e8a52 later 2024-05-06 22:25:55 -06:00
bel 5a75b3e5f2 o i am too structured huh 2024-05-06 22:10:56 -06:00
bel ad9c32c987 testdata 2024-05-06 21:18:20 -06:00
bel 3ee1dc069e stub rust? 2024-05-06 21:00:31 -06:00
Bel LaPointe 9b482d45b4 bucket inboxes by target for excessive automoving 2023-11-14 18:36:00 -07:00
Bel LaPointe ba81940b0b k 2023-11-09 08:19:27 -07:00
Bel LaPointe 5d74b458d5 again 2023-11-09 08:19:14 -07:00
Bel LaPointe f3ac2c63fe pkg from gogs 2023-11-09 08:18:00 -07:00
Bel LaPointe a3ca9b1917 more manual testing is all k 2023-11-09 07:51:42 -07:00
Bel LaPointe ca84bcbee3 inboxes can only merge to Todo because tempfiles only have Todo 2023-11-09 07:50:53 -07:00
Bel LaPointe 89a8f61fa3 cache 2023-11-07 07:20:14 -07:00
Bel LaPointe d3f34709b3 consistent 2023-11-07 06:11:30 -07:00
Bel LaPointe ea904ea729 if no original file, then edit merges with nil and calls itself an inbox 2023-11-07 06:10:36 -07:00
bel ead31e077b edit doesnt excess delete but keeps to many files like inboxes 2023-11-06 22:19:39 -07:00
bel 36c5228a6c oop 2023-11-06 22:07:38 -07:00
bel 72fee73a00 dont yield dirs 2023-11-06 21:39:57 -07:00
bel 550bace88d manual test an inbox 2023-11-06 21:29:33 -07:00
bel 0329b9690c untested refacotor of edit.go 2023-11-06 21:25:30 -07:00
bel fe8595cc68 comment 2023-11-06 20:58:03 -07:00
bel 3549010ab4 wipper on edit.go 2023-11-06 20:49:08 -07:00
Bel LaPointe caa9f9d6fd progressing 2023-11-06 13:29:56 -07:00
Bel LaPointe 8b63254cf0 progressing 2023-11-06 13:29:21 -07:00
Bel LaPointe 97f2cb197f wip 2023-11-06 13:23:28 -07:00
Bel LaPointe b2a51e65b0 time for edit.go 2023-11-06 13:15:01 -07:00
Bel LaPointe f75dcb3f7a test touched a local file oop 2023-11-06 13:14:20 -07:00
Bel LaPointe d4466598b4 drop test file 2023-11-06 13:13:42 -07:00
Bel LaPointe 3ba3b78afc use default for effectively default 2023-11-06 13:12:09 -07:00
Bel LaPointe f2e3e6f505 rm extra mem 2023-11-06 13:11:40 -07:00
Bel LaPointe 1ce120a647 shuffle code 2023-11-06 13:11:22 -07:00
Bel LaPointe bfdeebb7a2 use pttodo.Todos.Like...() 2023-11-06 13:06:11 -07:00
Bel LaPointe 39345e5e2a use pttodo.NewRootFromFiles in cli 2023-11-06 12:58:46 -07:00
Bel LaPointe bfcec9d1c5 add pttodo.NewRootFromFiles 2023-11-06 12:58:06 -07:00
Bel LaPointe 3e1f58c7b9 test pttodo.NewRootFromFile accepts both []Todo and Root 2023-11-06 12:54:46 -07:00
Bel LaPointe 92e8e14c08 bubble root.yaml to first 2023-11-06 12:44:31 -07:00
Bel LaPointe 84aa7cb319 test config resolving targets 2023-11-06 12:38:11 -07:00
Bel LaPointe d36f9e74b3 bake listing files not cached in config 2023-11-06 12:25:52 -07:00
Bel LaPointe 8dacd8da5d ptr 2023-11-06 12:17:46 -07:00
Bel LaPointe 23ef7f13ee gitignore bin 2023-11-06 12:15:29 -07:00
Bel LaPointe 02dab4b726 move pttodo-cli up from /cmd/ 2023-11-06 12:15:12 -07:00
Bel LaPointe cdd1be46a8 drop unused 2023-11-06 12:14:37 -07:00
Bel LaPointe 760c822323 add pttodo.NewRootFromFile, pttodo.NewTodosFromFile 2023-11-06 12:14:27 -07:00
Bel LaPointe 4e69646e88 drop dumb verifies 2023-11-06 12:14:16 -07:00
Bel LaPointe 9aee322c4e drop old 2023-11-06 12:11:44 -07:00
Bel LaPointe 887875f6d8 add creates a .todo.* file 2023-11-06 12:03:03 -07:00
Bel LaPointe d2f986d8b6 split config.go 2023-11-06 10:37:34 -07:00
Bel LaPointe 58aa05c155 split dump.go from main.go in cli 2023-11-06 10:35:01 -07:00
Bel LaPointe 97aff0f0b8 split cli.go into add.go, edit.go too 2023-11-06 10:33:32 -07:00
Bel LaPointe eb20706d12 drop some args 2023-11-06 10:28:36 -07:00
bel a800227c6f oop 2023-10-15 10:40:13 -06:00
bel 2c7563c1ab default file path if not empty 2023-10-15 10:39:58 -06:00
bel a8a135bb2f fix nil ptr on no previous file 2023-10-15 10:39:01 -06:00
Bel LaPointe c1c625afc0 skip hidden files 2023-08-16 06:54:21 -06:00
Bel LaPointe 0b790e3468 nil ptr 2023-07-16 19:47:11 -06:00
Bel LaPointe 7175d777cb on write conflict during vim, save as filename.uuid..fileExtension 2023-07-02 10:22:04 -06:00
Bel LaPointe debe28dbbc support -add-tags x,y,z 2023-04-24 15:02:13 -06:00
Bel LaPointe c1426566a0 fix 2023-04-10 10:45:16 -06:00
Bel LaPointe 6f79f7da5e gr 2023-04-10 10:44:41 -06:00
Bel LaPointe 284d7c06bd partial go mod 2023-04-10 10:42:11 -06:00
Bel LaPointe be702b1d74 gitignore 2022-09-10 11:25:23 -06:00
Bel LaPointe de62d99340 no prior file exists ok 2022-09-10 11:24:41 -06:00
Bel LaPointe bca9259caa on scheduled task done, in addition to requeue, add to done 2022-09-10 11:13:18 -06:00
Bel LaPointe dccf5c4028 quick add via cli -add, -add-schedule 2022-04-07 12:48:17 -06:00
Bel LaPointe 55e174e3b1 root moves fixed future schedules to shceduled on automove 2022-03-24 14:43:45 -06:00
Bel LaPointe d7dab75f48 revert to clean todo 2022-03-24 09:26:44 -06:00
Bel LaPointe 3426deae4d revert automove for crons 2022-03-24 09:25:34 -06:00
Bel LaPointe 7fe4686c05 always write ts, when a todo with schedule deleted, conditionally go back to schedule for loopers 2022-03-24 09:18:19 -06:00
Bel LaPointe eb57593665 cli on delete todo, write in Done identified by entire struct so dupes wouldnt but thats ok 2022-03-23 09:46:16 -06:00
Bel LaPointe 814ae3ab23 add todo.ID() 2022-03-23 09:42:21 -06:00
Bel LaPointe 939793bd3f ready for find deleted 2022-03-23 08:06:57 -06:00
Bel LaPointe 1d26cf125f if item in todo is scheduled for later, then move it 2022-03-23 08:04:35 -06:00
Bel LaPointe e20ce478d5 new edit ready for shuffles 2022-03-23 07:49:54 -06:00
Bel LaPointe 0bd6347a93 getconfig in pttodo-cli 2022-03-23 07:27:49 -06:00
Bel LaPointe 20770ff5e6 update tests for mst 2022-03-23 07:13:55 -06:00
bel 65178b8bdf wip 2022-02-20 17:11:16 -07:00
Bel LaPointe 4066d4aeb5 change default file path from stdin 2022-01-10 09:44:30 -05:00
Bel LaPointe 7f611e67bc update todo 2022-01-09 10:27:30 -05:00
Bel LaPointe 71c03f3ef5 accept THH on ezdate 2022-01-09 10:24:40 -05:00
Bel LaPointe 4b8d82c2a0 dont log noops 2022-01-09 10:22:23 -05:00
Bel LaPointe 850ff92d98 do not save if no effective changes for edit, dump 2022-01-09 10:18:08 -05:00
Bel LaPointe c2d1381607 add root.equals and todo.equals 2022-01-09 10:17:44 -05:00
Bel LaPointe 33ca7c60e1 root.yaml prio, otherwise sort -f results 2022-01-08 23:44:30 -05:00
Bel LaPointe cde6ea6cb6 if cli -f is a dir, then edit each file+clean each file+keep each as individual file 2022-01-08 23:42:05 -05:00
Bel LaPointe 6d3f423845 parseinlocation for non utc crud 2022-01-06 21:28:44 -05:00
Bel LaPointe fe1bd22987 instead of trashing editing file in cli, write err msg and re-edit 2022-01-04 12:37:35 -05:00
Bel LaPointe 6f9589b100 err msg 2022-01-04 12:33:39 -05:00
Bel LaPointe a0c0cb7053 fix checking hastags and nothas tags 2022-01-04 08:56:19 -05:00
Bel LaPointe 4a22b964db add anti tag search 2022-01-04 07:01:35 -05:00
Bel LaPointe 2caf2ae352 create script to scrape list of lists and scrape aech into a yaml 2022-01-03 16:36:59 -05:00
Bel LaPointe 455a7d52d5 update todo with no more ts in todo section 2022-01-02 20:45:17 -05:00
Bel LaPointe 1a9221f7c7 update todo 2022-01-02 20:44:23 -05:00
Bel LaPointe 6178e6ff93 root does not marshal TSs on todos, always on schedules, dones. Test. 2022-01-02 20:44:04 -05:00
Bel LaPointe fdb24fcc60 only write TS on a todo if writeTS is set 2022-01-02 20:37:40 -05:00
Bel LaPointe 8002b5e75c new todo 2022-01-02 20:32:16 -05:00
Bel LaPointe 3dd8cd1e03 update todo 2022-01-02 20:31:09 -05:00
Bel LaPointe c6ab36806d update todo.yaml 2022-01-02 20:17:32 -05:00
Bel LaPointe bf997c1814 add cli -g to merge in a file 2022-01-02 20:07:52 -05:00
Bel LaPointe 69eb868db6 add root MergeIn 2022-01-01 17:43:20 -05:00
Bel LaPointe 0c80162394 todo 2022-01-01 17:33:59 -05:00
Bel LaPointe c634fdd4d4 consts for what to print, default to just the todos 2022-01-01 17:30:40 -05:00
Bel LaPointe 92d76443bc ezdate for yyyy-mm-dd for schedule 2022-01-01 17:21:38 -05:00
Bel LaPointe a51d5e6960 rm test code 2022-01-01 17:14:03 -05:00
Bel LaPointe 7fc594d5c2 ts shouldnt yield zero ever, yield now if so 2022-01-01 17:13:50 -05:00
Bel LaPointe 2d8cfa6397 fix getting ts for completed tasks 2022-01-01 17:04:21 -05:00
Bel LaPointe f01dc04277 todo 2021-12-31 23:27:21 -05:00
Bel LaPointe 249ee84688 support tag, simple case insensitve search when recursing 2021-12-31 23:20:27 -05:00
Bel LaPointe 4111d1f490 tag saerch on todo 2021-12-31 23:14:31 -05:00
Bel LaPointe 5b6f62983b when writing output file, dont recurse 2021-12-31 23:03:08 -05:00
Bel LaPointe 1056f5d29e update todo 2021-12-31 23:00:16 -05:00
Bel LaPointe 7886723fe3 times are now unix dates over ints 2021-12-31 22:59:13 -05:00
Bel LaPointe ff77af9ed4 remove unused stub 2021-12-31 22:49:51 -05:00
Bel LaPointe 3bd1c6462d fix syntax highlight by using tempfile.yaml over tempfile 2021-12-31 22:46:49 -05:00
Bel LaPointe 3bcabde553 dont try to find vimrc 2021-12-31 22:45:29 -05:00
Bel LaPointe 604cd610a1 use $EDITOR, default to vim, use $HOME/.vimrc if exists 2021-12-31 22:44:33 -05:00
Bel LaPointe 9d35347b0c updated todo because i oofed 2021-12-31 22:37:47 -05:00
Bel LaPointe 1a8c687260 update install for gomo 2021-12-31 22:32:47 -05:00
Bel LaPointe 3246900db0 if optional positional arg is todo/scheduled/done, then resolve first level 2021-12-31 22:32:07 -05:00
Bel LaPointe 79cc171af5 up install script for location 2021-12-31 22:28:12 -05:00
Bel LaPointe a75d898487 set ts to currenttime if should display 2021-12-31 22:27:48 -05:00
Bel LaPointe f63f152b0e cli has dry mode and install script 2021-12-31 22:27:33 -05:00
Bel LaPointe 76a0231511 pttodo-cli default file via env 2021-12-31 22:07:16 -05:00
Bel LaPointe 7f37feea77 pttodo-cli works even if file does not initially exist 2021-12-31 22:05:47 -05:00
Bel LaPointe b84f8b59c9 rename gomod, root project 2021-12-31 21:58:02 -05:00
53 changed files with 5793 additions and 176 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

4
.gitignore vendored
View File

@ -1,5 +1,9 @@
**/*.sw*
**/target/
todo-server-yaml
cmd/cmd
cmd/cli
cmd/pttodo/pttodo
cmd/pttodo-cli/pttodo-cli
cmd/pttodo-cli
pttodoer/target

36
cmd/add.go Normal file
View File

@ -0,0 +1,36 @@
package main
import (
"fmt"
"io/ioutil"
"os"
"github.com/google/uuid"
"gitea.inhome.blapointe.com/gogs/pttodo/pttodo"
"gopkg.in/yaml.v2"
)
func add(config *config) error {
if config.add == "" {
return nil
}
v := pttodo.Todo{
Todo: config.add,
Schedule: pttodo.Schedule(config.addSchedule),
Tags: config.addTags,
}
return _add(config.Targets()[0], v)
}
func _add(filepath string, todo pttodo.Todo) error {
target := fmt.Sprintf("%s.todo.%s", filepath, uuid.New().String())
c, err := yaml.Marshal([]pttodo.Todo{todo})
if err != nil {
return err
} else if err := ioutil.WriteFile(target, c, os.ModePerm); err != nil {
return err
}
return nil
}

106
cmd/config.go Normal file
View File

@ -0,0 +1,106 @@
package main
import (
"flag"
"fmt"
"os"
"path"
"path/filepath"
"sort"
"strings"
)
type config struct {
target string
target2 string
root string
tags []string
search string
edit bool
add string
addSchedule string
addTags string
dryRun bool
archive string
dedupe bool
}
func getConfig() config {
defaultFilepath := os.Getenv("PTTODO_FILE")
if defaultFilepath == "" {
defaultFilepath = "./todo.yaml"
}
var config config
flag.StringVar(&config.target, "f", defaultFilepath, "($PTTODO_FILE) path to yaml file or dir (starting with root then alphabetical for dir)")
flag.StringVar(&config.target2, "g", "", "path to yaml file to merge into root of -f")
flag.StringVar(&config.archive, "archive", "", "path to yaml file to migrate done")
flag.StringVar(&config.root, "root", DUMP_TODO, "path to pretty print ("+fmt.Sprint([]string{DUMP_ALL, DUMP_TODO, DUMP_SCHEDULED, DUMP_DONE})+")")
var tagss string
flag.StringVar(&tagss, "tags", "", "csv of all tags to find, -x to invert")
flag.StringVar(&config.search, "search", "", "fts case insensitive")
flag.BoolVar(&config.edit, "e", false, "edit file")
flag.BoolVar(&config.dedupe, "dedupe", false, "dedupe file")
flag.BoolVar(&config.dryRun, "dry-run", false, "dry run")
flag.StringVar(&config.add, "add", "", "todo to add")
flag.StringVar(&config.addSchedule, "add-schedule", "", "todo to add schedule")
flag.StringVar(&config.addTags, "add-tags", "", "todo to add csv tags")
flag.Parse()
config.tags = strings.Split(tagss, ",")
config.Targets()
return config
}
func (config config) Targets() []string {
result, err := config.targets()
if err != nil {
panic(err)
}
sort.Strings(result)
for i := range result {
if path.Base(result[i]) == "root.yaml" {
newresult := append([]string{result[i]}, result[:i]...)
newresult = append(newresult, result[i+1:]...)
result = newresult
break
}
}
return result
}
func (config config) targets() ([]string, error) {
patterns := []string{config.target, fmt.Sprintf("%s.*", config.target)}
isDir := false
if stat, err := os.Stat(config.target); err == nil {
isDir = stat.IsDir()
}
if isDir {
patterns = []string{fmt.Sprintf("%s/*", config.target)}
}
result := make([]string, 0, 1)
for _, pattern := range patterns {
results, err := filepath.Glob(pattern)
if err != nil {
return nil, err
}
for i := range results {
if stat, err := os.Stat(results[i]); err != nil {
return nil, err
} else if !stat.IsDir() {
result = append(result, results[i])
}
}
}
if len(result) == 0 {
if isDir {
return []string{path.Join(config.target, "root.yaml")}, nil
}
return []string{config.target}, nil
}
return result, nil
}

87
cmd/config_test.go Normal file
View File

@ -0,0 +1,87 @@
package main
import (
"os"
"path"
"testing"
)
func TestConfigTargets(t *testing.T) {
touch := func(t *testing.T, p string) {
if err := os.WriteFile(p, nil, os.ModePerm); err != nil {
t.Fatal(err)
}
}
cases := map[string]struct {
setup func(*testing.T, string)
given string
want []string
}{
"does not exist": {
given: "x",
want: []string{"x"},
},
"one file": {
setup: func(t *testing.T, d string) {
touch(t, path.Join(d, "x"))
},
given: "x",
want: []string{"x"},
},
"two files": {
setup: func(t *testing.T, d string) {
touch(t, path.Join(d, "a"))
touch(t, path.Join(d, "root.yaml"))
touch(t, path.Join(d, "z"))
},
given: "",
want: []string{"root.yaml", "a", "z"},
},
"empty dir": {
setup: func(t *testing.T, d string) {
os.Mkdir(path.Join(d, "x"), os.ModePerm)
},
given: "x",
want: []string{"root.yaml"},
},
"dir": {
setup: func(t *testing.T, d string) {
os.Mkdir(path.Join(d, "x"), os.ModePerm)
touch(t, path.Join(d, "x", "a"))
touch(t, path.Join(d, "x", "a.todo.xyz"))
touch(t, path.Join(d, "x", "b"))
},
given: "x",
want: []string{"a", "a.todo.xyz", "b"},
},
}
for name, d := range cases {
c := d
t.Run(name, func(t *testing.T) {
d := t.TempDir()
if c.setup != nil {
c.setup(t, d)
}
config := config{target: path.Join(d, c.given)}
got := config.Targets()
for i := range got {
got[i] = path.Base(got[i])
}
for i := range c.want {
c.want[i] = path.Base(c.want[i])
}
t.Logf("want\n\t%+v, got \n\t%+v", c.want, got)
if len(got) != len(c.want) {
t.Error(c.want, got)
}
for i := range got {
if got[i] != c.want[i] {
t.Errorf("[%d] wanted %s, got %s", i, c.want[i], got[i])
}
}
})
}
}

44
cmd/dump.go Normal file
View File

@ -0,0 +1,44 @@
package main
import (
"io"
"os"
"gitea.inhome.blapointe.com/gogs/pttodo/pttodo"
"gopkg.in/yaml.v2"
)
func dump(config config) error {
return _dump(os.Stdout, config.Targets(), config.tags, config.search, config.root)
}
func _dump(writer io.Writer, filepaths []string, tags []string, search, rootDisplay string) error {
root, err := pttodo.NewRootFromFiles(filepaths...)
if err != nil {
return err
}
for _, x := range []*[]pttodo.Todo{
&root.Todo,
&root.Scheduled,
&root.Done,
} {
y := pttodo.Todos(*x)
y = y.LikeTags(tags)
y = y.LikeSearch(search)
*x = y
}
var v interface{} = root
switch rootDisplay {
case DUMP_TODO:
v = root.Todo
case DUMP_SCHEDULED:
v = root.Scheduled
case DUMP_DONE:
v = root.Done
default:
}
return yaml.NewEncoder(writer).Encode(v)
}

262
cmd/edit.go Normal file
View File

@ -0,0 +1,262 @@
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"sort"
"strings"
"syscall"
"time"
"gitea.inhome.blapointe.com/gogs/pttodo/pttodo"
"github.com/google/uuid"
"gopkg.in/yaml.v2"
)
func edit(config *config) error {
if !config.edit {
return nil
}
return _edit(config.Targets())
}
func _edit(filepaths []string) error {
editableDir, err := inEditableDirAsTodos(filepaths)
if err != nil {
return err
}
if err := modifyEditableDir(editableDir, func() error {
files, err := listDir(editableDir)
if err != nil {
return err
}
for _, p := range files {
if _, err := pttodo.NewTodosFromFile(p); err != nil {
return err
}
}
return nil
}); err != nil {
return err
}
editedFiles, err := listDir(editableDir)
if err != nil {
return err
}
edits := map[string]string{}
for _, editedFile := range editedFiles {
edits[path.Base(editedFile)] = editedFile
edited, err := pttodo.NewTodosFromFile(editedFile)
if err != nil {
return err
}
original, err := pttodo.NewRootFromFile(func() string {
for _, f := range filepaths {
if path.Base(f) == path.Base(editedFile) {
return f
}
}
return "/dev/null"
}())
if err != nil {
return err
}
editedIDs := map[string]int{}
for i := range edited {
editedIDs[edited[i].ID()] = 1
}
for i := range original.Todo {
if _, ok := editedIDs[original.Todo[i].ID()]; ok {
continue
}
original.Todo[i].TS = pttodo.TS(time.Now().Unix())
if string(original.Todo[i].Schedule) != "" && !original.Todo[i].Triggered() {
original.Scheduled = append(original.Scheduled, original.Todo[i])
}
original.Done = append(original.Done, original.Todo[i])
}
original.Todo = edited
original.AutoMove()
if err := func() error {
f, err := os.Create(editedFile)
if err != nil {
return err
}
defer f.Close()
return yaml.NewEncoder(f).Encode(original)
}(); err != nil {
return err
}
}
dir := ""
for _, f := range filepaths {
if edited, ok := edits[path.Base(f)]; ok {
if err := rename(edited, f); err != nil {
return err
}
delete(edits, path.Base(f))
} else if err := os.Remove(f); err != nil {
return err
}
dir = path.Dir(f)
}
for base, editedFile := range edits {
f := path.Join(dir, base)
if _, err := os.Stat(f); err == nil {
f = fmt.Sprintf("%s.todo.%s", f, uuid.New().String())
}
if err := rename(editedFile, f); err != nil {
return err
}
}
return nil
}
func inEditableDirAsTodos(filepaths []string) (string, error) {
tempDir, err := ioutil.TempDir(os.TempDir(), "edit-pttodo-*")
if err != nil {
return "", err
}
return tempDir, copyTodoToDir(tempDir, filepaths)
}
func copyTodoToDir(d string, filepaths []string) error {
inboxes := map[string][]string{}
for _, target := range filepaths {
p := path.Join(d, path.Base(target))
if strings.Contains(path.Base(target), ".todo.") {
p := path.Join(d, strings.Split(path.Base(p), ".todo")[0])
inboxes[p] = append(inboxes[p], target)
continue
}
if root, err := pttodo.NewRootFromFile(target); err != nil {
return err
} else if b, err := yaml.Marshal(root.Todo); err != nil {
return err
} else if err := os.WriteFile(p, b, os.ModePerm); err != nil {
return err
}
}
for p, inboxes := range inboxes {
inboxRoot := pttodo.Root{}
for _, inbox := range inboxes {
subInboxRoot, err := pttodo.NewRootFromFile(inbox)
if err != nil {
return err
}
inboxRoot.MergeIn(pttodo.Root{Todo: subInboxRoot.Todo})
inboxRoot.MergeIn(pttodo.Root{Todo: subInboxRoot.Scheduled})
}
root, err := pttodo.NewRootFromFile(p)
if err != nil {
return err
}
root.MergeIn(pttodo.Root{Todo: inboxRoot.Todo})
root.MergeIn(pttodo.Root{Todo: inboxRoot.Scheduled})
if b, err := yaml.Marshal(root.Todo); err != nil {
return err
} else if err := os.WriteFile(p, b, os.ModePerm); err != nil {
return err
}
}
return nil
}
func modifyEditableDir(d string, check func() error) error {
for {
if err := vimd(d); err != nil {
return err
}
err := check()
if err == nil {
return nil
}
log.Printf("%s, press <Enter> to resume editing", err)
b := make([]byte, 1)
if _, err := os.Stdin.Read(b); err != nil {
return err
}
}
}
func vimd(d string) error {
bin := "vim"
editorbin, err := exec.LookPath(bin)
if err != nil {
editorbin, err = exec.LookPath("vi")
}
if err != nil {
return err
}
args := []string{editorbin, "-p"}
files, err := listDir(d)
if err != nil {
return err
}
args = append(args, files...)
cpid, err := syscall.ForkExec(
editorbin,
args,
&syscall.ProcAttr{
Dir: d,
Env: os.Environ(),
Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
Sys: nil,
},
)
if err != nil {
return err
}
proc, err := os.FindProcess(cpid)
if err != nil {
return err
}
state, err := proc.Wait()
if err != nil {
return err
}
if exitCode := state.ExitCode(); exitCode != 0 {
return fmt.Errorf("bad exit code on vim: %d, state: %+v", exitCode, state)
}
return nil
}
func listDir(dname string) ([]string, error) {
entries, err := os.ReadDir(dname)
if err != nil {
return nil, err
}
paths := make([]string, 0, len(entries))
for i := range entries {
if entries[i].IsDir() {
continue
}
if strings.HasPrefix(path.Base(entries[i].Name()), ".") {
continue
}
paths = append(paths, path.Join(dname, entries[i].Name()))
}
sort.Slice(paths, func(i, j int) bool {
if path.Base(paths[i]) == "root.yaml" {
return true
}
return paths[i] < paths[j]
})
return paths, nil
}

29
cmd/edit_test.go Normal file
View File

@ -0,0 +1,29 @@
package main
import (
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
"testing"
)
func TestListDir(t *testing.T) {
d := t.TempDir()
want := []string{}
for i := 0; i < 3; i++ {
p := path.Join(d, strconv.Itoa(i))
ioutil.WriteFile(p, []byte{}, os.ModePerm)
want = append(want, p)
}
os.Mkdir(path.Join(d, "d"), os.ModePerm)
got, err := listDir(d)
if err != nil {
t.Fatal(err)
}
if fmt.Sprint(want) != fmt.Sprint(got) {
t.Fatal(want, got)
}
}

13
cmd/go.mod Normal file
View File

@ -0,0 +1,13 @@
module pttodo-cli
go 1.21
require (
gitea.inhome.blapointe.com/gogs/pttodo v0.0.0-20231109151914-5d74b458d542
github.com/google/uuid v1.3.0
gopkg.in/yaml.v2 v2.4.0
)
require github.com/robfig/cron/v3 v3.0.1 // indirect
replace gogs.inhome.blapointe.com/gogs/pttodo => ./..

View File

@ -1,3 +1,7 @@
gitea.inhome.blapointe.com/gogs/pttodo v0.0.0-20231109151914-5d74b458d542 h1:eOWrA2hEQoyu413vbdXbEXHLXEX2TVBXjWawlWndFhg=
gitea.inhome.blapointe.com/gogs/pttodo v0.0.0-20231109151914-5d74b458d542/go.mod h1:9CFZf/SSod0Z/8WvbmOI4gEzy6xGpBQAq8coVNncNvk=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=

20
cmd/install.sh Normal file
View File

@ -0,0 +1,20 @@
#! /bin/bash
cd "$(dirname "$BASH_SOURCE")"
binary_name="$(head -n 1 go.mod | awk '{print $NF}' | sed 's/.*\///')"
git_commit="$((
git rev-list -1 HEAD
if git diff | grep . > /dev/null; then
echo "-dirty"
fi
) 2> /dev/null | tr -d '\n')"
GOFLAGS="" \
GO111MODULE="" \
CGO_ENABLED=0 \
go build \
-o $GOPATH/bin/$binary_name \
-a \
-installsuffix cgo \
-ldflags "-s -w -X main.GitCommit=$git_commit"

302
cmd/main.go Normal file
View File

@ -0,0 +1,302 @@
package main
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"slices"
"gitea.inhome.blapointe.com/gogs/pttodo/pttodo"
"gopkg.in/yaml.v2"
)
const (
DUMP_ALL = "all"
DUMP_TODO = "todo"
DUMP_SCHEDULED = "scheduled"
DUMP_DONE = "done"
)
func main() {
if err := _main(); err != nil {
panic(err)
}
}
func _main() error {
config := getConfig()
if config.target2 != "" {
return merge(&config)
} else if config.archive != "" {
return archive(&config)
} else if config.dedupe {
return dedupe(&config)
} else {
if err := add(&config); err != nil {
return err
}
if err := edit(&config); err != nil {
return err
}
return dump(config)
}
}
func dedupe(config *config) error {
baseReader, err := filePathReader(config.target)
if err != nil {
return err
}
baseB, err := ioutil.ReadAll(baseReader)
if err != nil {
return err
}
var base pttodo.Root
if err := yaml.Unmarshal(baseB, &base); err != nil {
return fmt.Errorf("failed to parse yaml in %s: %w", config.target, err)
}
do := func(todos []pttodo.Todo) []pttodo.Todo {
i := len(todos) - 1
for i >= 0 {
j := slices.IndexFunc(todos, func(todo pttodo.Todo) bool {
return todo.Equals(todos[i])
})
if j >= 0 && j != i {
todos[j] = todos[i]
base.Done = append(base.Done, todos[j])
todos = todos[:i]
}
i -= 1
}
return todos
}
base.Todo = do(base.Todo)
base.Scheduled = do(base.Scheduled)
if tmppath, err := marshalRootToTempFile(base); err != nil {
return err
} else if config.dryRun {
log.Println("===before===")
_dump(os.Stdout, []string{config.target}, config.tags, config.search, config.root)
log.Println("===after===")
_dump(os.Stdout, []string{tmppath}, config.tags, config.search, config.root)
} else if err := rename(tmppath, config.target); err != nil {
return err
}
return nil
}
func archive(config *config) error {
if config.archive == "" {
return nil
}
baseReader, err := filePathReader(config.target)
if err != nil {
return err
}
baseB, err := ioutil.ReadAll(baseReader)
if err != nil {
return err
}
archiveReader, err := filePathReader(config.archive)
if err != nil {
return err
}
archiveB, err := ioutil.ReadAll(archiveReader)
if err != nil {
return err
}
var base, archive pttodo.Root
if err := yaml.Unmarshal(baseB, &base); err != nil {
return fmt.Errorf("failed to parse yaml in %s: %w", config.target, err)
}
if err := yaml.Unmarshal(archiveB, &archive); err != nil {
return fmt.Errorf("failed to parse yaml in %s: %w", config.archive, err)
}
archive.Done = append(archive.Done, base.Done...)
if tmppath2, err := marshalRootToTempFile(archive); err != nil {
return err
} else if config.dryRun {
} else if err := rename(tmppath2, config.archive); err != nil {
return err
}
base.Done = base.Done[:0]
if tmppath, err := marshalRootToTempFile(base); err != nil {
return err
} else if config.dryRun {
log.Println("===before===")
_dump(os.Stdout, []string{config.target}, config.tags, config.search, config.root)
log.Println("===after===")
_dump(os.Stdout, []string{tmppath}, config.tags, config.search, config.root)
} else if err := rename(tmppath, config.target); err != nil {
return err
}
return nil
}
func merge(config *config) error {
if config.target2 == "" {
return nil
}
baseReader, err := filePathReader(config.target)
if err != nil {
return err
}
baseB, err := ioutil.ReadAll(baseReader)
if err != nil {
return err
}
mergingReader, err := filePathReader(config.target2)
if err != nil {
return err
}
mergingB, err := ioutil.ReadAll(mergingReader)
if err != nil {
return err
}
var base, merging pttodo.Root
if err := yaml.Unmarshal(baseB, &base); err != nil {
return fmt.Errorf("failed to parse yaml in %s: %w", config.target, err)
}
if err := yaml.Unmarshal(mergingB, &merging); err != nil {
return fmt.Errorf("failed to parse yaml in %s: %w", config.target2, err)
}
slurp := func(dst, src *[]pttodo.Todo) {
_src := *src
for i := range _src {
found := false
for j := range *dst {
found = found || _src[i].Equals((*dst)[j])
}
if !found {
*dst = append(*dst, _src[i])
}
}
}
slurp(&base.Todo, &merging.Todo)
slurp(&base.Scheduled, &merging.Scheduled)
slurp(&base.Done, &merging.Done)
if tmppath, err := marshalRootToTempFile(base); err != nil {
return err
} else if config.dryRun {
log.Println("===before===")
_dump(os.Stdout, []string{config.target}, config.tags, config.search, config.root)
log.Println("===after===")
_dump(os.Stdout, []string{tmppath}, config.tags, config.search, config.root)
} else if err := rename(tmppath, config.target); err != nil {
return err
}
merging.Done = append(merging.Done, merging.Todo...)
merging.Todo = merging.Todo[:0]
merging.Scheduled = append(merging.Done, merging.Scheduled...)
merging.Scheduled = merging.Scheduled[:0]
if tmppath2, err := marshalRootToTempFile(merging); err != nil {
return err
} else if config.dryRun {
} else if err := rename(tmppath2, config.target2); err != nil {
return err
}
return nil
}
func _merge(filepath string, mergeTargetFilePath string) error {
baseReader, err := filePathReader(filepath)
if err != nil {
return err
}
baseB, err := ioutil.ReadAll(baseReader)
if err != nil {
return err
}
mergingReader, err := filePathReader(mergeTargetFilePath)
if err != nil {
return err
}
mergingB, err := ioutil.ReadAll(mergingReader)
if err != nil {
return err
}
var base, merging pttodo.Root
if err := yaml.Unmarshal(baseB, &base); err != nil {
return fmt.Errorf("failed to parse yaml in %s: %w", filepath, err)
}
if err := yaml.Unmarshal(mergingB, &merging); err != nil {
return fmt.Errorf("failed to parse yaml in %s: %w", mergeTargetFilePath, err)
}
base.MergeIn(merging)
tmppath, err := marshalRootToTempFile(base)
if err != nil {
return err
}
return rename(tmppath, filepath)
}
func rename(oldpath, newpath string) error {
b, err := os.ReadFile(oldpath)
if err != nil {
return err
}
if err := os.WriteFile(newpath, b, os.ModePerm); err != nil {
return err
}
return os.Remove(oldpath)
}
func marshalRootToTempFile(root pttodo.Root) (string, error) {
f, err := ioutil.TempFile(os.TempDir(), "tmp")
if err != nil {
return "", err
}
f.Close()
os.Remove(f.Name())
b, err := yaml.Marshal(root)
if err != nil {
return "", err
}
filepath := f.Name() + ".yaml"
err = ioutil.WriteFile(filepath, b, os.ModePerm)
return filepath, err
}
func filePathReader(path string) (io.Reader, error) {
if path == "-" {
return os.Stdin, nil
}
b, err := ioutil.ReadFile(path)
if os.IsNotExist(err) {
return bytes.NewReader([]byte("{}")), nil
}
if err != nil {
return nil, err
}
return bytes.NewReader(b), nil
}

View File

@ -1,134 +0,0 @@
package main
import (
"bytes"
"flag"
"fmt"
"io"
"io/ioutil"
"local/pt-todo-server/pttodo"
"os"
"os/exec"
"path"
"syscall"
"gopkg.in/yaml.v2"
)
func main() {
if err := _main(); err != nil {
panic(err)
}
}
func _main() error {
filepath := flag.String("f", "-", "path to yaml file")
e := flag.Bool("e", false, "edit file")
flag.Parse()
if *e {
if err := edit(*filepath); err != nil {
return err
}
}
return dump(os.Stdout, *filepath)
}
func edit(filepath string) error {
var tempFile string
cp := func() error {
f, err := ioutil.TempFile(os.TempDir(), path.Base(filepath))
if err != nil {
return err
}
g, err := os.Open(filepath)
if err != nil {
return err
}
if _, err := io.Copy(f, g); err != nil {
return err
}
g.Close()
f.Close()
tempFile = f.Name()
return nil
}
vi := func() error {
vibin, err := exec.LookPath("vi")
if err != nil {
return err
}
cpid, err := syscall.ForkExec(
vibin,
[]string{vibin, tempFile},
&syscall.ProcAttr{
Dir: "",
Env: os.Environ(),
Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
Sys: nil,
},
)
if err != nil {
return err
}
proc, err := os.FindProcess(cpid)
if err != nil {
return err
}
state, err := proc.Wait()
if err != nil {
return err
}
if exitCode := state.ExitCode(); exitCode != 0 {
return fmt.Errorf("bad exit code on vim: %d, state: %+v", exitCode, state)
}
return nil
}
verify := func() error {
return dump(io.Discard, tempFile)
}
save := func() error {
return os.Rename(tempFile, filepath)
}
for _, foo := range []func() error{cp, vi, verify, save} {
if err := foo(); err != nil {
if tempFile != "" {
os.Remove(tempFile)
}
return err
}
}
return nil
}
func dump(writer io.Writer, filepath string) error {
var reader io.Reader
if filepath == "-" {
reader = os.Stdin
} else {
b, err := ioutil.ReadFile(filepath)
if err != nil {
return err
}
reader = bytes.NewReader(b)
}
b, err := ioutil.ReadAll(reader)
if err != nil {
return err
}
var root pttodo.Root
if err := yaml.Unmarshal(b, &root); err != nil {
return err
}
root.MoveScheduledToTodo()
b2, err := yaml.Marshal(root)
if err != nil {
return err
}
fmt.Fprintf(writer, "%s\n", b2)
return nil
}

View File

@ -1,12 +0,0 @@
module pttodo
go 1.17
require (
gopkg.in/yaml.v2 v2.4.0
local/pt-todo-server v0.0.0-00010101000000-000000000000
)
require github.com/robfig/cron/v3 v3.0.1 // indirect
replace local/pt-todo-server => ../../

20
cmd/testdata/1.yaml vendored Executable file
View File

@ -0,0 +1,20 @@
todo:
- "1"
- "2"
- "1"
- "2"
- "2"
scheduled:
- todo: "10"
schedule: 0 0 0 * *
ts: Sat Aug 10 02:42:48 MDT 2024
- todo: "20"
- todo: "10"
- todo: "20"
- todo: "20"
done:
- todo: "100"
- todo: "200"
- todo: "100"
- todo: "200"
- todo: "200"

4
cmd/testdata/2.yaml vendored Executable file
View File

@ -0,0 +1,4 @@
todo:
- "2"
scheduled: []
done: []

4
cmd/testdata/3.yaml vendored Executable file
View File

@ -0,0 +1,4 @@
todo:
- "3"
scheduled: []
done: []

17
cmd/testdata/root.yaml vendored Executable file
View File

@ -0,0 +1,17 @@
todo:
- root
- stub
scheduled:
- todo: abc
schedule: "2024-01-01"
ts: Thu Nov 9 07:49:59 MST 2023
- todo: "1"
schedule: "2024-01-02"
ts: Thu Nov 9 07:51:13 MST 2023
- todo: "1"
schedule: "2024-01-02"
ts: Thu Nov 9 07:51:28 MST 2023
- todo: "1"
schedule: "2024-01-02"
ts: Thu Nov 9 07:51:36 MST 2023
done: []

10
cmd/testdata/test.yaml vendored Executable file
View File

@ -0,0 +1,10 @@
todo:
- hi20890
- hi
- b
- todo: loop
schedule: 10s
- todo: past
schedule: "2000-01-02"
scheduled: []
done: []

4
go.mod
View File

@ -1,6 +1,6 @@
module local/pt-todo-server
module gitea.inhome.blapointe.com/gogs/pttodo
go 1.17
go 1.21
require (
github.com/robfig/cron/v3 v3.0.1

View File

@ -1,11 +1,91 @@
package pttodo
import (
"bytes"
"fmt"
"os"
yaml "gopkg.in/yaml.v2"
)
type Root struct {
Todo []Todo
Scheduled []Todo
Done []Todo
}
func NewRootFromFiles(p ...string) (Root, error) {
var result Root
for _, p := range p {
subroot, err := NewRootFromFile(p)
if err != nil {
return Root{}, err
}
result.MergeIn(subroot)
}
result.AutoMove()
return result, nil
}
func NewRootFromFile(p string) (Root, error) {
if b, err := os.ReadFile(p); err == nil && len(bytes.TrimSpace(b)) == 0 {
return Root{}, nil
}
f, err := os.Open(p)
if os.IsNotExist(err) {
return Root{}, nil
}
if err != nil {
return Root{}, err
}
defer f.Close()
var result Root
if err := yaml.NewDecoder(f).Decode(&result); err != nil {
todos, err2 := NewTodosFromFile(p)
if err2 != nil {
return Root{}, fmt.Errorf("failed to parse yaml from %s: %w", p, err)
}
result.Todo = todos
}
result.AutoMove()
return result, nil
}
func (root Root) Equals(other Root) bool {
for i, slice := range [][2][]Todo{
[2][]Todo{root.Todo, other.Todo},
[2][]Todo{root.Scheduled, other.Scheduled},
[2][]Todo{root.Done, other.Done},
} {
_ = i
if !equalTodoSlices(slice[0], slice[1]) {
return false
}
}
return true
}
func (root *Root) AutoMove() {
root.MoveScheduledToTodo()
root.MoveTodoToScheduled()
}
func (root *Root) MoveTodoToScheduled() {
for i := len(root.Todo) - 1; i >= 0; i-- {
if !root.Todo[i].Schedule.isFixedFuture() {
continue
}
root.Scheduled = append(root.Scheduled, root.Todo[i])
for j := i; j < len(root.Todo)-1; j++ {
root.Todo[j] = root.Todo[j+1]
}
root.Todo = root.Todo[:len(root.Todo)-1]
}
}
func (root *Root) MoveScheduledToTodo() {
for i := len(root.Scheduled) - 1; i >= 0; i-- {
if root.Scheduled[i].Triggered() {
@ -18,3 +98,35 @@ func (root *Root) MoveScheduledToTodo() {
}
}
}
func (root *Root) MergeIn(root2 Root) {
for _, listPair := range [][2]*[]Todo{
[2]*[]Todo{&root.Todo, &root2.Todo},
[2]*[]Todo{&root.Scheduled, &root2.Scheduled},
[2]*[]Todo{&root.Done, &root2.Done},
} {
for _, candidate := range *listPair[1] {
found := false
for i := range *listPair[0] {
found = found || ((*listPair[0])[i].Todo == candidate.Todo)
}
if !found {
*listPair[0] = append(*listPair[0], candidate)
}
}
}
}
func (root Root) MarshalYAML() (interface{}, error) {
for i := range root.Todo {
root.Todo[i].writeTS = false
}
for i := range root.Scheduled {
root.Scheduled[i].writeTS = true
}
for i := range root.Done {
root.Done[i].writeTS = true
}
type Alt Root
return (Alt)(root), nil
}

View File

@ -1,11 +1,17 @@
package pttodo
import (
"bytes"
"encoding/json"
"fmt"
"os"
"path"
"strconv"
"strings"
"testing"
"time"
yaml "gopkg.in/yaml.v2"
)
func TestJSONRoot(t *testing.T) {
@ -21,6 +27,8 @@ func TestJSONRoot(t *testing.T) {
t.Fatal(err)
} else if fmt.Sprint(root) != fmt.Sprint(root2) {
t.Fatal(root, root2)
} else if !root.Equals(root2) {
t.Fatal(root2)
}
}
@ -92,7 +100,160 @@ func TestRootMoveScheduledToTodo(t *testing.T) {
got.MoveScheduledToTodo()
if fmt.Sprintf("%+v", got) != fmt.Sprintf("%+v", want) {
t.Fatalf("want \n\t%+v, got \n\t%+v", want, got)
} else if !got.Equals(want) {
t.Fatalf("want \n\t%+v, got \n\t%+v", want, got)
}
})
}
}
func TestMergeRoots(t *testing.T) {
root0yaml := `
todo:
- a
- b
- todo: c
- todo: d
tags: a
- exclusive to 0
`
root1yaml := `
todo:
- a
- b
- todo: c
- todo: d
tags: b
- exclusive to 1
`
rootWantyaml := `
todo:
- a
- b
- todo: c
- todo: d
tags: a
- exclusive to 0
- exclusive to 1
`
var root0, root1, rootWant Root
if err := yaml.Unmarshal([]byte(root0yaml), &root0); err != nil {
t.Fatal(err)
}
if err := yaml.Unmarshal([]byte(root1yaml), &root1); err != nil {
t.Fatal(err)
}
if err := yaml.Unmarshal([]byte(rootWantyaml), &rootWant); err != nil {
t.Fatal(err)
}
root0.MergeIn(root1)
if fmt.Sprintf("%+v", root0) != fmt.Sprintf("%+v", rootWant) {
t.Fatalf("want \n\t%+v, got \n\t%+v", rootWant, root0)
} else if !root0.Equals(rootWant) {
t.Fatalf("want \n\t%+v, got \n\t%+v", rootWant, root0)
}
}
func TestRootMarshalYAMLWriteTS(t *testing.T) {
root := Root{
Todo: []Todo{Todo{Todo: "todo", TS: 1, writeTS: true}},
Scheduled: []Todo{Todo{Todo: "sched", TS: 2, writeTS: false, Schedule: "2099-01-01"}},
Done: []Todo{Todo{Todo: "done", TS: 3, writeTS: false}},
}
got, err := yaml.Marshal(root)
if err != nil {
t.Fatal(err)
}
got = bytes.TrimSpace(got)
want := strings.TrimSpace(`
todo:
- todo
scheduled:
- todo: sched
schedule: "2099-01-01"
ts: Wed Dec 31 17:00:02 MST 1969
done:
- todo: done
ts: Wed Dec 31 17:00:03 MST 1969
`)
if string(got) != want {
t.Fatalf("want\n\t%q, got\n\t%q", want, string(got))
}
}
func TestRootFromFile(t *testing.T) {
cases := map[string]struct {
given string
want Root
}{
"empty": {},
"happy": {
given: `{"todo":["a", "b"],"scheduled":["c"], "done":[{"todo": "d"}]}`,
want: Root{
Todo: []Todo{
Todo{Todo: "a"},
Todo{Todo: "b"},
},
Scheduled: []Todo{
Todo{Todo: "c"},
},
Done: []Todo{
Todo{Todo: "d"},
},
},
},
"todos": {
given: `["a", {"todo": "b"}]`,
want: Root{
Todo: []Todo{
{Todo: "a"},
{Todo: "b"},
},
},
},
}
for name, d := range cases {
c := d
t.Run(name, func(t *testing.T) {
d := t.TempDir()
p := path.Join(d, "input.yaml")
if err := os.WriteFile(p, []byte(c.given), os.ModePerm); err != nil {
t.Fatal(err)
}
got, err := NewRootFromFile(p)
if err != nil {
t.Fatal(err)
}
if fmt.Sprintf("%+v", got) != fmt.Sprintf("%+v", c.want) {
t.Errorf("want\n\t%+v, got\n\t%+v", c.want, got)
}
})
}
}
func TestRootFromFiles(t *testing.T) {
d := t.TempDir()
ps := []string{
path.Join(d, "a"),
path.Join(d, "b"),
}
os.WriteFile(ps[0], []byte(`["a"]`), os.ModePerm)
os.WriteFile(ps[1], []byte(`["b"]`), os.ModePerm)
got, err := NewRootFromFiles(ps...)
if err != nil {
t.Fatal(err)
}
want := Root{
Todo: []Todo{
{Todo: "a"},
{Todo: "b"},
},
}
if fmt.Sprintf("%+v", got) != fmt.Sprintf("%+v", want) {
t.Errorf("want\n\t%+v, got \n\t%+v", want, got)
}
}

View File

@ -2,6 +2,7 @@ package pttodo
import (
"encoding/json"
"fmt"
"math"
"regexp"
"strconv"
@ -17,7 +18,10 @@ func (schedule Schedule) MarshalJSON() ([]byte, error) {
}
func (schedule *Schedule) UnmarshalJSON(b []byte) error {
return json.Unmarshal(b, (*string)(schedule))
if err := json.Unmarshal(b, (*string)(schedule)); err != nil {
return fmt.Errorf("failed to parse json from %q: %w", b, err)
}
return nil
}
func (schedule Schedule) Next(t time.Time) (time.Time, error) {
@ -25,6 +29,42 @@ func (schedule Schedule) Next(t time.Time) (time.Time, error) {
return scheduler.next(t)
}
func (schedule Schedule) isFixedFuture() bool {
if !schedule.isFixed() {
return false
}
return schedule.isFuture()
}
func (schedule Schedule) isFixed() bool {
if schedule.empty() {
return false
}
scheduler := schedulerFactory(string(schedule))
switch scheduler.(type) {
case scheduleEZDate, scheduleDue:
default:
return false
}
return true
}
func (schedule Schedule) isFuture() bool {
if schedule.empty() {
return false
}
scheduler := schedulerFactory(string(schedule))
t, err := scheduler.next(time.Now())
if err != nil {
return false
}
return time.Now().Before(t)
}
func (schedule Schedule) empty() bool {
return string(schedule) == ""
}
type scheduler interface {
next(time.Time) (time.Time, error)
}
@ -37,6 +77,8 @@ func schedulerFactory(s string) scheduler {
} else if scheduleDuePattern.MatchString(s) {
n, _ := strconv.Atoi(s)
return scheduleDue(n)
} else if scheduleEZDatePattern.MatchString(s) {
return scheduleEZDate(s)
}
return scheduleCron(s)
}
@ -88,3 +130,15 @@ var scheduleDuePattern = regexp.MustCompile(`^[0-9]+$`)
func (due scheduleDue) next(time.Time) (time.Time, error) {
return TS(due).time(), nil
}
// 2022-01-01
type scheduleEZDate string
var scheduleEZDatePattern = regexp.MustCompile(`^[0-9]{4}-[0-9]{2}-[0-9]{2}(T[0-9]{2})?$`)
func (ezdate scheduleEZDate) next(time.Time) (time.Time, error) {
if len(ezdate) == len("20xx-xx-xxTxx") {
return time.ParseInLocation("2006-01-02T15", string(ezdate), time.Local)
}
return time.ParseInLocation("2006-01-02", string(ezdate), time.Local)
}

View File

@ -50,11 +50,18 @@ func TestJSONSchedule(t *testing.T) {
func TestSchedulerFactory(t *testing.T) {
start := time.Date(2000, 1, 1, 1, 1, 1, 1, time.UTC)
someDay := time.Date(2001, 2, 3, 0, 0, 0, 0, time.UTC)
someHour := time.Date(2001, 2, 3, 15, 0, 0, 0, time.UTC)
cases := map[string]struct {
input string
want interface{}
next time.Time
}{
"ezdate with hour": {
input: `2000-01-03T15`,
want: scheduleEZDate(`2000-01-03T15`),
next: someHour,
},
"long dur": {
input: `2h1m`,
want: scheduleDuration("2h1m"),
@ -70,11 +77,6 @@ func TestSchedulerFactory(t *testing.T) {
want: scheduleDue(1),
next: time.Unix(1, 0),
},
"zero ts": {
input: `0`,
want: scheduleDue(0),
next: time.Unix(0, 0),
},
"never": {
input: ``,
want: scheduleNever{},
@ -90,6 +92,11 @@ func TestSchedulerFactory(t *testing.T) {
want: scheduleCron(`5 * * * *`),
next: start.Add(time.Duration(-1*start.Nanosecond() + -1*start.Minute() + -1*start.Second())).Add(5 * time.Minute),
},
"ezdate": {
input: `2000-01-03`,
want: scheduleEZDate(`2000-01-03`),
next: someDay,
},
}
for name, d := range cases {
@ -108,4 +115,75 @@ func TestSchedulerFactory(t *testing.T) {
}
})
}
t.Run("zero ts", func(t *testing.T) {
got := schedulerFactory("0")
if fmt.Sprintf("%T", scheduleDue(0)) != fmt.Sprintf("%T", got) {
t.Fatalf("want type %T, got %T", scheduleDue(0), got)
}
if fmt.Sprint(scheduleDue(0)) != fmt.Sprint(got) {
t.Fatalf("want %+v, got %+v", scheduleDue(0), got)
}
next, _ := got.next(start)
if now := time.Now(); next.Sub(now) > time.Second {
t.Fatalf("want next %+v, got %+v", now, next)
}
})
}
func TestScheduleIsFixedFuture(t *testing.T) {
cases := map[string]struct {
input string
want bool
}{
"empty": {
input: "",
want: false,
},
"cron": {
input: "* * * * *",
want: false,
},
"duration": {
input: "1m",
want: false,
},
"due past": {
input: "123",
want: false,
},
"due future": {
input: "9648154541",
want: true,
},
"ez date short past": {
input: "2000-01-02",
want: false,
},
"ez date short future": {
input: "2100-01-02",
want: true,
},
"ez date long past": {
input: "2000-01-02T01",
want: false,
},
"ez date long future": {
input: "2100-01-02T02",
want: true,
},
}
for name, d := range cases {
c := d
t.Run(name, func(t *testing.T) {
schedule := Schedule(c.input)
got := schedule.isFixedFuture()
if got != c.want {
gotFuture := schedule.isFuture()
gotFixed := schedule.isFixed()
t.Errorf("want %v, got %v = %v && %v", c.want, got, gotFuture, gotFixed)
}
})
}
}

View File

@ -1,17 +1,53 @@
package pttodo
import (
"encoding/base64"
"fmt"
"hash/crc32"
"os"
"time"
yaml "gopkg.in/yaml.v2"
)
type Todo struct {
Todo string
Details string `yaml:",omitempty"`
TS TS `yaml:",omitempty"`
Schedule Schedule `yaml:",omitempty"`
Tags string `yaml:",omitempty"`
Subtasks []Todo `yaml:",omitempty"`
TS TS `yaml:",omitempty"`
writeTS bool
}
func NewTodosFromFile(p string) ([]Todo, error) {
f, err := os.Open(p)
if os.IsNotExist(err) {
return nil, nil
}
if err != nil {
return nil, err
}
defer f.Close()
var result []Todo
if err := yaml.NewDecoder(f).Decode(&result); err != nil {
return nil, fmt.Errorf("failed to parse yaml from %s: %w", p, err)
}
return result, nil
}
func (todo Todo) ID() string {
hash := crc32.NewIEEE()
fmt.Fprintf(hash, "%d:%s", 0, todo.Todo)
fmt.Fprintf(hash, "%d:%s", 1, todo.Details)
fmt.Fprintf(hash, "%d:%s", 2, todo.Schedule)
fmt.Fprintf(hash, "%d:%s", 3, todo.Tags)
for i := range todo.Subtasks {
fmt.Fprintf(hash, "%d:%s", 4, todo.Subtasks[i].ID())
}
return base64.StdEncoding.EncodeToString(hash.Sum(nil))
}
func (todo Todo) Triggered() bool {
@ -21,6 +57,11 @@ func (todo Todo) Triggered() bool {
}
func (todo Todo) MarshalYAML() (interface{}, error) {
if !todo.writeTS {
todo.TS = 0
} else {
todo.TS = TS(todo.TS.time().Unix())
}
if fmt.Sprint(todo) == fmt.Sprint(Todo{Todo: todo.Todo}) {
return todo.Todo, nil
}
@ -37,3 +78,37 @@ func (todo *Todo) UnmarshalYAML(unmarshal func(interface{}) error) error {
alt := (*Alt)(todo)
return unmarshal(alt)
}
func (todo Todo) Equals(other Todo) bool {
if !equalTodoSlices(todo.Subtasks, other.Subtasks) {
return false
}
if todo.TS != other.TS {
return false
}
if todo.Tags != other.Tags {
return false
}
if todo.Schedule != other.Schedule {
return false
}
if todo.Details != other.Details {
return false
}
if todo.Todo != other.Todo {
return false
}
return true
}
func equalTodoSlices(a, b []Todo) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if !a[i].Equals(b[i]) {
return false
}
}
return true
}

View File

@ -125,3 +125,33 @@ func TestMarshalTodo(t *testing.T) {
}
})
}
func TestTodoID(t *testing.T) {
cases := map[string]Todo{
"empty": Todo{},
"todo": Todo{Todo: "abc"},
"details": Todo{Details: "abc"},
"todo,details": Todo{Todo: "abc", Details: "abc"},
}
got := map[string]bool{}
for name, todod := range cases {
todo := todod
t.Run(name, func(t *testing.T) {
if _, ok := got[todo.ID()]; ok {
t.Error("dupe", todo.ID())
}
got[todo.ID()] = true
t.Logf("%s: %+v", todo.ID(), todo)
todo2 := todo
todo2.TS = 1
if todo.ID() != todo2.ID() {
t.Error("ts changed")
}
todo2.writeTS = true
if todo.ID() != todo2.ID() {
t.Error("writets changed")
}
})
}
}

36
pttodo/todos.go Normal file
View File

@ -0,0 +1,36 @@
package pttodo
import "strings"
type Todos []Todo
func (todos Todos) LikeSearch(search string) Todos {
return todos.Like(func(todo Todo) bool {
return strings.Contains(
strings.ToLower(todo.Todo),
strings.ToLower(search),
)
})
}
func (todos Todos) LikeTags(tags []string) Todos {
return todos.Like(func(todo Todo) bool {
matches := true
for _, tag := range tags {
str := strings.TrimLeft(tag, "-")
want := !strings.HasPrefix(tag, "-")
matches = matches && strings.Contains(todo.Tags, str) == want
}
return matches
})
}
func (todos Todos) Like(like func(Todo) bool) Todos {
result := make(Todos, 0)
for i := range todos {
if like(todos[i]) {
result = append(result, todos[i])
}
}
return result
}

17
pttodo/todos_test.go Normal file
View File

@ -0,0 +1,17 @@
package pttodo
import "testing"
func TestTodosLikeTags(t *testing.T) {
todos := Todos{
{Todo: "a", Tags: "x"},
{Todo: "b", Tags: "x,y"},
}
result := todos.LikeTags([]string{"x", "-y"})
if len(result) != 1 {
t.Error(result)
} else if result[0].Todo != "a" {
t.Error(result[0].Todo)
}
}

View File

@ -2,12 +2,19 @@ package pttodo
import (
"encoding/json"
"errors"
"fmt"
"time"
yaml "gopkg.in/yaml.v2"
)
type TS int64
func (ts TS) time() time.Time {
if ts == 0 {
ts = TS(time.Now().Unix())
}
return time.Unix(int64(ts), 0)
}
@ -23,9 +30,27 @@ func (ts TS) MarshalYAML() (interface{}, error) {
if ts == 0 {
ts = TS(time.Now().Unix())
}
return int64(ts), nil
return ts.time().Format(time.UnixDate), nil
}
func (ts *TS) UnmarshalJSON(b []byte) error {
return json.Unmarshal(b, (*int64)(ts))
if err := yaml.Unmarshal(b, ts); err != nil {
return fmt.Errorf("faild to unmarshal TS from %q: %w", b, err)
}
return nil
}
func (ts *TS) UnmarshalYAML(unmarshaller func(interface{}) error) error {
var n int64
if err := unmarshaller(&n); err == nil {
*ts = TS(n)
return nil
}
var s string
if err := unmarshaller(&s); err == nil {
t, err := time.ParseInLocation(time.UnixDate, s, time.Local)
*ts = TS(t.Unix())
return err
}
return errors.New("illegal TS")
}

View File

@ -2,12 +2,41 @@ package pttodo
import (
"encoding/json"
"strings"
"testing"
"time"
yaml "gopkg.in/yaml.v2"
)
func TestTSMarshalYaml(t *testing.T) {
t.Run("nonzero", func(t *testing.T) {
var ts TS
if b, err := yaml.Marshal(TS(5)); err != nil {
t.Fatal(err)
} else if s := string(b); !strings.HasSuffix(strings.TrimSpace(s), ` 1969`) {
t.Fatal(s)
} else if err := yaml.Unmarshal(b, &ts); err != nil {
t.Fatal(err)
} else if ts != 5 {
t.Fatal(ts)
}
})
t.Run("zero", func(t *testing.T) {
var ts TS
if b, err := yaml.Marshal(TS(0)); err != nil {
t.Fatal(err)
} else if s := string(b); strings.TrimSpace(s) == `0` {
t.Fatal(s)
} else if err := yaml.Unmarshal(b, &ts); err != nil {
t.Fatal(err)
} else if ts == 0 {
t.Fatal(ts)
}
})
}
func TestJSONTS(t *testing.T) {
ts := TS(time.Now().Unix())
ts := TS(1234567890)
js, err := json.Marshal(ts)
if err != nil {
t.Fatal(err)
@ -18,7 +47,7 @@ func TestJSONTS(t *testing.T) {
t.Fatal(err)
}
if ts != ts2 {
t.Fatal(ts2)
t.Fatalf("want: %v, got: %v", ts, ts2)
}
if err := json.Unmarshal([]byte(`123`), &ts2); err != nil {

BIN
pttodoer/.DS_Store vendored Normal file

Binary file not shown.

634
pttodoer/Cargo.lock generated Normal file
View File

@ -0,0 +1,634 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anstream"
version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
[[package]]
name = "anstyle-parse"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "cc"
version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-targets",
]
[[package]]
name = "clap"
version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "colorchoice"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
[[package]]
name = "core-foundation-sys"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "croner"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "516aad5374ea0ea75a0f0f4512fb4e7ad46c5eeff9971cb8ebc8fd74f1cd16c1"
dependencies = [
"chrono",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "iana-time-zone"
version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "indexmap"
version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "js-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "memchr"
version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "proc-macro2"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
dependencies = [
"unicode-ident",
]
[[package]]
name = "pttodoer"
version = "0.1.0"
dependencies = [
"chrono",
"clap",
"croner",
"regex",
"serde",
"serde_yaml",
"tempdir",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "regex"
version = "1.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.202"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.202"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempdir"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
dependencies = [
"rand",
"remove_dir_all",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[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-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"

15
pttodoer/Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
[package]
name = "pttodoer"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = "0.4.38"
clap = { version = "4.4.8", features = ["derive"] }
croner = "2.0.4"
regex = "1.10.4"
serde = { version = "1.0.202", features = [ "serde_derive" ] }
serde_yaml = "0.9.34"
tempdir = "0.3.7"

1372
pttodoer/src/main.rs Normal file

File diff suppressed because it is too large Load Diff

4
pttodoer/src/testdata/.mvp.yaml.done vendored Normal file
View File

@ -0,0 +1,4 @@
- todo: a b and c
schedule: '* * * * *'
ts: 123
- d e f

17
pttodoer/src/testdata/legacy.yaml vendored Normal file
View File

@ -0,0 +1,17 @@
todo:
- a
- todo: b
schedule: 2000-01-01
scheduled:
- c
- todo: d
schedule: 2000-02-01
- todo: e
ts: Sun Dec 3 23:29:27 EST 2023
schedule: '0 0 1 1 *'
- todo: f
schedule: 2099-02-02
done:
- g
- todo: h
ts: Sun Dec 3 23:29:27 EST 2023

View File

@ -0,0 +1 @@
plaintext

View File

@ -0,0 +1,4 @@
todo: todo here
schedule: '* * * * *'
details: |
hello world

2
pttodoer/src/testdata/mvp.yaml vendored Normal file
View File

@ -0,0 +1,2 @@
- x
- y and z

View File

@ -0,0 +1,10 @@
- 1do: due
schedule: 2006-04-05
- 2do: scheduled
schedule: 2099-04-05
- 3do: repeating scheduled
schedule: 0 0 1 1 *
ts: 2001-02-03T04:05:06Z
- _done:
do: done
schedule: 2006-04-05

View File

@ -0,0 +1 @@
- file

View File

@ -0,0 +1 @@
- a

View File

@ -0,0 +1 @@
- b

View File

@ -0,0 +1,4 @@
- todo
- do: scheduled
schedule: 2099-01-01
- _done: any

View File

@ -0,0 +1 @@
{"ts":1762915830,"patch":{"op":"add","path":"/0","value":"scheduled tasks hide"}}

814
pttodoest/Cargo.lock generated Executable file
View File

@ -0,0 +1,814 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "aho-corasick"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anstream"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-parse"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys",
]
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bitflags"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "bumpalo"
version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "cc"
version = "1.2.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chrono"
version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
dependencies = [
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-link",
]
[[package]]
name = "clap"
version = "4.5.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
[[package]]
name = "colorchoice"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cron-parser"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702858ce99daf23d8822fb22ec363b641b4bdcd9704182211fc113b01870f6de"
dependencies = [
"chrono",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "gethostname"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
dependencies = [
"rustix",
"windows-link",
]
[[package]]
name = "hashbrown"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "iana-time-zone"
version = "0.1.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "indexmap"
version = "2.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "js-sys"
version = "0.3.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "json-patch"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f300e415e2134745ef75f04562dd0145405c2f7fd92065db029ac4b16b57fe90"
dependencies = [
"jsonptr",
"serde",
"serde_json",
"thiserror",
]
[[package]]
name = "jsonptr"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5a3cc660ba5d72bce0b3bb295bf20847ccbb40fd423f3f05b61273672e561fe"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "libc"
version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "linux-raw-sys"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
[[package]]
name = "log"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "proc-macro2"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "pttodoest"
version = "0.1.0"
dependencies = [
"chrono",
"clap",
"cron-parser",
"gethostname",
"json-patch",
"jsonptr",
"regex",
"serde",
"serde_json",
"serde_yaml",
"tempdir",
]
[[package]]
name = "quote"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "regex"
version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "rustix"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
"serde_core",
]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempdir"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
dependencies = [
"rand",
"remove_dir_all",
]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "wasm-bindgen"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76"
dependencies = [
"unicode-ident",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[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-core"
version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-result"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"

17
pttodoest/Cargo.toml Executable file
View File

@ -0,0 +1,17 @@
[package]
name = "pttodoest"
version = "0.1.0"
edition = "2024"
[dependencies]
chrono = "0.4.42"
clap = { version = "4.5.51", features = ["derive"] }
cron-parser = "0.11.0"
gethostname = "1.1.0"
json-patch = "4.1.0"
jsonptr = "0.7.1"
regex = "1.12.2"
serde = { version = "1.0.228", features = ["serde_derive"] }
serde_json = "1.0.145"
serde_yaml = "0.9.34"
tempdir = "0.3.7"

1136
pttodoest/src/main.rs Executable file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
{"ts":1762915973,"op":"Add","task":"read; https://topicpartition.io/blog/postgres-pubsub-queue-benchmarks","tasks":null}
{"ts":1762915973,"op":"Add","task":"pglogical vs ha\n\n# api.git#breel/keys-620-pglogical-always-set-cr/2-user-survives-cr\n$ mise run pulsegres-new ^logical/toggl\n","tasks":null}
{"ts":1762915973,"op":"Add","task":"drive; VERIFY spoc posts daily summary w/ unresolved","tasks":null}
{"ts":1762915973,"op":"Add","task":"drive; VERIFY spoc refreshes summary w/ thread comment contianing 'refresh'","tasks":null}
{"ts":1762915973,"op":"Add","task":"637; reconcile deploy if replicas wrong; https://github.com/renderinc/api/pull/26540/files","tasks":null}
{"ts":1762915973,"op":"Add","task":"https://linear.app/render-com/issue/KEYS-633/add-3-when-max-connections-overridden-for-3-superuser-connections","tasks":null}
{"ts":1762915973,"op":"Add","task":"https://linear.app/render-com/issue/KEYS-637/billing-resume-should-1-unsuspend-pg-in-cloudsql-2-unsuspend-pg-in-cr","tasks":null}
{"ts":1762915973,"op":"Add","task":"https://linear.app/render-com/issue/KEYS-638/pgoperator-generates-new-ha-patroni-cert-every-reconcile-no-matter","tasks":null}
{"ts":1762915973,"op":"Add","task":"pg; how2partition; https://renderinc.slack.com/archives/C0319NYCSSG/p1756357545556659?thread_ts=1756357467.613369&cid=C0319NYCSSG","tasks":null}
{"ts":1762915973,"op":"Add","task":"pitr; backup purge cronjob for PL types","tasks":null}
{"ts":1762915973,"op":"Add","task":"pg11 pgbackup doesnt write to envsetting mucked env key","tasks":null}
{"ts":1762915973,"op":"Add","task":"incident io; teach spocbotvr to read slacks","tasks":null}
{"ts":1762915973,"op":"Add","task":"userdb to internal; peer packages can use internal as userdb","tasks":null}
{"ts":1762915973,"op":"Add","task":"fcr; cannot pitr because pgbackrest doesnt know wal spans thus pgexporter and friends cant know pitr works","tasks":null}
{"ts":1762915973,"op":"Add","task":"etcd statefulset of 1 (for no random podname, no conflict, k8s ensures pod replace)\npatroni always\n","tasks":null}
{"ts":1762915973,"op":"Add","task":"maher; https://slab.render.com/posts/hopes-and-dreams-blegf8fx#hdsyt-valkey-bundle","tasks":null}
{"ts":1762915973,"op":"Add","task":"maher; shadow lizhi pm loops","tasks":null}
{"ts":1762915973,"op":"Add","task":"maher; get more interviewers","tasks":null}
{"ts":1762915973,"op":"Add","task":"maher; get concrete career and project plans so i can get promo in 2y; no manager to advocate","tasks":null}
{"ts":1762915973,"op":"Add","task":"read; https://trychroma.com/engineering/wal3","tasks":null}
{"ts":1762915973,"op":"Add","task":"read; https://github.com/renderinc/dashboard/pull/8883","tasks":null}
{"ts":1762915973,"op":"Add","task":"read; https://litestream.io/getting-started/","tasks":null}
{"ts":1762915973,"op":"Add","task":"kr\nto del gcloud old key\nie https://console.cloud.google.com/iam-admin/serviceaccounts/details/104206017956912104938/keys?hl=en&project=render-prod\n","tasks":null}
{"ts":1762915973,"op":"Add","task":{"subtasks":["","pitr\nhttps://slab.render.com/posts/pitr-as-a-service-health-abvnqx11\nmore aggressive alert autotune backup cores\nmore aggressive alert on MOAR backup cores\ncreate alert autotune archive-push cores\ncreate alert MOAR archive-push cores\n","cr; frontend","cr; cli.git","cr; public-api-schema.git; https://github.com/renderinc/public-api-schema/pull/407 STILL NEED EVENTS","cr; website.git","cr; changelog","ops; pgproxy rate limits 50ps 100burst; https://github.com/renderinc/dbproxy/pull/91","2873; no conn patroni if upgradeInProgressWithoutHA; https://github.com/renderinc/api/pull/26328","2733; only EnvSettings; https://github.com/renderinc/api/pull/25322/files","pg18; after cred rotation works, re enable e2e","655; pg18; pub api sch; https://github.com/renderinc/public-api-schema/pull/421","655; pg18; go generate pub api sch; https://github.com/renderinc/api/pull/26694","663; das; show status in /info; https://github.com/renderinc/dashboard/pull/9616","664; pg18; go gen terraform; https://github.com/renderinc/api/pull/26701","664; pg18; ga; push terraform.git#breel/keys-664-pg18","656; pg18; website; https://github.com/renderinc/website/pull/985/files","663; das; note disk cannot decrease even if autoscaled; https://github.com/renderinc/dashboard/pull/9621","pulsegres; pls let me keep my test emails; https://github.com/renderinc/api/pull/26741","pgup; restore view owner; https://github.com/renderinc/api/pull/26814","pgup; resync if missing resync; https://github.com/renderinc/api/pull/26817","pgup; replicas use $RESYNC; https://github.com/renderinc/api/pull/26878"],"todo":"blocked"},"tasks":null}
{"ts":1762915973,"op":"Add","task":"hi","tasks":null}
{"ts":1764635053,"op":"Snapshot","task":null,"tasks":["read; https://topicpartition.io/blog/postgres-pubsub-queue-benchmarks","pglogical vs ha\n\n# api.git#breel/keys-620-pglogical-always-set-cr/2-user-survives-cr\n$ mise run pulsegres-new ^logical/toggl\n","drive; VERIFY spoc posts daily summary w/ unresolved","drive; VERIFY spoc refreshes summary w/ thread comment contianing 'refresh'","637; reconcile deploy if replicas wrong; https://github.com/renderinc/api/pull/26540/files","https://linear.app/render-com/issue/KEYS-633/add-3-when-max-connections-overridden-for-3-superuser-connections","https://linear.app/render-com/issue/KEYS-637/billing-resume-should-1-unsuspend-pg-in-cloudsql-2-unsuspend-pg-in-cr","https://linear.app/render-com/issue/KEYS-638/pgoperator-generates-new-ha-patroni-cert-every-reconcile-no-matter","pg; how2partition; https://renderinc.slack.com/archives/C0319NYCSSG/p1756357545556659?thread_ts=1756357467.613369&cid=C0319NYCSSG","pitr; backup purge cronjob for PL types","pg11 pgbackup doesnt write to envsetting mucked env key","incident io; teach spocbotvr to read slacks","userdb to internal; peer packages can use internal as userdb","fcr; cannot pitr because pgbackrest doesnt know wal spans thus pgexporter and friends cant know pitr works","etcd statefulset of 1 (for no random podname, no conflict, k8s ensures pod replace)\npatroni always\n","maher; https://slab.render.com/posts/hopes-and-dreams-blegf8fx#hdsyt-valkey-bundle","maher; shadow lizhi pm loops","maher; get more interviewers","maher; get concrete career and project plans so i can get promo in 2y; no manager to advocate","read; https://trychroma.com/engineering/wal3","read; https://github.com/renderinc/dashboard/pull/8883","read; https://litestream.io/getting-started/","kr\nto del gcloud old key\nie https://console.cloud.google.com/iam-admin/serviceaccounts/details/104206017956912104938/keys?hl=en&project=render-prod\n",{"subtasks":["","pitr\nhttps://slab.render.com/posts/pitr-as-a-service-health-abvnqx11\nmore aggressive alert autotune backup cores\nmore aggressive alert on MOAR backup cores\ncreate alert autotune archive-push cores\ncreate alert MOAR archive-push cores\n","cr; frontend","cr; cli.git","cr; public-api-schema.git; https://github.com/renderinc/public-api-schema/pull/407 STILL NEED EVENTS","cr; website.git","cr; changelog","ops; pgproxy rate limits 50ps 100burst; https://github.com/renderinc/dbproxy/pull/91","2873; no conn patroni if upgradeInProgressWithoutHA; https://github.com/renderinc/api/pull/26328","2733; only EnvSettings; https://github.com/renderinc/api/pull/25322/files","pg18; after cred rotation works, re enable e2e","655; pg18; pub api sch; https://github.com/renderinc/public-api-schema/pull/421","655; pg18; go generate pub api sch; https://github.com/renderinc/api/pull/26694","663; das; show status in /info; https://github.com/renderinc/dashboard/pull/9616","664; pg18; go gen terraform; https://github.com/renderinc/api/pull/26701","664; pg18; ga; push terraform.git#breel/keys-664-pg18","656; pg18; website; https://github.com/renderinc/website/pull/985/files","663; das; note disk cannot decrease even if autoscaled; https://github.com/renderinc/dashboard/pull/9621","pulsegres; pls let me keep my test emails; https://github.com/renderinc/api/pull/26741","pgup; restore view owner; https://github.com/renderinc/api/pull/26814","pgup; resync if missing resync; https://github.com/renderinc/api/pull/26817","pgup; replicas use $RESYNC; https://github.com/renderinc/api/pull/26878"],"todo":"blocked"},"hi"]}
{"ts":1764636274,"op":"Add","task":{"schedule":"2026-01-01","todo":"not yet due"},"tasks":null}
{"ts":1764721753,"op":"Add","task":"just_add","tasks":null}
{"ts":1764721753,"op":"Snapshot","task":null,"tasks":["read; https://topicpartition.io/blog/postgres-pubsub-queue-benchmarks","pglogical vs ha\n\n# api.git#breel/keys-620-pglogical-always-set-cr/2-user-survives-cr\n$ mise run pulsegres-new ^logical/toggl\n","drive; VERIFY spoc posts daily summary w/ unresolved","drive; VERIFY spoc refreshes summary w/ thread comment contianing 'refresh'","637; reconcile deploy if replicas wrong; https://github.com/renderinc/api/pull/26540/files","https://linear.app/render-com/issue/KEYS-633/add-3-when-max-connections-overridden-for-3-superuser-connections","https://linear.app/render-com/issue/KEYS-637/billing-resume-should-1-unsuspend-pg-in-cloudsql-2-unsuspend-pg-in-cr","https://linear.app/render-com/issue/KEYS-638/pgoperator-generates-new-ha-patroni-cert-every-reconcile-no-matter","pg; how2partition; https://renderinc.slack.com/archives/C0319NYCSSG/p1756357545556659?thread_ts=1756357467.613369&cid=C0319NYCSSG","pitr; backup purge cronjob for PL types","pg11 pgbackup doesnt write to envsetting mucked env key","incident io; teach spocbotvr to read slacks","userdb to internal; peer packages can use internal as userdb","fcr; cannot pitr because pgbackrest doesnt know wal spans thus pgexporter and friends cant know pitr works","etcd statefulset of 1 (for no random podname, no conflict, k8s ensures pod replace)\npatroni always\n","maher; https://slab.render.com/posts/hopes-and-dreams-blegf8fx#hdsyt-valkey-bundle","maher; shadow lizhi pm loops","maher; get more interviewers","maher; get concrete career and project plans so i can get promo in 2y; no manager to advocate","read; https://trychroma.com/engineering/wal3","read; https://github.com/renderinc/dashboard/pull/8883","read; https://litestream.io/getting-started/","kr\nto del gcloud old key\nie https://console.cloud.google.com/iam-admin/serviceaccounts/details/104206017956912104938/keys?hl=en&project=render-prod\n",{"subtasks":["","pitr\nhttps://slab.render.com/posts/pitr-as-a-service-health-abvnqx11\nmore aggressive alert autotune backup cores\nmore aggressive alert on MOAR backup cores\ncreate alert autotune archive-push cores\ncreate alert MOAR archive-push cores\n","cr; frontend","cr; cli.git","cr; public-api-schema.git; https://github.com/renderinc/public-api-schema/pull/407 STILL NEED EVENTS","cr; website.git","cr; changelog","ops; pgproxy rate limits 50ps 100burst; https://github.com/renderinc/dbproxy/pull/91","2873; no conn patroni if upgradeInProgressWithoutHA; https://github.com/renderinc/api/pull/26328","2733; only EnvSettings; https://github.com/renderinc/api/pull/25322/files","pg18; after cred rotation works, re enable e2e","655; pg18; pub api sch; https://github.com/renderinc/public-api-schema/pull/421","655; pg18; go generate pub api sch; https://github.com/renderinc/api/pull/26694","663; das; show status in /info; https://github.com/renderinc/dashboard/pull/9616","664; pg18; go gen terraform; https://github.com/renderinc/api/pull/26701","664; pg18; ga; push terraform.git#breel/keys-664-pg18","656; pg18; website; https://github.com/renderinc/website/pull/985/files","663; das; note disk cannot decrease even if autoscaled; https://github.com/renderinc/dashboard/pull/9621","pulsegres; pls let me keep my test emails; https://github.com/renderinc/api/pull/26741","pgup; restore view owner; https://github.com/renderinc/api/pull/26814","pgup; resync if missing resync; https://github.com/renderinc/api/pull/26817","pgup; replicas use $RESYNC; https://github.com/renderinc/api/pull/26878"],"todo":"blocked"},"hi","just_add"]}
{"ts":1764721753,"op":"Add","task":{"schedule":"2000-01-01","do":"add_past"},"tasks":null}
{"ts":1764721753,"op":"Snapshot","task":null,"tasks":["read; https://topicpartition.io/blog/postgres-pubsub-queue-benchmarks","pglogical vs ha\n\n# api.git#breel/keys-620-pglogical-always-set-cr/2-user-survives-cr\n$ mise run pulsegres-new ^logical/toggl\n","drive; VERIFY spoc posts daily summary w/ unresolved","drive; VERIFY spoc refreshes summary w/ thread comment contianing 'refresh'","637; reconcile deploy if replicas wrong; https://github.com/renderinc/api/pull/26540/files","https://linear.app/render-com/issue/KEYS-633/add-3-when-max-connections-overridden-for-3-superuser-connections","https://linear.app/render-com/issue/KEYS-637/billing-resume-should-1-unsuspend-pg-in-cloudsql-2-unsuspend-pg-in-cr","https://linear.app/render-com/issue/KEYS-638/pgoperator-generates-new-ha-patroni-cert-every-reconcile-no-matter","pg; how2partition; https://renderinc.slack.com/archives/C0319NYCSSG/p1756357545556659?thread_ts=1756357467.613369&cid=C0319NYCSSG","pitr; backup purge cronjob for PL types","pg11 pgbackup doesnt write to envsetting mucked env key","incident io; teach spocbotvr to read slacks","userdb to internal; peer packages can use internal as userdb","fcr; cannot pitr because pgbackrest doesnt know wal spans thus pgexporter and friends cant know pitr works","etcd statefulset of 1 (for no random podname, no conflict, k8s ensures pod replace)\npatroni always\n","maher; https://slab.render.com/posts/hopes-and-dreams-blegf8fx#hdsyt-valkey-bundle","maher; shadow lizhi pm loops","maher; get more interviewers","maher; get concrete career and project plans so i can get promo in 2y; no manager to advocate","read; https://trychroma.com/engineering/wal3","read; https://github.com/renderinc/dashboard/pull/8883","read; https://litestream.io/getting-started/","kr\nto del gcloud old key\nie https://console.cloud.google.com/iam-admin/serviceaccounts/details/104206017956912104938/keys?hl=en&project=render-prod\n",{"subtasks":["","pitr\nhttps://slab.render.com/posts/pitr-as-a-service-health-abvnqx11\nmore aggressive alert autotune backup cores\nmore aggressive alert on MOAR backup cores\ncreate alert autotune archive-push cores\ncreate alert MOAR archive-push cores\n","cr; frontend","cr; cli.git","cr; public-api-schema.git; https://github.com/renderinc/public-api-schema/pull/407 STILL NEED EVENTS","cr; website.git","cr; changelog","ops; pgproxy rate limits 50ps 100burst; https://github.com/renderinc/dbproxy/pull/91","2873; no conn patroni if upgradeInProgressWithoutHA; https://github.com/renderinc/api/pull/26328","2733; only EnvSettings; https://github.com/renderinc/api/pull/25322/files","pg18; after cred rotation works, re enable e2e","655; pg18; pub api sch; https://github.com/renderinc/public-api-schema/pull/421","655; pg18; go generate pub api sch; https://github.com/renderinc/api/pull/26694","663; das; show status in /info; https://github.com/renderinc/dashboard/pull/9616","664; pg18; go gen terraform; https://github.com/renderinc/api/pull/26701","664; pg18; ga; push terraform.git#breel/keys-664-pg18","656; pg18; website; https://github.com/renderinc/website/pull/985/files","663; das; note disk cannot decrease even if autoscaled; https://github.com/renderinc/dashboard/pull/9621","pulsegres; pls let me keep my test emails; https://github.com/renderinc/api/pull/26741","pgup; restore view owner; https://github.com/renderinc/api/pull/26814","pgup; resync if missing resync; https://github.com/renderinc/api/pull/26817","pgup; replicas use $RESYNC; https://github.com/renderinc/api/pull/26878"],"todo":"blocked"},"hi","just_add",{"schedule":"2000-01-01","do":"add_past"}]}
{"ts":2051222400,"op":"Add","task":{"schedule":"2035-01-01","do":"add_future"},"tasks":null}

67
pttodoest/src/testdata/root.yaml vendored Executable file
View File

@ -0,0 +1,67 @@
- read; https://topicpartition.io/blog/postgres-pubsub-queue-benchmarks
- |
pglogical vs ha
# api.git#breel/keys-620-pglogical-always-set-cr/2-user-survives-cr
$ mise run pulsegres-new ^logical/toggl
- drive; VERIFY spoc posts daily summary w/ unresolved
- drive; VERIFY spoc refreshes summary w/ thread comment contianing 'refresh'
- 637; reconcile deploy if replicas wrong; https://github.com/renderinc/api/pull/26540/files
- https://linear.app/render-com/issue/KEYS-633/add-3-when-max-connections-overridden-for-3-superuser-connections
- https://linear.app/render-com/issue/KEYS-637/billing-resume-should-1-unsuspend-pg-in-cloudsql-2-unsuspend-pg-in-cr
- https://linear.app/render-com/issue/KEYS-638/pgoperator-generates-new-ha-patroni-cert-every-reconcile-no-matter
- pg; how2partition; https://renderinc.slack.com/archives/C0319NYCSSG/p1756357545556659?thread_ts=1756357467.613369&cid=C0319NYCSSG
- pitr; backup purge cronjob for PL types
- pg11 pgbackup doesnt write to envsetting mucked env key
- incident io; teach spocbotvr to read slacks
- userdb to internal; peer packages can use internal as userdb
- fcr; cannot pitr because pgbackrest doesnt know wal spans thus pgexporter and friends cant know pitr works
- |
etcd statefulset of 1 (for no random podname, no conflict, k8s ensures pod replace)
patroni always
- maher; https://slab.render.com/posts/hopes-and-dreams-blegf8fx#hdsyt-valkey-bundle
- maher; shadow lizhi pm loops
- maher; get more interviewers
- maher; get concrete career and project plans so i can get promo in 2y; no manager to advocate
- read; https://trychroma.com/engineering/wal3
- read; https://github.com/renderinc/dashboard/pull/8883
- read; https://litestream.io/getting-started/
- |
kr
to del gcloud old key
ie https://console.cloud.google.com/iam-admin/serviceaccounts/details/104206017956912104938/keys?hl=en&project=render-prod
- subtasks:
- ''
- |
pitr
https://slab.render.com/posts/pitr-as-a-service-health-abvnqx11
more aggressive alert autotune backup cores
more aggressive alert on MOAR backup cores
create alert autotune archive-push cores
create alert MOAR archive-push cores
- cr; frontend
- cr; cli.git
- cr; public-api-schema.git; https://github.com/renderinc/public-api-schema/pull/407 STILL NEED EVENTS
- cr; website.git
- cr; changelog
- ops; pgproxy rate limits 50ps 100burst; https://github.com/renderinc/dbproxy/pull/91
- 2873; no conn patroni if upgradeInProgressWithoutHA; https://github.com/renderinc/api/pull/26328
- 2733; only EnvSettings; https://github.com/renderinc/api/pull/25322/files
- pg18; after cred rotation works, re enable e2e
- 655; pg18; pub api sch; https://github.com/renderinc/public-api-schema/pull/421
- 655; pg18; go generate pub api sch; https://github.com/renderinc/api/pull/26694
- 663; das; show status in /info; https://github.com/renderinc/dashboard/pull/9616
- 664; pg18; go gen terraform; https://github.com/renderinc/api/pull/26701
- 664; pg18; ga; push terraform.git#breel/keys-664-pg18
- 656; pg18; website; https://github.com/renderinc/website/pull/985/files
- 663; das; note disk cannot decrease even if autoscaled; https://github.com/renderinc/dashboard/pull/9621
- pulsegres; pls let me keep my test emails; https://github.com/renderinc/api/pull/26741
- pgup; restore view owner; https://github.com/renderinc/api/pull/26814
- pgup; resync if missing resync; https://github.com/renderinc/api/pull/26817
- pgup; replicas use $RESYNC; https://github.com/renderinc/api/pull/26878
todo: blocked
- hi
- just_add
- schedule: 2000-01-01
do: add_past

2
pttodoest/todo.yaml Normal file
View File

@ -0,0 +1,2 @@
- scheduled tasks hide

View File

View File

@ -1,9 +1,11 @@
todo:
- todo: when to run scheduled modifier? like, syncthing could have conflicts if I modify only file on remote
details: |
- if it's a web ui hook or somethin, then it'd only have file conflict if I modify without waiting
- but thats a step back from current todo solution
- when merging, check for complete/incomplete and cross reference todo vs scheduled
vs done
- click on links when dumped via cli
scheduled: []
done:
- xactions emails extend ledger,todo-server with pttodo - schedule merge inbox style
- xactions emails extend ledger,todo-server with pttodo
- todo: ez edit on many platforms, even offline and mobile
details: |
mobile view + complete method
@ -12,22 +14,69 @@ todo:
web server access to ops
is a web ui for mobile best solution?
let git be smart-ish and keep latest? would provide versioning OOTB without touching raw
done:
- todo: when to run scheduled modifier? like, syncthing could have conflicts if I
modify only file on remote
details: |
- if it's a web ui hook or somethin, then it'd only have file conflict if I modify without waiting
- but thats a step back from current todo solution
tags: stuffToTry,secondTag
- what do about todo-now vs todo vs watch vs later... could do many files and manage
outside binary, but search would suck. Good would be vendor lockin, and that's UI
problem
- merge multi todo files for 'inbox' style with some dedupe (probably best to just
do this via calling pttodo-cli from script or somethin) (probably at an off hour
so no collisions from live editing)
- how to defer a scheduled task?
- todo: add tag anti-search, like -tag=-notMe
ts: Tue Jan 4 06:56:03 EST 2022
- todo: if in todo, then omit ts field
ts: Sun Jan 2 20:44:27 EST 2022
- todo: merge full files to import from todo-server
ts: Sun Jan 2 20:44:27 EST 2022
- todo: more schedule formats, like just 2022-01-15, for deferring
ts: Sun Jan 2 20:44:27 EST 2022
- todo: from todo-server to pttodo format
ts: Sun Jan 2 20:44:27 EST 2022
- todo: add -tag to search via cli
ts: Sun Jan 2 20:44:27 EST 2022
- todo: ts to human readable
ts: Sun Jan 2 20:44:27 EST 2022
- todo: here is my really here is my really here is my really here is my really here
is my really here is my really here is my really here is my really here is my
really here is my really here is my really here is my really here is my really
here is my really here is my really long string
ts: Sun Jan 2 20:44:27 EST 2022
- todo: vim doesnt source vimrc, so stuff like tab width and tab characters, also
syntax highlight :(
ts: Sun Jan 2 20:44:27 EST 2022
- todo: crontab -e style editing to ensure good syntax
ts: Sun Jan 2 20:44:27 EST 2022
- todo: YAML based todo
details:
because goddamnit a year of this shit
isn't significant on disk or in RAM for vim
details: because goddamnit a year of this shit isn't significant on disk or in RAM
for vim
ts: Fri Dec 31 22:33:12 EST 2021
- todo: yaml based todo for plaintext
details: a year isnt even a mb
- ez edit, start on many platforms
- defer
- schedule
- looping
- details
ts: Fri Dec 31 22:33:12 EST 2021
- todo: ez edit, start on many platforms
ts: Sun Jan 2 20:44:27 EST 2022
- todo: defer
ts: Sun Jan 2 20:44:27 EST 2022
- todo: schedule
ts: Sun Jan 2 20:44:27 EST 2022
- todo: looping
ts: Sun Jan 2 20:44:27 EST 2022
- todo: details
ts: Sun Jan 2 20:44:27 EST 2022
- todo: let UI be UI for whatever platform
- tags
ts: Sun Jan 2 20:44:27 EST 2022
- todo: tags
ts: Sun Jan 2 20:44:27 EST 2022
- todo: sub tasks
subtasks:
- a
ts: Fri Dec 31 22:33:12 EST 2021
- todo: crap losing on a bad edit hurts
details: |
?
ts: Fri Dec 31 22:37:58 EST 2021