Compare commits

..

No commits in common. "92b4be130c19576200ae578b730465252a39e487" and "b544616801b50ac4e643247678551aefe68a037d" have entirely different histories.

6 changed files with 113 additions and 705 deletions

524
Cargo.lock generated
View File

@ -129,18 +129,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "arrayref"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a"
[[package]]
name = "arrayvec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "async-trait"
version = "0.1.80"
@ -223,12 +211,6 @@ dependencies = [
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64"
version = "0.21.7"
@ -253,12 +235,6 @@ version = "0.10.0-beta"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea"
[[package]]
name = "beef"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
[[package]]
name = "bip39"
version = "2.0.0"
@ -311,29 +287,6 @@ dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "blake2b_simd"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
dependencies = [
"arrayref",
"arrayvec",
"constant_time_eq",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
@ -391,12 +344,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "chacha20"
version = "0.9.1"
@ -435,16 +382,6 @@ dependencies = [
"windows-targets 0.52.5",
]
[[package]]
name = "chrono-english"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f73d909da7eb4a7d88c679c3f5a1bc09d965754e0adb2e7627426cef96a00d6f"
dependencies = [
"chrono",
"scanlex",
]
[[package]]
name = "cipher"
version = "0.4.4"
@ -456,15 +393,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "clipboard-win"
version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892"
dependencies = [
"error-code",
]
[[package]]
name = "colog"
version = "1.3.0"
@ -492,12 +420,6 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "core-foundation-sys"
version = "0.8.6"
@ -513,12 +435,6 @@ dependencies = [
"libc",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "crypto-common"
version = "0.1.6"
@ -547,49 +463,12 @@ dependencies = [
"subtle",
]
[[package]]
name = "dirs"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
dependencies = [
"libc",
"redox_users 0.3.5",
"winapi",
]
[[package]]
name = "dirs"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
dependencies = [
"libc",
"redox_users 0.4.5",
"winapi",
]
[[package]]
name = "either"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
[[package]]
name = "endian-type"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
[[package]]
name = "env_filter"
version = "0.1.2"
@ -619,33 +498,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "error-code"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b"
[[package]]
name = "fd-lock"
version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947"
dependencies = [
"cfg-if",
"rustix",
"windows-sys 0.52.0",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -760,17 +612,6 @@ dependencies = [
"version_check",
]
[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.2.14"
@ -780,7 +621,7 @@ dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasi",
"wasm-bindgen",
]
@ -839,15 +680,6 @@ dependencies = [
"digest",
]
[[package]]
name = "home"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "http"
version = "1.1.0"
@ -1015,16 +847,6 @@ dependencies = [
"web-sys",
]
[[package]]
name = "interim"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afd0f0bff60c0e845844b6ee665e07990541ef3b70d8cd21861cf85b69fbef4"
dependencies = [
"chrono",
"logos",
]
[[package]]
name = "ipnet"
version = "2.9.0"
@ -1073,33 +895,6 @@ version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.6.0",
"libc",
]
[[package]]
name = "linefeed"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28715d08e35c6c074f9ae6b2e6a2420bac75d050c66ecd669d7d5b98e2caa036"
dependencies = [
"dirs 1.0.5",
"mortal",
"winapi",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "lnurl-pay"
version = "0.5.0"
@ -1118,39 +913,6 @@ version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "logos"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff1ceb190eb9bdeecdd8f1ad6a71d6d632a50905948771718741b5461fb01e13"
dependencies = [
"logos-derive",
]
[[package]]
name = "logos-codegen"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90be66cb7bd40cb5cc2e9cfaf2d1133b04a3d93b72344267715010a466e0915a"
dependencies = [
"beef",
"fnv",
"lazy_static",
"proc-macro2",
"quote",
"regex-syntax",
"syn",
]
[[package]]
name = "logos-derive"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45154231e8e96586b39494029e58f12f8ffcb5ecf80333a603a13aa205ea8cbd"
dependencies = [
"logos-codegen",
]
[[package]]
name = "lru"
version = "0.12.3"
@ -1172,12 +934,6 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.7.2"
@ -1194,43 +950,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasi",
"windows-sys 0.48.0",
]
[[package]]
name = "mortal"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c624fa1b7aab6bd2aff6e9b18565cc0363b6d45cbcd7465c9ed5e3740ebf097"
dependencies = [
"bitflags 2.6.0",
"libc",
"nix 0.26.4",
"smallstr",
"terminfo",
"unicode-normalization",
"unicode-width",
"winapi",
]
[[package]]
name = "mostr"
version = "0.3.0"
dependencies = [
"chrono",
"chrono-english",
"colog",
"colored",
"env_logger",
"interim",
"itertools",
"linefeed",
"log",
"nostr-sdk",
"parse_datetime",
"regex",
"rustyline",
"tokio",
"xdg",
]
@ -1241,48 +976,6 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e664971378a3987224f7a0e10059782035e89899ae403718ee07de85bec42afe"
[[package]]
name = "nibble_vec"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
dependencies = [
"smallvec",
]
[[package]]
name = "nix"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
"libc",
]
[[package]]
name = "nix"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [
"bitflags 2.6.0",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "nostr"
version = "0.33.0"
@ -1296,7 +989,7 @@ dependencies = [
"cbc",
"chacha20",
"chacha20poly1305",
"getrandom 0.2.14",
"getrandom",
"instant",
"js-sys",
"negentropy",
@ -1442,16 +1135,6 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
name = "parse_datetime"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bbf4e25b13841080e018a1e666358adfe5e39b6d353f986ca5091c210b586a1"
dependencies = [
"chrono",
"regex",
]
[[package]]
name = "password-hash"
version = "0.5.0"
@ -1489,44 +1172,6 @@ dependencies = [
"rustc_version",
]
[[package]]
name = "phf"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_codegen"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
dependencies = [
"phf_generator",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_shared"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
dependencies = [
"siphasher",
]
[[package]]
name = "pin-project"
version = "1.1.5"
@ -1594,16 +1239,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "radix_trie"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
dependencies = [
"endian-type",
"nibble_vec",
]
[[package]]
name = "rand"
version = "0.8.5"
@ -1631,35 +1266,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.14",
]
[[package]]
name = "redox_syscall"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "redox_users"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
dependencies = [
"getrandom 0.1.16",
"redox_syscall",
"rust-argon2",
]
[[package]]
name = "redox_users"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
dependencies = [
"getrandom 0.2.14",
"libredox",
"thiserror",
"getrandom",
]
[[package]]
@ -1741,25 +1348,13 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.14",
"getrandom",
"libc",
"spin",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "rust-argon2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
dependencies = [
"base64 0.13.1",
"blake2b_simd",
"constant_time_eq",
"crossbeam-utils",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
@ -1775,19 +1370,6 @@ dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags 2.6.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
]
[[package]]
name = "rustls"
version = "0.22.4"
@ -1843,28 +1425,6 @@ dependencies = [
"untrusted",
]
[[package]]
name = "rustyline"
version = "14.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7803e8936da37efd9b6d4478277f4b2b9bb5cdb37a113e8d63222e58da647e63"
dependencies = [
"bitflags 2.6.0",
"cfg-if",
"clipboard-win",
"fd-lock",
"home",
"libc",
"log",
"memchr",
"nix 0.28.0",
"radix_trie",
"unicode-segmentation",
"unicode-width",
"utf8parse",
"windows-sys 0.52.0",
]
[[package]]
name = "ryu"
version = "1.0.17"
@ -1880,12 +1440,6 @@ dependencies = [
"cipher",
]
[[package]]
name = "scanlex"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "088c5d71572124929ea7549a8ce98e1a6fd33d0a38367b09027b382e67c033db"
[[package]]
name = "scrypt"
version = "0.11.0"
@ -1997,12 +1551,6 @@ dependencies = [
"digest",
]
[[package]]
name = "siphasher"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "slab"
version = "0.4.9"
@ -2012,15 +1560,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "smallstr"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e922794d168678729ffc7e07182721a14219c65814e66e91b839a272fe5ae4f"
dependencies = [
"smallvec",
]
[[package]]
name = "smallvec"
version = "1.13.2"
@ -2066,19 +1605,6 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "terminfo"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "666cd3a6681775d22b200409aad3b089c5b99fb11ecdd8a204d9d62f8148498f"
dependencies = [
"dirs 4.0.0",
"fnv",
"nom",
"phf",
"phf_codegen",
]
[[package]]
name = "thiserror"
version = "1.0.59"
@ -2305,18 +1831,6 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-width"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
[[package]]
name = "universal-hash"
version = "0.5.1"
@ -2372,12 +1886,6 @@ dependencies = [
"try-lock",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@ -2486,28 +1994,6 @@ dependencies = [
"rustls-pki-types",
]
[[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"

View File

@ -19,13 +19,6 @@ chrono = "0.4"
env_logger = "0.11"
colog = "1.3"
colored = "2.1"
parse_datetime = "0.5.0"
interim = { version = "0.1", features = ["chrono"] }
nostr-sdk = "0.33" # { git = "https://github.com/rust-nostr/nostr" }
nostr-sdk = "0.33"
tokio = { version = "1.37", features = ["rt", "rt-multi-thread", "macros"] }
regex = "1.10.5"
[dev-dependencies]
chrono-english = "0.1"
linefeed = "0.6"
rustyline = { version = "14.0", features = ["custom-bindings"] }

View File

@ -93,23 +93,22 @@ To stop time-tracking completely, simply move to the root of all tasks.
`TASK` creation syntax: `NAME: TAG1 TAG2 ...`
- `TASK` - create task (prefix with space if you want a task to start with a command character)
- `.` - clear filters
- `.` - clear filters and reload
- `.TASK`
+ activate task by id
+ match by task name prefix: if one or more tasks match, filter / activate (tries case-sensitive then case-insensitive)
+ no match: create & activate task
- `.2` - set view depth to the given number (how many subtask levels to show, default is 1)
- `/[TEXT]` - activate task or filter by smart-case substring match
- `.2` - set view depth to `2`, which can be substituted for any number (how many subtask levels to show, default 1)
- `/[TEXT]` - like `.`, but never creates a task and filters beyond currently visible tasks
- `||TASK` - create and activate a new task procedure (where subtasks automatically depend on the previously created task)
- `|[TASK]` - (un)mark current task as procedure or create a sibling task depending on the current one and move up
Dot or slash can be repeated to move to parent tasks before acting.
Dots and slashes can be repeated to move to parent tasks.
- `:[IND][PROP]` - add property column PROP at IND or end, if it already exists remove property column PROP or IND (1-indexed)
- `::[PROP]` - Sort by property PROP (multiple space-separated values allowed)
- `([TIME]` - insert timetracking with the specified offset such as `-1d`, `-15 minutes` or `yesterday 17:20` (empty:
list tracked times)
- `)[TIME]` - stop timetracking with the specified offset - convenience helper to move to root (empty: stop now)
- `([TIME]` - insert timetracking with the specified offset in minutes (empty: list tracked times)
- `)[TIME]` - stop timetracking with the specified offset in minutes - convenience helper to move to root (empty: stop now)
- `>[TEXT]` - complete active task and move up, with optional status description
- `<[TEXT]` - close active task and move up, with optional status description
- `!TEXT` - set status for current task from text and move up (empty to open)
@ -121,9 +120,9 @@ Dot or slash can be repeated to move to parent tasks before acting.
Property Filters:
- `#TAG1 TAG2` - set tag filter (empty: list all used tags)
- `#TAG` - set tag filter (empty: list all used tags)
- `+TAG` - add tag filter
- `-TAG` - remove tag filters by prefix
- `-TAG` - remove tag filters
- `?STATUS` - filter by status (type or description) - plain `?` to reset, `??` to show all
- `@AUTHOR` - filter by author (`@` for self, id prefix, name prefix)
- TBI: Filter by time

View File

@ -1,10 +1,7 @@
use std::fmt::Display;
use std::io::{stdin, stdout, Write};
use chrono::{Local, NaiveDateTime, TimeZone};
use chrono::LocalResult::Single;
use log::{debug, error, info, trace, warn};
use nostr_sdk::Timestamp;
pub fn some_non_empty(str: &str) -> Option<String> {
if str.is_empty() { None } else { Some(str.to_string()) }
@ -30,39 +27,3 @@ pub fn prompt(prompt: &str) -> Option<String> {
}
}
// For use in format strings but not possible, so need global find-replace
pub const MAX_TIMESTAMP_WIDTH: u8 = 15;
/// Format nostr Timestamp relative to local time with optional day specifier or full date if needed
pub fn relative_datetimestamp(stamp: &Timestamp) -> String {
match Local.timestamp_opt(stamp.as_u64() as i64, 0) {
Single(time) => {
let date = time.date_naive();
let prefix = match Local::now()
.date_naive()
.signed_duration_since(date)
.num_days()
{
-1 => "tomorrow ".into(),
0 => "".into(),
1 => "yesterday ".into(),
2..=6 => date.format("last %a ").to_string(),
_ => date.format("%y-%m-%d ").to_string(),
};
format!("{}{}", prefix, time.format("%H:%M"))
}
_ => stamp.to_human_datetime(),
}
}
pub fn local_datetimestamp(stamp: &Timestamp) -> String {
format_stamp(stamp, "%y-%m-%d %a %H:%M")
}
pub fn format_stamp(stamp: &Timestamp, format: &str) -> String {
match Local.timestamp_opt(stamp.as_u64() as i64, 0) {
Single(time) => time.format(format).to_string(),
_ => stamp.to_human_datetime(),
}
}

View File

@ -18,7 +18,6 @@ use env_logger::Builder;
use itertools::Itertools;
use log::{debug, error, info, LevelFilter, trace, warn};
use nostr_sdk::prelude::*;
use nostr_sdk::TagStandard::Hashtag;
use regex::Regex;
use xdg::BaseDirectories;
@ -304,10 +303,13 @@ async fn main() {
println!();
let tasks = selected_relay.as_ref().and_then(|url| relays.get(url)).unwrap_or(&local_tasks);
print!(
"{} {}{}) ",
selected_relay.as_ref().map_or("TEMP".to_string(), |url| url.to_string()).bright_black(),
tasks.get_task_path(tasks.get_position()).bold(),
tasks.get_prompt_suffix().italic(),
"{} {}) ",
selected_relay.as_ref().map_or("TEMP".to_string(), |url| url.to_string()).bright_black().italic(),
format!(
"{}{}",
tasks.get_task_path(tasks.get_position()),
tasks.get_prompt_suffix()
).bold()
);
stdout().flush().unwrap();
match lines.next() {
@ -381,7 +383,7 @@ async fn main() {
None => {
tasks.get_current_task().map_or_else(
|| info!("With a task selected, use ,NOTE to attach NOTE and , to list all its notes"),
|task| println!("{}", task.description_events().map(|e| format!("{} {}", local_datetimestamp(&e.created_at), e.content)).join("\n")),
|task| println!("{}", task.description_events().map(|e| format!("{} {}", e.created_at.to_human_datetime(), e.content)).join("\n")),
);
continue;
}
@ -406,13 +408,13 @@ async fn main() {
match arg {
None => match tasks.get_position() {
None => {
info!("Filtering for Procedures");
tasks.set_filter(
tasks.filtered_tasks(None)
tasks.current_tasks().into_iter()
.filter(|t| t.pure_state() == State::Procedure)
.map(|t| t.event.id)
.collect()
);
info!("Filtering for procedures");
}
Some(id) => {
tasks.set_state_for(id, "", State::Procedure);
@ -454,7 +456,7 @@ async fn main() {
Some('#') =>
match arg {
Some(arg) => tasks.set_tags(arg.split_whitespace().map(|s| Hashtag(s.to_string()).into())),
Some(arg) => tasks.set_tag(arg.to_string()),
None => {
println!("Hashtags of all known tasks:\n{}", tasks.all_hashtags().join(" "));
continue;
@ -499,8 +501,8 @@ async fn main() {
dots += 1;
pos = tasks.get_parent(pos).cloned();
}
let slice = input[dots..].trim();
if pos != tasks.get_position() || slice.is_empty() {
tasks.move_to(pos);
}
@ -522,30 +524,21 @@ async fn main() {
dots += 1;
pos = tasks.get_parent(pos).cloned();
}
let slice = &input[dots..].trim().to_ascii_lowercase();
let slice = input[dots..].trim();
if slice.is_empty() {
tasks.move_to(pos);
if dots > 1 {
info!("Moving up {} tasks", dots - 1)
}
} else if let Ok(depth) = slice.parse::<i8>() {
tasks.move_to(pos);
tasks.set_depth(depth);
} else {
let mut transform: Box<dyn Fn(&str) -> String> = Box::new(|s: &str| s.to_string());
if slice.chars().find(|c| c.is_ascii_uppercase()).is_none() {
// Smart-case - case-sensitive if any uppercase char is entered
transform = Box::new(|s| s.to_ascii_lowercase());
}
let filtered = tasks.filtered_tasks(pos)
.filter(|t| {
transform(&t.event.content).contains(slice) || t.tags.iter().flatten().any(|tag|
tag.content().is_some_and(|s| transform(s).contains(slice))
)
})
let filtered = tasks
.children_of(pos)
.into_iter()
.filter_map(|child| tasks.get_by_id(&child))
.filter(|t| t.event.content.to_ascii_lowercase().starts_with(slice))
.map(|t| t.event.id)
.collect_vec();
.collect::<Vec<_>>();
if filtered.len() == 1 {
tasks.move_to(filtered.into_iter().nth(0));
} else {

View File

@ -7,7 +7,8 @@ use std::str::FromStr;
use std::sync::mpsc::Sender;
use std::time::Duration;
use chrono::Local;
use chrono::{DateTime, Local, TimeZone};
use chrono::LocalResult::Single;
use colored::Colorize;
use itertools::Itertools;
use log::{debug, error, info, trace, warn};
@ -15,8 +16,8 @@ use nostr_sdk::{Event, EventBuilder, EventId, Keys, Kind, PublicKey, Tag, TagSta
use nostr_sdk::prelude::Marker;
use TagStandard::Hashtag;
use crate::{EventSender, MostrMessage};
use crate::helpers::{format_stamp, local_datetimestamp, relative_datetimestamp, some_non_empty};
use crate::{Events, EventSender, MostrMessage};
use crate::helpers::some_non_empty;
use crate::kinds::*;
use crate::task::{MARKER_DEPENDS, MARKER_PARENT, State, Task, TaskState};
@ -127,7 +128,6 @@ impl Tasks {
],
sorting: VecDeque::from([
"state".into(),
"hashtags".into(),
"rtime".into(),
"name".into(),
]),
@ -182,7 +182,7 @@ impl Tasks {
.map(|str| EventId::from_str(str).ok().map_or(str.to_string(), |id| self.get_task_title(&id)))
.join(" "));
if new != last {
full.push_str(&format!("{:>15} {}\n", relative_datetimestamp(&event.created_at), new.as_ref().unwrap_or(&"---".to_string())));
full.push_str(&format!("{} {}\n", event.created_at.to_human_datetime(), new.as_ref().unwrap_or(&"---".to_string())));
last = new;
}
}
@ -198,21 +198,12 @@ impl Tasks {
let mut vec = Vec::with_capacity(set.len() / 2);
let mut iter = timestamps(set.iter(), &ids).tuples();
while let Some(((start, _), (end, _))) = iter.next() {
vec.push(format!("{} - {} by {}",
local_datetimestamp(start),
// Only use full stamp when ambiguous (>1day)
if end.as_u64() - start.as_u64() > 86400 {
local_datetimestamp(end)
} else {
format_stamp(end, "%H:%M")
},
key))
vec.push(format!("{} - {} by {}", start.to_human_datetime(), end.to_human_datetime(), key))
}
iter.into_buffer()
.for_each(|(stamp, _)|
vec.push(format!("{} started by {}", local_datetimestamp(stamp), key)));
iter.into_buffer().for_each(|(stamp, _)|
vec.push(format!("{} started by {}", stamp.to_human_datetime(), key)));
vec
}).sorted_unstable() // TODO sorting depends on timestamp format - needed to interleave different people
}).sorted_unstable()
).join("\n")
}
}
@ -353,9 +344,17 @@ impl Tasks {
.map(|t| t.get_id())
}
pub(crate) fn filtered_tasks(&self, position: Option<EventId>) -> impl Iterator<Item=&Task> {
pub(crate) fn current_tasks(&self) -> Vec<&Task> {
if self.depth == 0 {
return self.get_current_task().into_iter().collect();
}
let res: Vec<&Task> = self.resolve_tasks(self.view.iter());
if res.len() > 0 {
// Currently ignores filtered view when it matches nothing
return res;
}
// TODO use ChildrenIterator
self.resolve_tasks(self.children_of(position)).into_iter()
self.resolve_tasks(self.children_of(self.position)).into_iter()
.filter(|t| {
// TODO apply filters in transit
self.state.matches(t) &&
@ -368,39 +367,36 @@ impl Tasks {
self.tags.iter().all(|tag| iter.any(|t| t == tag))
}))
})
}
pub(crate) fn visible_tasks(&self) -> Vec<&Task> {
if self.depth == 0 {
return self.get_current_task().into_iter().collect();
}
if self.view.len() > 0 {
return self.resolve_tasks(self.view.iter());
}
self.filtered_tasks(self.position).collect()
.collect()
}
pub(crate) fn print_tasks(&self) -> Result<(), Error> {
let mut lock = stdout().lock();
if let Some(t) = self.get_current_task() {
let state = t.state_or_default();
let now = &Timestamp::now();
let mut tracking_stamp: Option<Timestamp> = None;
for elem in
timestamps(self.history.get(&self.sender.pubkey()).into_iter().flatten(), &vec![t.get_id()])
.map(|(e, o)| e) {
if tracking_stamp.is_some() && elem > now {
break;
}
tracking_stamp = Some(elem.clone())
}
writeln!(
lock,
"Tracking since {} (total tracked time {}m) - {} since {}",
tracking_stamp.map_or("?".to_string(), |t| relative_datetimestamp(&t)),
self.time_tracked(*t.get_id()) / 60,
"{} since {} (total tracked time {}m)",
// TODO tracking since, scheduled/planned for
state.get_label(),
relative_datetimestamp(&state.time)
match Local.timestamp_opt(state.time.as_u64() as i64, 0) {
Single(time) => {
let date = time.date_naive();
let prefix = match Local::now()
.date_naive()
.signed_duration_since(date)
.num_days()
{
0 => "".into(),
1 => "yesterday ".into(),
2..=6 => date.format("%a ").to_string(),
_ => date.format("%y-%m-%d ").to_string(),
};
format!("{}{}", prefix, time.format("%H:%M"))
}
_ => state.time.to_human_datetime(),
},
self.time_tracked(*t.get_id()) / 60
)?;
writeln!(lock, "{}", t.descriptions().join("\n"))?;
}
@ -408,7 +404,7 @@ impl Tasks {
// TODO hide empty columns
writeln!(lock, "{}", self.properties.join("\t").bold())?;
let mut total_time = 0;
let mut tasks = self.visible_tasks();
let mut tasks = self.current_tasks();
let count = tasks.len();
tasks.sort_by_cached_key(|task| {
self.sorting
@ -479,9 +475,6 @@ impl Tasks {
// Movement and Selection
pub(crate) fn set_filter(&mut self, view: Vec<EventId>) {
if view.is_empty() {
warn!("No match for filter!")
}
self.view = view;
}
@ -492,10 +485,10 @@ impl Tasks {
info!("Removed all filters");
}
pub(crate) fn set_tags(&mut self, tags: impl IntoIterator<Item=Tag>) {
pub(crate) fn set_tag(&mut self, tag: String) {
self.tags_excluded.clear();
self.tags.clear();
self.tags.extend(tags);
self.add_tag(tag);
}
pub(crate) fn add_tag(&mut self, tag: String) {
@ -537,10 +530,11 @@ impl Tasks {
if let Ok(id) = EventId::parse(arg) {
return vec![id];
}
let mut filtered: Vec<EventId> = Vec::with_capacity(32);
let tasks = self.current_tasks();
let mut filtered: Vec<EventId> = Vec::with_capacity(tasks.len());
let lowercase_arg = arg.to_ascii_lowercase();
let mut filtered_more: Vec<EventId> = Vec::with_capacity(32);
for task in self.filtered_tasks(self.position) {
let mut filtered_more: Vec<EventId> = Vec::with_capacity(tasks.len());
for task in tasks {
let lowercase = task.event.content.to_ascii_lowercase();
if lowercase == lowercase_arg {
return vec![task.event.id];
@ -567,7 +561,7 @@ impl Tasks {
if arg.len() > 2 {
Some(self.make_task(arg))
} else {
warn!("Name of a task needs to have at least 3 characters");
warn!("Not creating task under 3 chars to avoid silly mistakes");
None
}
}
@ -699,38 +693,21 @@ impl Tasks {
}
/// Parse string and set tracking
/// Returns false and prints a message if parsing failed
/// Returns false if parsing failed
pub(crate) fn track_from(&mut self, str: &str) -> bool {
// Using two libraries for better exhaustiveness, see https://github.com/uutils/parse_datetime/issues/84
let stripped = str.trim().trim_start_matches('+').trim_start_matches("in ");
if let Ok(num) = stripped.parse::<i64>() {
if let Ok(num) = str.parse::<i64>() {
self.track_at(Timestamp::from(Timestamp::now().as_u64().saturating_add_signed(num * 60)));
return true
}
match interim::parse_date_string(stripped, Local::now(), interim::Dialect::Us) {
Ok(date) => Some(date.to_utc()),
Err(e) => {
match parse_datetime::parse_datetime_at_date(Local::now(), str) {
Ok(date) => Some(date.to_utc()),
Err(_) => {
warn!("Could not parse time from {str}: {e}");
None
}
}
}
}.filter(|time| {
if time.timestamp() > 0 {
self.track_at(Timestamp::from(time.timestamp() as u64));
true
} else if let Ok(date) = DateTime::parse_from_rfc3339(str) {
self.track_at(Timestamp::from(date.to_utc().timestamp() as u64));
} else {
warn!("Can only track times after 1970!");
false
warn!("Cannot parse time from {str}");
return false;
}
}).is_some()
true
}
pub(crate) fn track_at(&mut self, time: Timestamp) -> EventId {
info!("{} from {}", self.position.map_or(String::from("Stopping time-tracking"), |id| format!("Tracking \"{}\"", self.get_task_title(&id))), relative_datetimestamp(&time));
info!("{} from {}", self.position.map_or(String::from("Stopping time-tracking"), |id| format!("Tracking \"{}\"", self.get_task_title(&id))), time.to_human_datetime());
let pos = self.get_position();
let tracking = build_tracking(pos);
// TODO this can lead to funny deletions
@ -944,7 +921,6 @@ fn matching_tag_id<'a>(event: &'a Event, ids: &'a Vec<&'a EventId>) -> Option<&'
})
}
/// Filters out event timestamps to those that start or stop one of the given events
fn timestamps<'a>(events: impl Iterator<Item=&'a Event>, ids: &'a Vec<&'a EventId>) -> impl Iterator<Item=(&Timestamp, Option<&EventId>)> {
events.map(|event| (&event.created_at, matching_tag_id(event, ids)))
.dedup_by(|(_, e1), (_, e2)| e1 == e2)
@ -1131,59 +1107,59 @@ mod tasks_test {
assert_eq!(tasks.depth, 1);
assert_eq!(task1.pure_state(), State::Open);
debug!("{:?}", tasks);
assert_eq!(tasks.visible_tasks().len(), 1);
assert_eq!(tasks.current_tasks().len(), 1);
tasks.depth = 0;
assert_eq!(tasks.visible_tasks().len(), 0);
assert_eq!(tasks.current_tasks().len(), 0);
tasks.move_to(Some(t1));
tasks.depth = 2;
assert_eq!(tasks.visible_tasks().len(), 0);
assert_eq!(tasks.current_tasks().len(), 0);
let t2 = tasks.make_task("t2");
assert_eq!(tasks.visible_tasks().len(), 1);
assert_eq!(tasks.current_tasks().len(), 1);
assert_eq!(tasks.get_task_path(Some(t2)), "t1>t2");
assert_eq!(tasks.relative_path(t2), "t2");
let t3 = tasks.make_task("t3");
assert_eq!(tasks.visible_tasks().len(), 2);
assert_eq!(tasks.current_tasks().len(), 2);
tasks.move_to(Some(t2));
assert_eq!(tasks.visible_tasks().len(), 0);
assert_eq!(tasks.current_tasks().len(), 0);
let t4 = tasks.make_task("t4");
assert_eq!(tasks.visible_tasks().len(), 1);
assert_eq!(tasks.current_tasks().len(), 1);
assert_eq!(tasks.get_task_path(Some(t4)), "t1>t2>t4");
assert_eq!(tasks.relative_path(t4), "t4");
tasks.depth = 2;
assert_eq!(tasks.visible_tasks().len(), 1);
assert_eq!(tasks.current_tasks().len(), 1);
tasks.depth = -1;
assert_eq!(tasks.visible_tasks().len(), 1);
assert_eq!(tasks.current_tasks().len(), 1);
tasks.move_to(Some(t1));
assert_eq!(tasks.relative_path(t4), "t2>t4");
assert_eq!(tasks.visible_tasks().len(), 2);
assert_eq!(tasks.current_tasks().len(), 2);
tasks.depth = 2;
assert_eq!(tasks.visible_tasks().len(), 3);
assert_eq!(tasks.current_tasks().len(), 3);
tasks.set_filter(vec![t2]);
assert_eq!(tasks.visible_tasks().len(), 2);
assert_eq!(tasks.current_tasks().len(), 2);
tasks.depth = 1;
assert_eq!(tasks.visible_tasks().len(), 1);
assert_eq!(tasks.current_tasks().len(), 1);
tasks.depth = -1;
assert_eq!(tasks.visible_tasks().len(), 1);
assert_eq!(tasks.current_tasks().len(), 1);
tasks.set_filter(vec![t2, t3]);
assert_eq!(tasks.visible_tasks().len(), 2);
assert_eq!(tasks.current_tasks().len(), 2);
tasks.depth = 2;
assert_eq!(tasks.visible_tasks().len(), 3);
assert_eq!(tasks.current_tasks().len(), 3);
tasks.depth = 1;
assert_eq!(tasks.visible_tasks().len(), 2);
assert_eq!(tasks.current_tasks().len(), 2);
tasks.move_to(None);
assert_eq!(tasks.visible_tasks().len(), 1);
assert_eq!(tasks.current_tasks().len(), 1);
tasks.depth = 2;
assert_eq!(tasks.visible_tasks().len(), 3);
assert_eq!(tasks.current_tasks().len(), 3);
tasks.depth = 3;
assert_eq!(tasks.visible_tasks().len(), 4);
assert_eq!(tasks.current_tasks().len(), 4);
tasks.depth = 9;
assert_eq!(tasks.visible_tasks().len(), 4);
assert_eq!(tasks.current_tasks().len(), 4);
tasks.depth = -1;
assert_eq!(tasks.visible_tasks().len(), 2);
assert_eq!(tasks.current_tasks().len(), 2);
}
#[test]