aboutsummaryrefslogtreecommitdiffstats
path: root/pkgs/by-name/ts
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--pkgs/by-name/ts/tskm/.envrc14
-rw-r--r--pkgs/by-name/ts/tskm/.gitignore10
-rw-r--r--pkgs/by-name/ts/tskm/Cargo.lock1134
-rw-r--r--pkgs/by-name/ts/tskm/Cargo.toml58
-rw-r--r--pkgs/by-name/ts/tskm/build.rs49
-rw-r--r--pkgs/by-name/ts/tskm/flake.lock27
-rw-r--r--pkgs/by-name/ts/tskm/flake.nix32
-rw-r--r--pkgs/by-name/ts/tskm/package.nix23
-rw-r--r--pkgs/by-name/ts/tskm/src/browser/mod.rs217
-rw-r--r--pkgs/by-name/ts/tskm/src/cli.rs295
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/input/handle.rs131
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/input/mod.rs189
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/mod.rs10
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs66
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/neorg/mod.rs21
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/open/handle.rs293
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/open/mod.rs240
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/project/handle.rs22
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/project/mod.rs10
-rw-r--r--pkgs/by-name/ts/tskm/src/main.rs68
-rw-r--r--pkgs/by-name/ts/tskm/src/rofi/mod.rs10
-rw-r--r--pkgs/by-name/ts/tskm/src/state.rs53
-rw-r--r--pkgs/by-name/ts/tskm/src/task/mod.rs209
-rwxr-xr-xpkgs/by-name/ts/tskm/update.sh13
24 files changed, 2282 insertions, 912 deletions
diff --git a/pkgs/by-name/ts/tskm/.envrc b/pkgs/by-name/ts/tskm/.envrc
index d21a17fc..a84d550d 100644
--- a/pkgs/by-name/ts/tskm/.envrc
+++ b/pkgs/by-name/ts/tskm/.envrc
@@ -1,8 +1,18 @@
#!/usr/bin/env sh
-SHELL_COMPLETION_DIR="$(pwd)/target/shell"
-export SHELL_COMPLETION_DIR
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
export TSKM_PROJECT_FILE=/home/soispha/repos/nix/config/modules/common/projects.json
use flake
+
+PATH_add ./target/debug
+PATH_add ./target/release
diff --git a/pkgs/by-name/ts/tskm/.gitignore b/pkgs/by-name/ts/tskm/.gitignore
index 2d5df85d..f255eebd 100644
--- a/pkgs/by-name/ts/tskm/.gitignore
+++ b/pkgs/by-name/ts/tskm/.gitignore
@@ -1,2 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
/target
.direnv
diff --git a/pkgs/by-name/ts/tskm/Cargo.lock b/pkgs/by-name/ts/tskm/Cargo.lock
index 4a064858..20af41dc 100644
--- a/pkgs/by-name/ts/tskm/Cargo.lock
+++ b/pkgs/by-name/ts/tskm/Cargo.lock
@@ -1,12 +1,21 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
version = 4
[[package]]
-name = "android-tzdata"
-version = "0.1.1"
+name = "adler2"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "android_system_properties"
@@ -19,9 +28,9 @@ dependencies = [
[[package]]
name = "anstream"
-version = "0.6.18"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -34,97 +43,121 @@ dependencies = [
[[package]]
name = "anstyle"
-version = "1.0.10"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
[[package]]
name = "anstyle-parse"
-version = "0.2.6"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
+checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
-version = "1.1.2"
+version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
+checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
name = "anstyle-wincon"
-version = "3.0.7"
+version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
+checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
- "once_cell",
- "windows-sys",
+ "once_cell_polyfill",
+ "windows-sys 0.61.2",
]
[[package]]
name = "anyhow"
-version = "1.0.97"
+version = "1.0.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
+
+[[package]]
+name = "arraydeque"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
+checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
+
+[[package]]
+name = "async-trait"
+version = "0.1.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
[[package]]
name = "autocfg"
-version = "1.4.0"
+version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "bitflags"
-version = "2.9.0"
+version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
+checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a"
[[package]]
name = "bumpalo"
-version = "3.17.0"
+version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
+checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
-version = "1.2.17"
+version = "1.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
+checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
dependencies = [
- "shlex",
+ "find-msvc-tools",
+ "shlex 2.0.1",
]
[[package]]
name = "cfg-if"
-version = "1.0.0"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chrono"
-version = "0.4.40"
+version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
+checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
dependencies = [
- "android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
+ "serde",
"wasm-bindgen",
"windows-link",
]
[[package]]
name = "clap"
-version = "4.5.34"
+version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e958897981290da2a852763fe9cdb89cd36977a5d729023127095fa94d95e2ff"
+checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
dependencies = [
"clap_builder",
"clap_derive",
@@ -132,9 +165,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.34"
+version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83b0f35019843db2160b5bb19ae09b4e6411ac33fc6a712003c33e03090e2489"
+checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
dependencies = [
"anstream",
"anstyle",
@@ -144,18 +177,21 @@ dependencies = [
[[package]]
name = "clap_complete"
-version = "4.5.47"
+version = "4.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06f5378ea264ad4f82bbc826628b5aad714a75abf6ece087e923010eb937fb6"
+checksum = "e0a7a9bfdb35811f9e59832f0f05975114d2251b415fb534108e6f34060fd772"
dependencies = [
"clap",
+ "clap_lex",
+ "is_executable",
+ "shlex 1.3.0",
]
[[package]]
name = "clap_derive"
-version = "4.5.32"
+version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
+checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
dependencies = [
"heck",
"proc-macro2",
@@ -165,15 +201,15 @@ dependencies = [
[[package]]
name = "clap_lex"
-version = "0.7.4"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]]
name = "colorchoice"
-version = "1.0.3"
+version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]]
name = "core-foundation-sys"
@@ -182,6 +218,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
+name = "crc32fast"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
name = "dirs"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -199,14 +244,14 @@ dependencies = [
"libc",
"option-ext",
"redox_users",
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
name = "displaydoc"
-version = "0.2.5"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
dependencies = [
"proc-macro2",
"quote",
@@ -214,19 +259,98 @@ dependencies = [
]
[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "fallible-iterator"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+
+[[package]]
+name = "flate2"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+
+[[package]]
+name = "foldhash"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
+
+[[package]]
name = "form_urlencoded"
-version = "1.2.1"
+version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
dependencies = [
"percent-encoding",
]
[[package]]
+name = "futures-core"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
+
+[[package]]
+name = "futures-task"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
+
+[[package]]
+name = "futures-util"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
name = "getrandom"
-version = "0.2.15"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
@@ -234,6 +358,54 @@ dependencies = [
]
[[package]]
+name = "getrandom"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "r-efi",
+ "wasip2",
+ "wasip3",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.15.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
+dependencies = [
+ "foldhash 0.1.5",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+dependencies = [
+ "foldhash 0.2.0",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
+
+[[package]]
+name = "hashlink"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230"
+dependencies = [
+ "hashbrown 0.16.1",
+]
+
+[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -241,15 +413,15 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
-version = "0.5.0"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e"
+checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "iana-time-zone"
-version = "0.1.62"
+version = "0.1.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2fd658b06e56721792c5df4475705b6cda790e9298d19d2f8af083457bcd127"
+checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
dependencies = [
"android_system_properties",
"core-foundation-sys",
@@ -271,21 +443,23 @@ dependencies = [
[[package]]
name = "icu_collections"
-version = "1.5.0"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
+checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
dependencies = [
"displaydoc",
+ "potential_utf",
+ "utf8_iter",
"yoke",
"zerofrom",
"zerovec",
]
[[package]]
-name = "icu_locid"
-version = "1.5.0"
+name = "icu_locale_core"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
+checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
dependencies = [
"displaydoc",
"litemap",
@@ -295,103 +469,71 @@ dependencies = [
]
[[package]]
-name = "icu_locid_transform"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
-dependencies = [
- "displaydoc",
- "icu_locid",
- "icu_locid_transform_data",
- "icu_provider",
- "tinystr",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid_transform_data"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d"
-
-[[package]]
name = "icu_normalizer"
-version = "1.5.0"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
+checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
dependencies = [
- "displaydoc",
"icu_collections",
"icu_normalizer_data",
"icu_properties",
"icu_provider",
"smallvec",
- "utf16_iter",
- "utf8_iter",
- "write16",
"zerovec",
]
[[package]]
name = "icu_normalizer_data"
-version = "1.5.1"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7"
+checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
[[package]]
name = "icu_properties"
-version = "1.5.1"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
+checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
dependencies = [
- "displaydoc",
"icu_collections",
- "icu_locid_transform",
+ "icu_locale_core",
"icu_properties_data",
"icu_provider",
- "tinystr",
+ "zerotrie",
"zerovec",
]
[[package]]
name = "icu_properties_data"
-version = "1.5.1"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2"
+checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
[[package]]
name = "icu_provider"
-version = "1.5.0"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
+checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
dependencies = [
"displaydoc",
- "icu_locid",
- "icu_provider_macros",
- "stable_deref_trait",
- "tinystr",
+ "icu_locale_core",
"writeable",
"yoke",
"zerofrom",
+ "zerotrie",
"zerovec",
]
[[package]]
-name = "icu_provider_macros"
-version = "1.5.0"
+name = "id-arena"
+version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
+checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "idna"
-version = "1.0.3"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
dependencies = [
"idna_adapter",
"smallvec",
@@ -400,89 +542,134 @@ dependencies = [
[[package]]
name = "idna_adapter"
-version = "1.2.0"
+version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
+checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714"
dependencies = [
"icu_normalizer",
"icu_properties",
]
[[package]]
+name = "indexmap"
+version = "2.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.17.1",
+ "serde",
+ "serde_core",
+]
+
+[[package]]
name = "is-terminal"
-version = "0.4.16"
+version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
+checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
dependencies = [
"hermit-abi",
"libc",
- "windows-sys",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "is_executable"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baabb8b4867b26294d818bf3f651a454b6901431711abb96e296245888d6e8c4"
+dependencies = [
+ "windows-sys 0.60.2",
]
[[package]]
name = "is_terminal_polyfill"
-version = "1.70.1"
+version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "itoa"
-version = "1.0.15"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "js-sys"
-version = "0.3.77"
+version = "0.3.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
+checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11"
dependencies = [
+ "cfg-if",
+ "futures-util",
"once_cell",
"wasm-bindgen",
]
[[package]]
+name = "leb128fmt"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
+
+[[package]]
name = "libc"
-version = "0.2.171"
+version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "libredox"
-version = "0.1.3"
+version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
+checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3"
dependencies = [
- "bitflags",
"libc",
]
[[package]]
+name = "libsqlite3-sys"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f111c8c41e7c61a49cd34e44c7619462967221a6443b0ec299e0ac30cfb9b1"
+dependencies = [
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
name = "litemap"
-version = "0.7.5"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
+checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
[[package]]
name = "log"
-version = "0.4.27"
+version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+checksum = "113b30b4cd05f7c06868fdb2854f66a7b9fece9a48425351cd532e810d74024f"
[[package]]
-name = "lz4_flex"
-version = "0.11.3"
+name = "md5"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5"
-dependencies = [
- "twox-hash",
-]
+checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0"
[[package]]
name = "memchr"
-version = "2.7.4"
+version = "2.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
[[package]]
name = "num-traits"
@@ -495,9 +682,15 @@ dependencies = [
[[package]]
name = "once_cell"
-version = "1.21.3"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
+[[package]]
+name = "once_cell_polyfill"
+version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "option-ext"
@@ -507,50 +700,106 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "percent-encoding"
-version = "2.3.1"
+version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
+
+[[package]]
+name = "potential_utf"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "prettyplease"
+version = "0.2.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
[[package]]
name = "proc-macro2"
-version = "1.0.94"
+version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.40"
+version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
+name = "r-efi"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
+
+[[package]]
name = "redox_users"
-version = "0.5.0"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
+checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [
- "getrandom",
+ "getrandom 0.2.17",
"libredox",
"thiserror",
]
[[package]]
-name = "rustversion"
-version = "1.0.20"
+name = "rsqlite-vfs"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
+checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c"
+dependencies = [
+ "hashbrown 0.16.1",
+ "thiserror",
+]
[[package]]
-name = "ryu"
-version = "1.0.20"
+name = "rusqlite"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0d2b0146dd9661bf67bb107c0bb2a55064d556eeb3fc314151b957f313bcd4e"
+dependencies = [
+ "bitflags",
+ "fallible-iterator",
+ "fallible-streaming-iterator",
+ "hashlink",
+ "libsqlite3-sys",
+ "smallvec",
+ "sqlite-wasm-rs",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "same-file"
@@ -562,19 +811,46 @@ dependencies = [
]
[[package]]
+name = "semver"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
+
+[[package]]
name = "serde"
-version = "1.0.219"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde-wasm-bindgen"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
+dependencies = [
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.219"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
@@ -583,14 +859,15 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.140"
+version = "1.0.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
+checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
dependencies = [
"itoa",
"memchr",
- "ryu",
"serde",
+ "serde_core",
+ "zmij",
]
[[package]]
@@ -600,22 +877,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
+name = "shlex"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
+
+[[package]]
+name = "simd-adler32"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
+
+[[package]]
+name = "slab"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
+
+[[package]]
name = "smallvec"
-version = "1.14.0"
+version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
-name = "stable_deref_trait"
-version = "1.2.0"
+name = "sqlite-wasm-rs"
+version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+checksum = "dc3efc0da82635d7e1ced0053bbbfa8c7ab9645d0bf36ceb4f7127bb85315d75"
+dependencies = [
+ "cc",
+ "js-sys",
+ "rsqlite-vfs",
+ "wasm-bindgen",
+]
[[package]]
-name = "static_assertions"
-version = "1.1.0"
+name = "stable_deref_trait"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "stderrlog"
@@ -623,7 +924,6 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c910772f992ab17d32d6760e167d2353f4130ed50e796752689556af07dc6b"
dependencies = [
- "chrono",
"is-terminal",
"log",
"termcolor",
@@ -637,10 +937,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
+name = "strum"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd"
+
+[[package]]
+name = "strum_macros"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "syn"
-version = "2.0.100"
+version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
@@ -649,9 +967,9 @@ dependencies = [
[[package]]
name = "synstructure"
-version = "0.13.1"
+version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
@@ -659,6 +977,32 @@ dependencies = [
]
[[package]]
+name = "taskchampion"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82f3be5bd922568eaaa1cbf30d4daf7979723c53465f3b202a88c7746fd0d7b6"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "byteorder",
+ "chrono",
+ "flate2",
+ "getrandom 0.4.2",
+ "log",
+ "rusqlite",
+ "serde",
+ "serde-wasm-bindgen",
+ "serde_json",
+ "strum",
+ "strum_macros",
+ "thiserror",
+ "tokio",
+ "uuid",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+]
+
+[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -669,18 +1013,18 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "2.0.12"
+version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "2.0.12"
+version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
@@ -689,25 +1033,45 @@ dependencies = [
[[package]]
name = "thread_local"
-version = "1.1.8"
+version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
"cfg-if",
- "once_cell",
]
[[package]]
name = "tinystr"
-version = "0.7.6"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
dependencies = [
"displaydoc",
"zerovec",
]
[[package]]
+name = "tokio"
+version = "1.52.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
+dependencies = [
+ "pin-project-lite",
+ "tokio-macros",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "tskm"
version = "0.1.0"
dependencies = [
@@ -716,49 +1080,43 @@ dependencies = [
"clap_complete",
"dirs",
"log",
- "lz4_flex",
+ "md5",
"serde",
"serde_json",
"stderrlog",
+ "taskchampion",
+ "tokio",
"url",
"walkdir",
+ "yaml-rust2",
]
[[package]]
-name = "twox-hash"
-version = "1.6.3"
+name = "unicode-ident"
+version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
-dependencies = [
- "cfg-if",
- "static_assertions",
-]
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
-name = "unicode-ident"
-version = "1.0.18"
+name = "unicode-xid"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "url"
-version = "2.5.4"
+version = "2.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
+checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
+ "serde_derive",
]
[[package]]
-name = "utf16_iter"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
-
-[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -771,6 +1129,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
+name = "uuid"
+version = "1.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7"
+dependencies = [
+ "getrandom 0.4.2",
+ "js-sys",
+ "serde_core",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -782,41 +1158,56 @@ dependencies = [
[[package]]
name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wasip2"
+version = "1.0.3+wasi-0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
+dependencies = [
+ "wit-bindgen 0.57.1",
+]
+
+[[package]]
+name = "wasip3"
+version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
+dependencies = [
+ "wit-bindgen 0.51.0",
+]
[[package]]
name = "wasm-bindgen"
-version = "0.2.100"
+version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
+checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
+ "wasm-bindgen-shared",
]
[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.100"
+name = "wasm-bindgen-futures"
+version = "0.4.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
+checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f"
dependencies = [
- "bumpalo",
- "log",
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-shared",
+ "js-sys",
+ "wasm-bindgen",
]
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.100"
+version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
+checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -824,65 +1215,153 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.100"
+version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
+checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e"
dependencies = [
+ "bumpalo",
"proc-macro2",
"quote",
"syn",
- "wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.100"
+version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437"
dependencies = [
"unicode-ident",
]
[[package]]
+name = "wasm-encoder"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
+dependencies = [
+ "leb128fmt",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasm-metadata"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
+dependencies = [
+ "anyhow",
+ "indexmap",
+ "wasm-encoder",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasmparser"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
+dependencies = [
+ "bitflags",
+ "hashbrown 0.15.5",
+ "indexmap",
+ "semver",
+]
+
+[[package]]
name = "winapi-util"
-version = "0.1.9"
+version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
name = "windows-core"
-version = "0.52.0"
+version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
dependencies = [
- "windows-targets",
+ "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.1.1"
+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 = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
+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.59.0"
+version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets",
]
[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
name = "windows-targets"
-version = "0.52.6"
+version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
+ "windows-link",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
@@ -895,71 +1374,169 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.52.6"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.52.6"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
-version = "0.52.6"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]]
name = "windows_i686_gnullvm"
-version = "0.52.6"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
-version = "0.52.6"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.52.6"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.52.6"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.52.6"
+version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
-name = "write16"
-version = "1.0.0"
+name = "wit-bindgen"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
+dependencies = [
+ "wit-bindgen-rust-macro",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.57.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
+
+[[package]]
+name = "wit-bindgen-core"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
+dependencies = [
+ "anyhow",
+ "heck",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-bindgen-rust"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
+dependencies = [
+ "anyhow",
+ "heck",
+ "indexmap",
+ "prettyplease",
+ "syn",
+ "wasm-metadata",
+ "wit-bindgen-core",
+ "wit-component",
+]
+
+[[package]]
+name = "wit-bindgen-rust-macro"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
+dependencies = [
+ "anyhow",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wit-bindgen-core",
+ "wit-bindgen-rust",
+]
+
+[[package]]
+name = "wit-component"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
+dependencies = [
+ "anyhow",
+ "bitflags",
+ "indexmap",
+ "log",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "wasm-encoder",
+ "wasm-metadata",
+ "wasmparser",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-parser"
+version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
+checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
+dependencies = [
+ "anyhow",
+ "id-arena",
+ "indexmap",
+ "log",
+ "semver",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "unicode-xid",
+ "wasmparser",
+]
[[package]]
name = "writeable"
-version = "0.5.5"
+version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
+
+[[package]]
+name = "yaml-rust2"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "631a50d867fafb7093e709d75aaee9e0e0d5deb934021fcea25ac2fe09edc51e"
+dependencies = [
+ "arraydeque",
+ "encoding_rs",
+ "hashlink",
+]
[[package]]
name = "yoke"
-version = "0.7.5"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
+checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
dependencies = [
- "serde",
"stable_deref_trait",
"yoke-derive",
"zerofrom",
@@ -967,9 +1544,9 @@ dependencies = [
[[package]]
name = "yoke-derive"
-version = "0.7.5"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
+checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
dependencies = [
"proc-macro2",
"quote",
@@ -979,18 +1556,18 @@ dependencies = [
[[package]]
name = "zerofrom"
-version = "0.1.6"
+version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
+checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
dependencies = [
"zerofrom-derive",
]
[[package]]
name = "zerofrom-derive"
-version = "0.1.6"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
+checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
dependencies = [
"proc-macro2",
"quote",
@@ -999,10 +1576,21 @@ dependencies = [
]
[[package]]
+name = "zerotrie"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
name = "zerovec"
-version = "0.10.4"
+version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
+checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
dependencies = [
"yoke",
"zerofrom",
@@ -1011,11 +1599,17 @@ dependencies = [
[[package]]
name = "zerovec-derive"
-version = "0.10.3"
+version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
+checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
+
+[[package]]
+name = "zmij"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
diff --git a/pkgs/by-name/ts/tskm/Cargo.toml b/pkgs/by-name/ts/tskm/Cargo.toml
index d2990b0c..c934ce19 100644
--- a/pkgs/by-name/ts/tskm/Cargo.toml
+++ b/pkgs/by-name/ts/tskm/Cargo.toml
@@ -1,3 +1,13 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
[package]
name = "tskm"
version = "0.1.0"
@@ -6,16 +16,31 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
-anyhow = "1.0.97"
-clap = { version = "4.5.34", features = ["derive"] }
-dirs = "6.0.0"
-log = "0.4.27"
-lz4_flex = "0.11.3"
-serde = { version = "1.0.219", features = ["derive"] }
-serde_json = "1.0.140"
-stderrlog = "0.6.0"
-url = { version = "2.5.4", features = ["serde"] }
-walkdir = "2.5.0"
+anyhow = { version = "1.0.102", default-features = false }
+clap = { version = "4.6.1", features = [
+ "derive",
+ "std",
+ "color",
+ "help",
+ "usage",
+ "error-context",
+ "suggestions",
+], default-features = false }
+clap_complete = { version = "4.6.5", features = ["unstable-dynamic"] }
+dirs = { version = "6.0.0", default-features = false }
+log = { version = "0.4.31", default-features = false }
+serde = { version = "1.0.228", features = ["derive"], default-features = false }
+serde_json = { version = "1.0.150", default-features = false }
+stderrlog = { version = "0.6.0", default-features = false }
+taskchampion = { version = "3.1.0", features =["storage", "storage-sqlite"], default-features = false }
+url = { version = "2.5.8", features = [
+ "serde",
+ "std",
+], default-features = false }
+walkdir = { version = "2.5.0", default-features = false }
+md5 = { version = "0.8.0", default-features = false }
+yaml-rust2 = "0.11.0"
+tokio = { version = "1.52.3", features = ["rt-multi-thread"] }
[profile.release]
lto = true
@@ -74,14 +99,5 @@ style = { level = "warn", priority = -1 }
complexity = { level = "warn", priority = -1 }
perf = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
-
-[build-dependencies]
-anyhow = "1.0.97"
-clap = { version = "4.5.34", features = ["derive"] }
-clap_complete = "4.5.47"
-dirs = "6.0.0"
-log = "0.4.27"
-serde = { version = "1.0.219", features = ["derive"] }
-serde_json = "1.0.140"
-url = "2.5.4"
-walkdir = "2.5.0"
+missing_panics_doc = "allow"
+missing_errors_doc = "allow"
diff --git a/pkgs/by-name/ts/tskm/build.rs b/pkgs/by-name/ts/tskm/build.rs
deleted file mode 100644
index 8dfb213b..00000000
--- a/pkgs/by-name/ts/tskm/build.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-use anyhow::{Context, Result};
-use clap::{CommandFactory, ValueEnum};
-use clap_complete::generate_to;
-use clap_complete::Shell;
-
-use std::env;
-use std::fs;
-use std::path::PathBuf;
-
-use crate::cli::CliArgs;
-
-pub mod task {
- include!("src/task/mod.rs");
-}
-
-pub mod interface {
- pub mod input {
- include!("src/interface/input/mod.rs");
- }
- pub mod project {
- include!("src/interface/project/mod.rs");
- }
-}
-
-pub mod cli {
- include!("src/cli.rs");
-}
-
-fn main() -> Result<()> {
- let outdir = match env::var_os("SHELL_COMPLETION_DIR") {
- None => return Ok(()),
- Some(outdir) => outdir,
- };
-
- if !PathBuf::from(&outdir).exists() {
- fs::create_dir_all(&outdir)?;
- }
-
- let mut cmd = CliArgs::command();
-
- for &shell in Shell::value_variants() {
- let path = generate_to(shell, &mut cmd, "tskm", &outdir).with_context(|| {
- format!("Failed to output shell completion for {shell} to {outdir:?}")
- })?;
- println!("cargo:warning=completion file for {shell} is generated at: {path:?}");
- }
-
- Ok(())
-}
diff --git a/pkgs/by-name/ts/tskm/flake.lock b/pkgs/by-name/ts/tskm/flake.lock
deleted file mode 100644
index 82448387..00000000
--- a/pkgs/by-name/ts/tskm/flake.lock
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "nodes": {
- "nixpkgs": {
- "locked": {
- "lastModified": 1743076231,
- "narHash": "sha256-yQugdVfi316qUfqzN8JMaA2vixl+45GxNm4oUfXlbgw=",
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "6c5963357f3c1c840201eda129a99d455074db04",
- "type": "github"
- },
- "original": {
- "owner": "NixOS",
- "ref": "nixpkgs-unstable",
- "repo": "nixpkgs",
- "type": "github"
- }
- },
- "root": {
- "inputs": {
- "nixpkgs": "nixpkgs"
- }
- }
- },
- "root": "root",
- "version": 7
-}
diff --git a/pkgs/by-name/ts/tskm/flake.nix b/pkgs/by-name/ts/tskm/flake.nix
index 4b1142d6..e4d22c09 100644
--- a/pkgs/by-name/ts/tskm/flake.nix
+++ b/pkgs/by-name/ts/tskm/flake.nix
@@ -1,22 +1,36 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
{
description = "This is the core interface to the system-integrated task management";
inputs = {
- nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
};
- outputs = {nixpkgs, ...}: let
+ outputs = {...}: let
system = "x86_64-linux";
- pkgs = nixpkgs.legacyPackages."${system}";
+
+ sources = import ../../../../npins/full.nix {};
+ pkgs = (sources.loadFlake "nixpkgs").legacyPackages."${system}";
in {
devShells."${system}".default = pkgs.mkShell {
- packages = with pkgs; [
- cargo
- clippy
- rustc
- rustfmt
+ buildInputs = [
+ pkgs.sqlite
+ ];
+
+ packages = [
+ pkgs.cargo
+ pkgs.clippy
+ pkgs.rustc
+ pkgs.rustfmt
- cargo-edit
+ pkgs.cargo-edit
];
};
};
diff --git a/pkgs/by-name/ts/tskm/package.nix b/pkgs/by-name/ts/tskm/package.nix
index 1f206abf..ad10865f 100644
--- a/pkgs/by-name/ts/tskm/package.nix
+++ b/pkgs/by-name/ts/tskm/package.nix
@@ -1,3 +1,12 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
{
rustPlatform,
installShellFiles,
@@ -7,7 +16,7 @@
taskwarrior3,
git,
rofi,
- firefox,
+ sqlite,
}:
rustPlatform.buildRustPackage (finalAttrs: {
pname = "tskm";
@@ -18,15 +27,11 @@ rustPlatform.buildRustPackage (finalAttrs: {
lockFile = ./Cargo.lock;
};
- env = {
- SHELL_COMPLETION_DIR = "./shell";
- };
-
buildInputs = [
taskwarrior3
git
rofi
- firefox
+ sqlite
];
nativeBuildInputs = [
@@ -36,9 +41,9 @@ rustPlatform.buildRustPackage (finalAttrs: {
postInstall = ''
installShellCompletion --cmd tskm \
- --bash ./shell/tskm.bash \
- --fish ./shell/tskm.fish \
- --zsh ./shell/_tskm
+ --bash <(COMPLETE=bash $out/bin/tskm) \
+ --fish <(COMPLETE=fish $out/bin/tskm) \
+ --zsh <(COMPLETE=zsh $out/bin/tskm)
# NOTE: We cannot clear the path, because we need access to the $EDITOR. <2025-04-04>
wrapProgram $out/bin/tskm \
diff --git a/pkgs/by-name/ts/tskm/src/browser/mod.rs b/pkgs/by-name/ts/tskm/src/browser/mod.rs
new file mode 100644
index 00000000..fd90b820
--- /dev/null
+++ b/pkgs/by-name/ts/tskm/src/browser/mod.rs
@@ -0,0 +1,217 @@
+use std::{
+ env, fs,
+ io::Write,
+ os::unix::net::UnixStream,
+ path::PathBuf,
+ process::{self, ExitStatus},
+};
+
+use anyhow::{Context, Result};
+use log::{error, info};
+use serde_json::json;
+use url::Url;
+
+use crate::{state::State, task};
+
+#[allow(clippy::too_many_lines)]
+pub async fn open_in_browser<U>(
+ selected_project: &task::Project,
+ state: &mut State,
+ urls: Option<Vec<U>>,
+) -> Result<()>
+where
+ U: Into<Url>,
+{
+ let old_project: Option<task::Project> =
+ task::Project::get_current().context("Failed to get currently active project")?;
+ let old_task: Option<task::Task> = task::Task::get_current(state)
+ .await
+ .context("Failed to get currently active task")?;
+
+ selected_project.activate().with_context(|| {
+ format!(
+ "Failed to active project: '{}'",
+ selected_project.to_project_display()
+ )
+ })?;
+
+ let tracking_task = {
+ let all_tasks = selected_project.get_tasks(state).await.with_context(|| {
+ format!(
+ "Failed to get assoctiated tasks for project: '{}'",
+ selected_project.to_project_display()
+ )
+ })?;
+
+ let tracking_task = {
+ let mut output = None;
+
+ for t in all_tasks {
+ let maybe_desc = t.description(state).await;
+ let found = if let Ok(desc) = maybe_desc {
+ desc == "tracking"
+ } else {
+ error!(
+ "Getting task description returned error: {}",
+ maybe_desc.expect_err("We already check for Ok")
+ );
+ false
+ };
+
+ if found {
+ output = Some(t);
+ break;
+ }
+ }
+
+ output
+ };
+
+ if let Some(task) = tracking_task {
+ info!(
+ "Starting task {} -> tracking",
+ selected_project.to_project_display()
+ );
+ task.start(state)
+ .await
+ .with_context(|| format!("Failed to start task {task}"))?;
+ }
+ tracking_task
+ };
+
+ let status = {
+ // #!/bin/sh
+ // # initial idea: Florian Bruhin (The-Compiler)
+ // # author: Thore Bödecker (foxxx0)
+ //
+ // _url="$1"
+ // _qb_version='1.0.4'
+ // _proto_version=1
+ // _ipc_socket="${XDG_RUNTIME_DIR}/qutebrowser/ipc-$(printf '%s' "$USER" | md5sum | cut -d' ' -f1)"
+ // _qute_bin="/usr/bin/qutebrowser"
+ //
+ // printf '{"args": ["%s"], "target_arg": null, "version": "%s", "protocol_version": %d, "cwd": "%s"}\n' \
+ // "${_url}" \
+ // "${_qb_version}" \
+ // "${_proto_version}" \
+ // "${PWD}" | socat -lf /dev/null - UNIX-CONNECT:"${_ipc_socket}" || "$_qute_bin" "$@" &
+
+ let ipc_socket_path = PathBuf::from(
+ env::var("XDG_RUNTIME_DIR").context("Failed to access XDG_RUNTIME_DIR var")?,
+ )
+ .join("qutebrowser")
+ .join(selected_project.to_project_display())
+ .join(format!("ipc-{:x}", {
+ let user_name = env::var("USER").context("Failed to get USER var")?;
+ let base_dir = env::var("XDG_DATA_HOME").context("Failed to get XDG_DATA_HOME")?;
+
+ md5::compute(
+ format!(
+ "{user_name}-{}",
+ PathBuf::from(base_dir)
+ .join("qutebrowser")
+ .join(selected_project.to_project_display())
+ .display()
+ )
+ .as_bytes(),
+ )
+ }));
+
+ let socket = if ipc_socket_path.exists() {
+ match UnixStream::connect(&ipc_socket_path) {
+ Ok(ok) => Some(ok),
+ Err(err) => match err.kind() {
+ std::io::ErrorKind::ConnectionRefused => {
+ // There is no qutebrowser listening to our connection.
+ fs::remove_file(&ipc_socket_path).with_context(|| {
+ format!(
+ "Failed to remove orphaned qutebrowser socket: {}",
+ ipc_socket_path.display()
+ )
+ })?;
+ None
+ }
+ _ => Err(err).with_context(|| {
+ format!(
+ "Failed to connect to qutebrowser's ipc socket at: {}",
+ ipc_socket_path.display()
+ )
+ })?,
+ },
+ }
+ } else {
+ None
+ };
+
+ if let Some(mut stream) = socket {
+ let real_url = if let Some(urls) = urls {
+ urls.into_iter().map(|url| url.into().to_string()).collect()
+ } else {
+ // Always add a new tab, so that qutebrowser is marked as “urgent”.
+ vec!["qute://start".to_owned()]
+ };
+
+ stream.write_all(
+ json! {
+ {
+ "args": real_url,
+ "target_arg": null,
+ "version": "1.0.4",
+ "protocol_version": 1,
+ "cwd": "/"
+ }
+ }
+ .to_string()
+ .as_bytes(),
+ )?;
+ stream.write_all(b"\n")?;
+
+ ExitStatus::default()
+ } else {
+ let args = if let Some(urls) = urls {
+ urls.into_iter()
+ .map(Into::<Url>::into)
+ .map(|u| u.to_string())
+ .collect()
+ } else {
+ vec![]
+ };
+
+ process::Command::new(format!(
+ "qutebrowser-{}",
+ selected_project.to_project_display()
+ ))
+ .args(args)
+ .status()
+ .context("Failed to start qutebrowser")?
+ }
+ };
+
+ if !status.success() {
+ error!("Qutebrowser run exited with error.");
+ }
+
+ if let Some(task) = tracking_task {
+ task.stop(state)
+ .await
+ .with_context(|| format!("Failed to stop task {task}"))?;
+ }
+ if let Some(task) = old_task {
+ task.start(state)
+ .await
+ .with_context(|| format!("Failed to start task {task}"))?;
+ }
+
+ if let Some(project) = old_project {
+ project.activate().with_context(|| {
+ format!(
+ "Failed to activate project {}",
+ project.to_project_display()
+ )
+ })?;
+ } else {
+ task::Project::clear().context("Failed to clear currently focused project")?;
+ }
+
+ Ok(())
+}
diff --git a/pkgs/by-name/ts/tskm/src/cli.rs b/pkgs/by-name/ts/tskm/src/cli.rs
index 99c8693e..3dc1181d 100644
--- a/pkgs/by-name/ts/tskm/src/cli.rs
+++ b/pkgs/by-name/ts/tskm/src/cli.rs
@@ -1,26 +1,90 @@
-use std::path::PathBuf;
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
-use clap::{Parser, Subcommand};
+use std::{ffi::OsStr, path::PathBuf, thread};
+
+use anyhow::{bail, Result};
+use clap::{builder::StyledStr, ArgAction, Parser, Subcommand, ValueEnum};
+use clap_complete::{ArgValueCompleter, CompletionCandidate};
+use tokio::runtime::Runtime;
use crate::{
- interface::{input::Input, project::ProjectName},
- task,
+ interface::{
+ input::{Input, Tag},
+ open::UrlLike,
+ project::ProjectName,
+ },
+ state, task,
};
+macro_rules! as_sync {
+ (
+ wrap $old_name_1:ident($($arg_name_1:ident : $arg_type_1:ty),*) -> $output_1:ty => $new_name_1:ident;
+ $(
+ wrap $old_name:ident($($arg_name:ident : $arg_type:ty),*) -> $output:ty => $new_name:ident;
+ )+
+ ) => {
+ as_sync!(
+ wrap $old_name_1($($arg_name_1 : $arg_type_1),*) -> $output_1 => $new_name_1;
+ );
+ as_sync!(
+ $(
+ wrap $old_name($($arg_name : $arg_type),*) -> $output => $new_name;
+ )+
+ );
+ };
+ (
+ wrap $old_name:ident($($arg_name:ident : $arg_type:ty),*) -> $output:ty => $new_name:ident $(;)?
+ ) => {
+ fn $new_name($($arg_name: $arg_type),*) -> $output {
+ $(
+ let $arg_name = $arg_name.to_owned();
+ ),*
+
+ let handle: std::thread::JoinHandle<$output> = thread::spawn(move || {
+ let rt = Runtime::new().expect("No runtime issue");
+
+ let output = rt.block_on($old_name($($arg_name.as_ref()),*));
+
+ output
+ });
+
+ handle.join().expect("The thread should be joinable")
+ }
+ };
+}
+
#[derive(Parser, Debug)]
#[command(author, version, about, long_about, verbatim_doc_comment)]
/// This is the core interface to the system-integrated task management
///
/// `tskm` effectively combines multiple applications together:
-/// - `taskwarrior` projects are raised connected to `firefox` profiles, making it possible to “open”
+/// - `taskwarrior` projects are connected to `qutebrowser` profiles, making it possible to “open”
/// a project.
+///
/// - Every `taskwarrior` project has a determined `neorg` path, so that extra information for a
/// `project` can be stored in this `norg` file.
+///
/// - `tskm` can track inputs for you. These are URLs with optional tags which you can that
/// “review” to open tasks based on them.
pub struct CliArgs {
#[command(subcommand)]
pub command: Command,
+
+ /// Increase message verbosity
+ #[arg(long="verbose", short = 'v', action = ArgAction::Count, default_value_t = 2)]
+ pub verbosity: u8,
+
+ /// Silence all output
+ #[arg(long, short = 'q')]
+ pub quiet: bool,
}
#[derive(Subcommand, Debug)]
@@ -43,7 +107,7 @@ pub enum Command {
command: NeorgCommand,
},
- /// Interface with the Firefox profile of each project.
+ /// Interface with the Qutebrowser profile of each project.
Open {
#[command(subcommand)]
command: OpenCommand,
@@ -66,57 +130,246 @@ pub enum ProjectCommand {
#[derive(Subcommand, Debug, Clone, Copy)]
pub enum NeorgCommand {
/// Open the `neorg` project associated with id of the task.
- Task { id: task::Id },
+ Task {
+ /// The working set id of the task
+ #[arg(value_name = "ID", value_parser = task_from_working_set_id_sync, add = ArgValueCompleter::new(complete_task_id_sync))]
+ task: task::Task,
+ },
+}
+
+as_sync!(
+ wrap task_from_working_set_id(id: &str) -> Result<task::Task> => task_from_working_set_id_sync;
+ wrap complete_task_id(current: &OsStr) -> Vec<CompletionCandidate> => complete_task_id_sync;
+);
+
+async fn task_from_working_set_id(id: &str) -> Result<task::Task> {
+ let id: usize = id.parse()?;
+ let mut state = state::State::new_ro().await?;
+
+ let Some(task) = task::Task::from_working_set(id, &mut state).await? else {
+ bail!("Working set id '{id}' is not valid!")
+ };
+ Ok(task)
}
#[derive(Subcommand, Debug)]
pub enum OpenCommand {
- /// Open each project's Firefox profile consecutively, that was opened since the last review.
+ /// Open each project's Qutebrowser profile consecutively, that was opened since the last review.
///
/// This allows you to remove stale opened tabs and to commit open tabs to the `inputs`.
- Review,
+ Review {
+ /// Review all projects, if they contain tabs
+ #[arg(short, long, default_value_t)]
+ non_empty: bool,
+ },
- /// Opens Firefox with either the supplied project or the currently active project profile.
+ /// Opens Qutebrowser with either the supplied project or the currently active project profile.
Project {
/// The project to open.
- #[arg(value_parser = task::Project::from_project_string)]
- project: Option<task::Project>,
+ #[arg(value_parser = task::Project::from_project_string, add = ArgValueCompleter::new(complete_project))]
+ project: task::Project,
+
+ /// The URLs to open.
+ urls: Option<Vec<UrlLike>>,
},
- /// Open a selected project in it's Firefox profile.
+ /// Open a selected project in it's Qutebrowser profile.
///
/// This will use rofi's dmenu mode to select one project from the list of all registered
/// projects.
- Select,
+ Select {
+ /// The URLs to open.
+ urls: Option<Vec<UrlLike>>,
+ },
/// List all open tabs in the project.
ListTabs {
- /// The project to open.
- #[arg(value_parser = task::Project::from_project_string)]
- project: Option<task::Project>,
+ /// The projects to open.
+ #[arg(value_parser = task::Project::from_project_string, add = ArgValueCompleter::new(complete_project))]
+ projects: Option<Vec<task::Project>>,
+
+ /// Only show the tabs, that are in this mode
+ #[arg(short, long, conflicts_with = "projects")]
+ mode: Option<ListMode>,
},
}
+#[derive(Clone, Copy, ValueEnum, Debug)]
+pub enum ListMode {
+ // The tab contains no tabs.
+ Empty,
+
+ // The tab contains tabs.
+ NonEmpty,
+}
+
#[derive(Subcommand, Debug)]
pub enum InputCommand {
/// Add URLs as inputs to be categorized.
Add { inputs: Vec<Input> },
/// Remove URLs
- Remove { inputs: Vec<Input> },
+ Remove {
+ #[arg(add = ArgValueCompleter::new(complete_input_url))]
+ inputs: Vec<Input>,
+ },
/// Add all URLs in the file as inputs to be categorized.
///
/// This expects each line to contain one URL.
- File { file: PathBuf },
+ File {
+ /// The file to read from.
+ file: PathBuf,
+
+ /// Additional tags to apply to every read URL in the file.
+ #[arg(add = ArgValueCompleter::new(complete_tag))]
+ tags: Vec<Tag>,
+ },
/// Like 'review', but for the inputs that have previously been added.
/// It takes a project in which to open the URLs.
Review {
/// Opens all the URLs in this project.
- #[arg(value_parser = task::Project::from_project_string)]
+ #[arg(value_parser = task::Project::from_project_string, add = ArgValueCompleter::new(complete_project))]
project: task::Project,
},
/// List all the previously added inputs.
- List,
+ List {
+ /// Only list the inputs that have all the specified tags
+ #[arg(add = ArgValueCompleter::new(complete_tag))]
+ tags: Vec<Tag>,
+ },
+
+ /// Show all the available tags.
+ Tags {},
+}
+
+async fn complete_task_id(current: &OsStr) -> Vec<CompletionCandidate> {
+ async fn format_task(
+ task: task::Task,
+ current: &str,
+ state: &mut state::State,
+ ) -> Option<CompletionCandidate> {
+ let id = {
+ let Ok(base) = task.working_set_id(state).await else {
+ return None;
+ };
+ base.to_string()
+ };
+
+ if !id.starts_with(current) {
+ return None;
+ }
+
+ let description = {
+ let Ok(base) = task.description(state).await else {
+ return None;
+ };
+ StyledStr::from(base)
+ };
+
+ Some(CompletionCandidate::new(id).help(Some(description)))
+ }
+
+ let mut output = vec![];
+
+ let Some(current) = current.to_str() else {
+ return output;
+ };
+
+ let Ok(mut state) = state::State::new_ro().await else {
+ return output;
+ };
+
+ let Ok(pending) = state.replica().pending_tasks().await else {
+ return output;
+ };
+
+ let Ok(current_project) = task::Project::get_current() else {
+ return output;
+ };
+
+ if let Some(current_project) = current_project {
+ for t in pending {
+ let task = task::Task::from(&t);
+ if let Ok(project) = task.project(&mut state).await {
+ if project == current_project {
+ if let Some(out) = format_task(task, current, &mut state).await {
+ output.push(out);
+ }
+ }
+ }
+ }
+ } else {
+ for t in pending {
+ let task = task::Task::from(&t);
+ if let Some(out) = format_task(task, current, &mut state).await {
+ output.push(out);
+ }
+ }
+ }
+
+ output
+}
+fn complete_project(current: &OsStr) -> Vec<CompletionCandidate> {
+ let mut output = vec![];
+
+ let Some(current) = current.to_str() else {
+ return output;
+ };
+
+ let Ok(all) = task::Project::all() else {
+ return output;
+ };
+
+ for a in all {
+ if a.to_project_display().starts_with(current) {
+ output.push(CompletionCandidate::new(a.to_project_display()));
+ }
+ }
+
+ output
+}
+fn complete_input_url(current: &OsStr) -> Vec<CompletionCandidate> {
+ let mut output = vec![];
+
+ let Some(current) = current.to_str() else {
+ return output;
+ };
+
+ let Ok(all) = Input::all() else {
+ return output;
+ };
+
+ for a in all {
+ if a.to_string().starts_with(current) {
+ output.push(CompletionCandidate::new(a.to_string()));
+ }
+ }
+
+ output
+}
+fn complete_tag(current: &OsStr) -> Vec<CompletionCandidate> {
+ let mut output = vec![];
+
+ let Some(current) = current.to_str() else {
+ return output;
+ };
+
+ if !current.starts_with('+') {
+ output.push(CompletionCandidate::new(format!("+{current}")));
+ }
+
+ output
+}
+
+#[cfg(test)]
+mod test {
+ use clap::CommandFactory;
+
+ use super::CliArgs;
+ #[test]
+ fn verify_cli() {
+ CliArgs::command().debug_assert();
+ }
}
diff --git a/pkgs/by-name/ts/tskm/src/interface/input/handle.rs b/pkgs/by-name/ts/tskm/src/interface/input/handle.rs
index 0ff0e56e..cd868f7a 100644
--- a/pkgs/by-name/ts/tskm/src/interface/input/handle.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/input/handle.rs
@@ -1,23 +1,34 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
use std::{
- fs, process,
+ collections::{HashMap, HashSet},
+ fs,
str::FromStr,
- thread::{self, sleep},
- time::Duration,
};
use anyhow::{Context, Result};
-use log::{error, info};
+use log::info;
+use taskchampion::chrono::Utc;
-use crate::cli::InputCommand;
+use crate::{browser::open_in_browser, cli::InputCommand, state::State};
-use super::Input;
+use super::{Input, Tag};
/// # Errors
/// When command handling fails.
///
/// # Panics
/// When internal assertions fail.
-pub fn handle(command: InputCommand) -> Result<()> {
+#[allow(clippy::too_many_lines)]
+pub async fn handle(command: InputCommand, state: &mut State) -> Result<()> {
match command {
InputCommand::Add { inputs } => {
for input in inputs {
@@ -33,47 +44,43 @@ pub fn handle(command: InputCommand) -> Result<()> {
})?;
}
}
- InputCommand::File { file } => {
- let file = fs::read_to_string(file)?;
- for line in file.lines() {
- let input = Input::from_str(line)?;
+ InputCommand::File { file, tags } => {
+ let file = fs::read_to_string(&file)
+ .with_context(|| format!("Failed to read input file '{}'", file.display()))?;
+
+ let mut tag_set = HashSet::with_capacity(tags.len());
+ for tag in tags {
+ tag_set.insert(tag);
+ }
+
+ tag_set.insert(
+ Tag::new(format!("+{}", Utc::now().format("%Y-%m-%d")).as_str())
+ .expect("hardcoded"),
+ );
+
+ for line in file.lines().map(str::trim) {
+ if line.is_empty() {
+ continue;
+ }
+
+ let mut input = Input::from_str(line)?;
+ input.tags = input.tags.union(&tag_set).cloned().collect();
+
input.commit().with_context(|| {
format!("Failed to add input ('{input}') to the input storage.")
})?;
}
}
InputCommand::Review { project } => {
- let project = project.to_project_display();
-
- let local_project = project.clone();
- let handle = thread::spawn(move || {
- // We assume that the project is not yet open.
- let mut firefox = process::Command::new("firefox")
- .args(["-P", local_project.as_str(), "about:newtab"])
- .spawn()?;
-
- Ok::<_, anyhow::Error>(firefox.wait()?)
- });
- // Give Firefox some time to start.
- info!("Waiting on firefox to start");
- sleep(Duration::from_secs(4));
-
- let project_str = project.as_str();
'outer: for all in Input::all()?.chunks(100) {
info!("Starting review for the first hundred URLs.");
- for input in all {
- info!("-> '{input}'");
- let status = process::Command::new("firefox")
- .args(["-P", project_str, input.url().to_string().as_str()])
- .status()?;
-
- if status.success() {
- input.remove()?;
- } else {
- error!("Adding `{input}` to Firefox failed!");
- }
- }
+ open_in_browser(
+ &project,
+ state,
+ Some(all.iter().map(|f| f.url.clone()).collect()),
+ )
+ .await?;
{
use std::io::{stdin, stdout, Write};
@@ -98,15 +105,51 @@ pub fn handle(command: InputCommand) -> Result<()> {
}
}
}
-
- info!("Waiting for firefox to stop");
- handle.join().expect("Should be joinable")?;
}
- InputCommand::List => {
- for url in Input::all()? {
+ InputCommand::List { tags } => {
+ let mut tag_set = HashSet::with_capacity(tags.len());
+ for tag in tags {
+ tag_set.insert(tag);
+ }
+
+ for url in Input::all()?
+ .iter()
+ .filter(|input| tag_set.is_subset(&input.tags))
+ {
println!("{url}");
}
}
+ InputCommand::Tags {} => {
+ let mut without_tags = 0;
+ let mut tag_set: HashMap<Tag, u64> = HashMap::new();
+
+ for input in Input::all()? {
+ if input.tags.is_empty() {
+ without_tags += 1;
+ }
+
+ for tag in input.tags {
+ if let Some(number) = tag_set.get_mut(&tag) {
+ *number += 1;
+ } else {
+ tag_set.insert(tag, 1);
+ }
+ }
+ }
+
+ let mut tags: Vec<(Tag, u64)> = tag_set.into_iter().collect();
+ tags.sort_by_key(|(_, number)| *number);
+ tags.reverse();
+
+ for (tag, number) in tags {
+ println!("{tag} {number}");
+ }
+
+ if without_tags != 0 {
+ println!();
+ println!("Witohut tags: {without_tags}");
+ }
+ }
}
Ok(())
}
diff --git a/pkgs/by-name/ts/tskm/src/interface/input/mod.rs b/pkgs/by-name/ts/tskm/src/interface/input/mod.rs
index 9ece7a3a..b6176a96 100644
--- a/pkgs/by-name/ts/tskm/src/interface/input/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/input/mod.rs
@@ -1,14 +1,24 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
use std::{
- collections::HashSet,
+ collections::{HashMap, HashSet},
fmt::Display,
- fs::{self, read_to_string, File},
+ fs,
io::Write,
path::PathBuf,
process::Command,
str::FromStr,
};
-use anyhow::{bail, Context, Result};
+use anyhow::{Context, Result, bail};
use url::Url;
use walkdir::WalkDir;
@@ -16,38 +26,47 @@ pub mod handle;
pub use handle::handle;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct NoWhitespaceString(String);
+pub struct Tag(String);
-impl NoWhitespaceString {
- /// # Panics
- /// If the input contains whitespace.
- #[must_use]
- pub fn new(input: String) -> Self {
- if input.contains(' ') {
- panic!("Your input '{input}' contains whitespace. I did not expect that.")
+impl Tag {
+ pub fn new(input: &str) -> Result<Self> {
+ Self::from_str(input)
+ }
+}
+
+impl FromStr for Tag {
+ type Err = anyhow::Error;
+
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+ if let Some(tag) = s.strip_prefix('+') {
+ if tag.contains(' ') {
+ bail!("Your tag '{s}' should not whitespace.")
+ }
+
+ Ok(Self(tag.to_owned()))
} else {
- Self(input)
+ bail!("Your tag '{s}' does not start with the required '+'");
}
}
}
-impl Display for NoWhitespaceString {
+impl Display for Tag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- self.0.fmt(f)
+ write!(f, "+{}", self.0)
}
}
-impl NoWhitespaceString {
+impl Tag {
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Input {
url: Url,
- tags: HashSet<NoWhitespaceString>,
+ tags: HashSet<Tag>,
}
impl FromStr for Input {
@@ -61,13 +80,7 @@ impl FromStr for Input {
tags: {
tags.trim()
.split(' ')
- .map(|tag| {
- if let Some(tag) = tag.strip_prefix('+') {
- Ok(NoWhitespaceString::new(tag.to_owned()))
- } else {
- bail!("Your tag '{tag}' does not start with the required '+'");
- }
- })
+ .map(Tag::new)
.collect::<Result<_, _>>()?
},
})
@@ -91,13 +104,9 @@ impl Display for Input {
self.url,
self.tags
.iter()
- .fold(String::new(), |mut acc, tag| {
- acc.push('+');
- acc.push_str(tag.as_str());
- acc.push(' ');
- acc
- })
- .trim()
+ .map(ToString::to_string)
+ .collect::<Vec<_>>()
+ .join(" ")
)
}
}
@@ -113,7 +122,10 @@ impl Input {
fn url_path(url: &Url) -> Result<PathBuf> {
let base_path = Self::base_path();
- let url_path = base_path.join(url.to_string());
+ let url_path = base_path
+ .join(url.scheme())
+ .join(url.host_str().unwrap_or("<No Host>"))
+ .join(url.path().trim_matches('/'));
fs::create_dir_all(&url_path)
.with_context(|| format!("Failed to open file: '{}'", url_path.display()))?;
@@ -132,17 +144,12 @@ impl Input {
pub fn commit(&self) -> Result<()> {
let url_path = Self::url_path(&self.url)?;
- let url_content = {
- if url_path.exists() {
- read_to_string(&url_path)?
- } else {
- String::new()
- }
- };
-
- let mut file = File::create(&url_path)
+ let mut file = fs::OpenOptions::new()
+ .create(true)
+ .append(true)
+ .open(&url_path)
.with_context(|| format!("Failed to open file: '{}'", url_path.display()))?;
- writeln!(file, "{url_content}{self}")?;
+ writeln!(file, "{self}")?;
Self::git_commit(&format!("Add new url: '{self}'"))?;
@@ -173,28 +180,7 @@ impl Input {
Ok(())
}
- /// Commit your changes
- fn git_commit(message: &str) -> Result<()> {
- let status = Command::new("git")
- .args(["add", "."])
- .current_dir(Self::base_path())
- .status()?;
- if !status.success() {
- bail!("Git add . failed!");
- }
-
- let status = Command::new("git")
- .args(["commit", "--message", message, "--no-gpg-sign"])
- .current_dir(Self::base_path())
- .status()?;
- if !status.success() {
- bail!("Git commit failed!");
- }
-
- Ok(())
- }
-
- /// Get all previously [`Self::commit`]ed inputs.
+ /// Get all previously [committed][`Self::commit`] inputs.
///
/// # Errors
/// When IO handling fails.
@@ -217,41 +203,58 @@ impl Input {
continue;
}
- let url_value_file = entry
- .path()
- .to_str()
- .expect("All of these should be URLs and thus valid strings");
- assert!(url_value_file.ends_with("/url_value"));
+ let url_value_file = entry.path();
+ assert!(url_value_file.ends_with("url_value"));
- let url = {
- let base = url_value_file
- .strip_prefix(&format!("{}/", Self::base_path().display()))
- .expect("This will exist");
+ let url_values = fs::read_to_string(PathBuf::from(url_value_file))?;
- let (proto, path) = base.split_once(':').expect("This will countain a :");
+ let mut inputs: HashMap<Url, Self> = HashMap::new();
+ for input in url_values
+ .lines()
+ .map(Self::from_str)
+ .collect::<Result<Vec<Self>, _>>()?
+ {
+ if let Some(found) = inputs.get_mut(&input.url) {
+ found.tags = found.tags.union(&input.tags).cloned().collect();
+ } else {
+ assert_eq!(inputs.insert(input.url.clone(), input), None);
+ }
+ }
- let path = path.strip_suffix("/url_value").expect("Will exist");
+ output.extend(inputs.drain().map(|(_, value)| value));
+ }
- Url::from_str(&format!("{proto}:/{path}"))
- .expect("This was a URL, it should still be one")
- };
- let tags = {
- let url_values = read_to_string(PathBuf::from(url_value_file))?;
- url_values
- .lines()
- .map(|line| {
- let input = Self::from_str(line)?;
- Ok::<_, anyhow::Error>(input.tags)
- })
- .collect::<Result<Vec<HashSet<NoWhitespaceString>>, _>>()?
- .into_iter()
- .flatten()
- .collect()
- };
+ Ok(output)
+ }
- output.push(Self { url, tags });
+ /// Commit your changes
+ fn git_commit(message: &str) -> Result<()> {
+ if !Self::base_path().join(".git").exists() {
+ let status = Command::new("git")
+ .args(["init"])
+ .current_dir(Self::base_path())
+ .status()?;
+ if !status.success() {
+ bail!("Git init failed!");
+ }
}
- Ok(output)
+ let status = Command::new("git")
+ .args(["add", "."])
+ .current_dir(Self::base_path())
+ .status()?;
+ if !status.success() {
+ bail!("Git add . failed!");
+ }
+
+ let status = Command::new("git")
+ .args(["commit", "--message", message, "--no-gpg-sign"])
+ .current_dir(Self::base_path())
+ .status()?;
+ if !status.success() {
+ bail!("Git commit failed!");
+ }
+
+ Ok(())
}
}
diff --git a/pkgs/by-name/ts/tskm/src/interface/mod.rs b/pkgs/by-name/ts/tskm/src/interface/mod.rs
index 1a0d934c..513ca317 100644
--- a/pkgs/by-name/ts/tskm/src/interface/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/mod.rs
@@ -1,3 +1,13 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
pub mod input;
pub mod neorg;
pub mod open;
diff --git a/pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs b/pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs
index a9a46ee7..12a0180d 100644
--- a/pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs
@@ -1,33 +1,59 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
use std::{
env,
- fs::{self, read_to_string, OpenOptions},
+ fs::{self, read_to_string, File, OpenOptions},
io::Write,
process::Command,
};
-use anyhow::{bail, Result};
+use anyhow::{bail, Context, Result};
-use crate::cli::NeorgCommand;
+use crate::{cli::NeorgCommand, state::State};
-pub fn handle(command: NeorgCommand) -> Result<()> {
+pub async fn handle(command: NeorgCommand, state: &mut State) -> Result<()> {
match command {
- NeorgCommand::Task { id } => {
- let project = id.project()?;
- let path = dirs::data_local_dir()
+ NeorgCommand::Task { task } => {
+ let project = task.project(state).await?;
+ let base = dirs::data_local_dir()
.expect("This should exists")
- .join("notes")
- .join(project.get_neorg_path()?);
+ .join("tskm/notes");
+ let path = base.join(project.get_neorg_path()?);
fs::create_dir_all(path.parent().expect("This should exist"))?;
{
- let contents = read_to_string(&path)?;
- if contents.contains(format!("% {}", id.to_uuid()?).as_str()) {
+ let contents = if path.exists() {
+ read_to_string(&path)
+ .with_context(|| format!("Failed to read file: '{}'", path.display()))?
+ } else {
+ File::create(&path)
+ .with_context(|| format!("Failed to create file: '{}'", path.display()))?;
+ String::new()
+ };
+
+ if !contents.contains(format!("% {}", task.uuid()).as_str()) {
let mut options = OpenOptions::new();
- options.append(true).create(true);
+ options.append(true).create(false);
let mut file = options.open(&path)?;
- file.write_all(format!("* TITLE (% {})", id.to_uuid()?).as_bytes())?;
+ file.write_all(
+ format!("* {} (% {})", task.description(state).await?, task.uuid())
+ .as_bytes(),
+ )
+ .with_context(|| {
+ format!("Failed to write task uuid to file: '{}'", path.display())
+ })?;
+ file.flush()
+ .with_context(|| format!("Failed to flush file: '{}'", path.display()))?;
}
}
@@ -36,7 +62,7 @@ pub fn handle(command: NeorgCommand) -> Result<()> {
.args([
path.to_str().expect("Should be a utf-8 str"),
"-c",
- format!("/% {}", id.to_uuid()?).as_str(),
+ format!("/% {}", task.uuid()).as_str(),
])
.status()?;
if !status.success() {
@@ -46,7 +72,7 @@ pub fn handle(command: NeorgCommand) -> Result<()> {
{
let status = Command::new("git")
.args(["add", "."])
- .current_dir(&path)
+ .current_dir(path.parent().expect("Will exist"))
.status()?;
if !status.success() {
bail!("Git add . failed!");
@@ -56,14 +82,10 @@ pub fn handle(command: NeorgCommand) -> Result<()> {
.args([
"commit",
"--message",
- format!(
- "chore({}): Update",
- path.parent().expect("Should have a parent").display()
- )
- .as_str(),
+ format!("chore({}): Update", project.get_neorg_path()?.display()).as_str(),
"--no-gpg-sign",
])
- .current_dir(&path)
+ .current_dir(path.parent().expect("Will exist"))
.status()?;
if !status.success() {
bail!("Git commit failed!");
@@ -71,7 +93,7 @@ pub fn handle(command: NeorgCommand) -> Result<()> {
}
{
- id.annotate("[neorg data]")?;
+ task.mark_neorg_data(state).await?;
}
}
}
diff --git a/pkgs/by-name/ts/tskm/src/interface/neorg/mod.rs b/pkgs/by-name/ts/tskm/src/interface/neorg/mod.rs
index dc5cdf19..6bed1e39 100644
--- a/pkgs/by-name/ts/tskm/src/interface/neorg/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/neorg/mod.rs
@@ -1,18 +1,35 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
use std::path::PathBuf;
use anyhow::Result;
-use crate::task::{run_task, Project};
+use crate::task::{Project, run_task};
pub mod handle;
pub use handle::handle;
impl Project {
+ /// Return the stored neorg path of this project.
+ /// The returned path will never start with a slash (/).
pub(super) fn get_neorg_path(&self) -> Result<PathBuf> {
let project_path = run_task(&[
"_get",
format!("rc.context.{}.rc.neorg_path", self.to_context_display()).as_str(),
])?;
- Ok(PathBuf::from(project_path.as_str()))
+
+ let final_path = project_path
+ .strip_prefix('/')
+ .unwrap_or(project_path.as_str());
+
+ Ok(PathBuf::from(final_path))
}
}
diff --git a/pkgs/by-name/ts/tskm/src/interface/open/handle.rs b/pkgs/by-name/ts/tskm/src/interface/open/handle.rs
index dc0d165d..5b9100bc 100644
--- a/pkgs/by-name/ts/tskm/src/interface/open/handle.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/open/handle.rs
@@ -1,48 +1,77 @@
-use std::process;
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use std::str::FromStr;
use anyhow::{bail, Context, Result};
use log::{error, info};
+use url::Url;
+
+use crate::{browser::open_in_browser, cli::OpenCommand, rofi, state::State, task};
+
+fn is_empty(project: &task::Project) -> Result<bool> {
+ let tabs = get_tabs(project)?;
-use crate::{cli::OpenCommand, rofi, task};
+ if tabs.is_empty() {
+ Ok(true)
+ } else if tabs.len() > 1 {
+ Ok(false)
+ } else {
+ let url = &tabs[0].1;
-pub fn handle(command: OpenCommand) -> Result<()> {
+ Ok(url == &Url::from_str("qute://start/").expect("Hardcoded"))
+ }
+}
+
+#[allow(clippy::too_many_lines)]
+pub async fn handle(command: OpenCommand, state: &mut State) -> Result<()> {
match command {
- OpenCommand::Review => {
+ OpenCommand::Review { non_empty } => {
for project in task::Project::all().context("Failed to get all project files")? {
- if project.is_touched() {
- info!("Reviewing project: '{}'", project.to_project_display());
- open_in_browser(project).with_context(|| {
- format!(
- "Failed to open project ('{}') in Firefox",
- project.to_project_display()
- )
- })?;
- project.untouch().with_context(|| {
- format!(
- "Failed to untouch project ('{}')",
- project.to_project_display()
- )
- })?;
+ let is_empty = is_empty(project)?;
+
+ if project.is_touched() || (non_empty && !is_empty) {
+ info!(
+ "Reviewing project: '{}' ({})",
+ project.to_project_display(),
+ if is_empty { "is empty" } else { "is not empty" }
+ );
+ open_in_browser(project, state, None::<Vec<Url>>)
+ .await
+ .with_context(|| {
+ format!(
+ "Failed to open project ('{}') in qutebrowser",
+ project.to_project_display()
+ )
+ })?;
+
+ if project.is_touched() {
+ project.untouch().with_context(|| {
+ format!(
+ "Failed to untouch project ('{}')",
+ project.to_project_display()
+ )
+ })?;
+ }
}
}
}
- OpenCommand::Project { project } => {
- let project = if let Some(p) = project {
- p
- } else if let Some(p) =
- task::Project::get_current().context("Failed to get currently focused project")?
- {
- p
- } else {
- bail!("You need to either supply a project or have a project active!");
- };
-
+ OpenCommand::Project { project, urls } => {
project.touch().context("Failed to touch project")?;
- open_in_browser(&project).with_context(|| {
- format!("Failed to open project: {}", project.to_project_display())
- })?;
+ open_in_browser(&project, state, urls)
+ .await
+ .with_context(|| {
+ format!("Failed to open project: {}", project.to_project_display())
+ })?;
}
- OpenCommand::Select => {
+ OpenCommand::Select { urls } => {
let selected_project: task::Project = task::Project::from_project_string(
&rofi::select(
task::Project::all()
@@ -60,130 +89,108 @@ pub fn handle(command: OpenCommand) -> Result<()> {
.touch()
.context("Failed to touch project")?;
- open_in_browser(&selected_project).context("Failed to open project")?;
+ open_in_browser(&selected_project, state, urls)
+ .await
+ .context("Failed to open project")?;
}
- OpenCommand::ListTabs { project } => {
- let project = if let Some(p) = project {
- p
- } else if let Some(p) =
- task::Project::get_current().context("Failed to get currently focused project")?
- {
- p
- } else {
- bail!("You need to either supply a project or have a project active!");
+ OpenCommand::ListTabs { projects, mode } => {
+ let projects = {
+ if let Some(p) = projects {
+ p
+ } else if mode.is_some() {
+ task::Project::all()
+ .context("Failed to get all projects")?
+ .to_owned()
+ } else if let Some(p) = task::Project::get_current()
+ .context("Failed to get currently focused project")?
+ {
+ vec![p]
+ } else {
+ bail!("You need to either select projects or pass --mode");
+ }
};
- let session_store = project.get_sessionstore().with_context(|| {
- format!(
- "Failed to get session store for project: '{}'",
- project.to_project_display()
- )
- })?;
+ for project in &projects {
+ if let Some(mode) = mode {
+ match mode {
+ crate::cli::ListMode::Empty => {
+ if !is_empty(project)? {
+ continue;
+ }
- let selected = session_store
- .windows
- .iter()
- .map(|w| w.selected)
- .collect::<Vec<_>>();
+ // We do not need to print, tabs they are always empty.
+ if projects.len() > 1 {
+ println!("/* {} */", project.to_project_display());
+ }
+ continue;
+ }
+ crate::cli::ListMode::NonEmpty => {
+ if is_empty(project)? {
+ continue;
+ }
+ }
+ }
+ }
- let tabs = session_store
- .windows
- .iter()
- .flat_map(|window| window.tabs.iter())
- .map(|tab| tab.entries.get(tab.index - 1).expect("This should be Some"))
- .collect::<Vec<_>>();
+ if projects.len() > 1 {
+ println!("/* {} */", project.to_project_display());
+ }
+
+ let tabs = match get_tabs(project) {
+ Ok(ok) => ok,
+ Err(err) => {
+ if projects.len() > 1 {
+ error!(
+ "While trying to get the sessionstore for {}: {:?}",
+ project.to_project_display(),
+ err
+ );
+ continue;
+ }
- for (index, entry) in tabs.iter().enumerate() {
- let index = index + 1;
- let is_selected = {
- if selected.contains(&index) {
- "🔻 "
- } else {
- " "
+ return Err(err).with_context(|| {
+ format!(
+ "While trying to get the sessionstore for {}",
+ project.to_project_display()
+ )
+ });
}
};
- println!("{}{}", is_selected, entry.url);
+
+ for (active, url) in tabs {
+ let is_selected = {
+ if active {
+ "🔻 "
+ } else {
+ " "
+ }
+ };
+ println!("{is_selected}{url}");
+ }
}
}
}
+
Ok(())
}
-fn open_in_browser(selected_project: &task::Project) -> Result<()> {
- let old_project: Option<task::Project> =
- task::Project::get_current().context("Failed to get currently active project")?;
- // We have ensured that only one task may be active
- let old_task: Option<task::Id> =
- task::Id::get_current().context("Failed to get currently active task")?;
-
- selected_project.activate().with_context(|| {
- format!(
- "Failed to active project: '{}'",
- selected_project.to_project_display()
- )
- })?;
-
- let tracking_task = {
- let all_tasks = selected_project.get_tasks().with_context(|| {
- format!(
- "Failed to get assoctiated tasks for project: '{}'",
- selected_project.to_project_display()
- )
- })?;
-
- let tracking_task = all_tasks.into_iter().find(|t| {
- let maybe_desc = t.description();
- if let Ok(desc) = maybe_desc {
- desc == "tracking"
- } else {
- error!(
- "Getting task description returned error: {}",
- maybe_desc.expect_err("We already check for Ok")
- );
- false
- }
- });
-
- if let Some(task) = tracking_task {
- info!(
- "Starting task {} -> tracking",
- selected_project.to_project_display()
- );
- task.start()
- .with_context(|| format!("Failed to start task {task}"))?;
- }
- tracking_task
- };
-
- let status = process::Command::new("firefox")
- .args([
- "-P",
- selected_project.to_project_display().as_str(),
- "about:newtab",
- ])
- .status()
- .context("Failed to start firefox")?;
-
- if !status.success() {
- error!("Firefox run exited with error.");
- }
-
- if let Some(task) = tracking_task {
- task.stop()
- .with_context(|| format!("Failed to stop task {task}"))?;
- }
- if let Some(task) = old_task {
- task.start()
- .with_context(|| format!("Failed to start task {task}"))?;
- }
+fn get_tabs(project: &task::Project) -> Result<Vec<(bool, Url)>> {
+ let session_store = project.get_sessionstore()?;
- if let Some(project) = old_project {
- project.activate().with_context(|| {
- format!("Failed to active project {}", project.to_project_display())
- })?;
- } else {
- task::Project::clear().context("Failed to clear currently focused project")?;
- }
+ let tabs = session_store
+ .windows
+ .iter()
+ .flat_map(|window| window.tabs.iter())
+ .filter_map(|tab| {
+ tab.history
+ .iter()
+ .find(|hist| hist.active)
+ .map(|hist| (tab.active, hist))
+ })
+ .collect::<Vec<_>>();
- Ok(())
+ Ok(tabs
+ .into_iter()
+ .map(|(active, hist)| (active, hist.url.clone()))
+ .collect())
}
diff --git a/pkgs/by-name/ts/tskm/src/interface/open/mod.rs b/pkgs/by-name/ts/tskm/src/interface/open/mod.rs
index 2dc75957..407536d2 100644
--- a/pkgs/by-name/ts/tskm/src/interface/open/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/open/mod.rs
@@ -1,106 +1,198 @@
-use std::{collections::HashMap, fs::File, io};
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
-use anyhow::{Context, Result};
-use lz4_flex::decompress_size_prepended;
-use serde::Deserialize;
-use serde_json::Value;
+use std::{
+ fs::{self, File},
+ io::Read,
+ str::FromStr,
+};
+
+use anyhow::{Context, Result, anyhow};
+use taskchampion::chrono::NaiveDateTime;
use url::Url;
+use yaml_rust2::Yaml;
use crate::task::Project;
pub mod handle;
pub use handle::handle;
+/// An Url that also accepts file paths
+#[derive(Debug, Clone)]
+pub struct UrlLike(Url);
+
+impl FromStr for UrlLike {
+ type Err = url::ParseError;
+
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+ if let Ok(u) = fs::canonicalize(s) {
+ Ok(Self(Url::from_file_path(u).expect(
+ "The path could be canonicalized, as such it is valid for this",
+ )))
+ } else {
+ Url::from_str(s).map(Self)
+ }
+ }
+}
+
+impl From<UrlLike> for Url {
+ fn from(value: UrlLike) -> Self {
+ value.0
+ }
+}
+
impl Project {
pub(super) fn get_sessionstore(&self) -> Result<SessionStore> {
- let path = dirs::home_dir()
- .expect("Will exist")
- .join(".mozilla/firefox")
+ let path = dirs::data_local_dir()
+ .context("Failed to get data dir")?
+ .join("qutebrowser")
.join(self.to_project_display())
- .join("sessionstore-backups/recovery.jsonlz4");
- let file = decompress_mozlz4(
- File::open(&path)
- .with_context(|| format!("Failed to open path '{}'", path.display()))?,
- )
- .with_context(|| format!("Failed to decompress file as mozlzh '{}'", path.display()))?;
+ .join("data/sessions/default.yml");
- let contents: SessionStore = serde_json::from_str(&file).with_context(|| {
- format!(
- "Failed to deserialize file ('{}') as session store.",
- path.display()
- )
- })?;
- Ok(contents)
+ let mut file = File::open(&path)
+ .with_context(|| format!("Failed to open path '{}'", path.display()))?;
+
+ let mut yaml_str = String::new();
+ file.read_to_string(&mut yaml_str)
+ .context("Failed to read _autosave.yml path")?;
+ let yaml = yaml_rust2::YamlLoader::load_from_str(&yaml_str)?;
+
+ let store = qute_store_from_yaml(&yaml).context("Failed to read yaml store")?;
+
+ Ok(store)
}
}
-fn decompress_mozlz4<P: io::Read>(mut file: P) -> Result<String> {
- const MOZLZ4_MAGIC_NUMBER: &[u8] = b"mozLz40\0";
+fn qute_store_from_yaml(yaml: &[Yaml]) -> Result<SessionStore> {
+ assert_eq!(yaml.len(), 1);
+ let doc = &yaml[0];
- let mut buf = [0u8; 8];
- file.read_exact(&mut buf)
- .context("Failed to read the mozlz40 header.")?;
+ let hash = doc.as_hash().context("Invalid yaml")?;
+ let windows = hash
+ .get(&Yaml::String("windows".to_owned()))
+ .ok_or(anyhow!("Missing windows"))?
+ .as_vec()
+ .ok_or(anyhow!("Windows not vector"))?;
- assert_eq!(buf, MOZLZ4_MAGIC_NUMBER);
+ Ok(SessionStore {
+ windows: windows
+ .iter()
+ .map(|window| {
+ let hash = window.as_hash().ok_or(anyhow!("Windows not hashmap"))?;
- let mut buf = vec![];
- file.read_to_end(&mut buf).context("Failed to read file")?;
+ Ok::<_, anyhow::Error>(Window {
+ geometry: hash
+ .get(&Yaml::String("geometry".to_owned()))
+ .ok_or(anyhow!("Missing window geometry"))?
+ .as_str()
+ .ok_or(anyhow!("geometry not string"))?
+ .to_owned(),
+ tabs: hash
+ .get(&Yaml::String("tabs".to_owned()))
+ .ok_or(anyhow!("Missing window tabs"))?
+ .as_vec()
+ .ok_or(anyhow!("Tabs not vec"))?
+ .iter()
+ .map(|tab| {
+ let hash = tab.as_hash().ok_or(anyhow!("Tab not hashmap"))?;
- let uncompressed = decompress_size_prepended(&buf).context("Failed to decompress file")?;
+ Ok::<_, anyhow::Error>(Tab {
+ history: hash
+ .get(&Yaml::String("history".to_owned()))
+ .ok_or(anyhow!("Missing tab history"))?
+ .as_vec()
+ .ok_or(anyhow!("tab history not vec"))?
+ .iter()
+ .map(|history| {
+ let hash = history
+ .as_hash()
+ .ok_or(anyhow!("Tab history not hashmap"))?;
- Ok(String::from_utf8(uncompressed).expect("This should be valid json and thus utf8"))
+ Ok::<_, anyhow::Error>(TabHistory {
+ active: hash
+ .get(&Yaml::String("active".to_owned()))
+ .unwrap_or(&Yaml::Boolean(false))
+ .as_bool()
+ .ok_or(anyhow!("tab history active not bool"))?,
+ last_visited: NaiveDateTime::from_str(
+ hash.get(&Yaml::String("last_visited".to_owned()))
+ .ok_or(anyhow!(
+ "Missing tab history last_visited"
+ ))?
+ .as_str()
+ .ok_or(anyhow!(
+ "tab history last_visited not string"
+ ))?,
+ )
+ .context("Failed to parse last_visited")?,
+ pinned: hash
+ .get(&Yaml::String("pinned".to_owned()))
+ .ok_or(anyhow!("Missing tab history pinned"))?
+ .as_bool()
+ .ok_or(anyhow!("tab history pinned not bool"))?,
+ title: hash
+ .get(&Yaml::String("title".to_owned()))
+ .ok_or(anyhow!("Missing tab history title"))?
+ .as_str()
+ .ok_or(anyhow!("tab history title not string"))?
+ .to_owned(),
+ url: Url::parse(
+ hash.get(&Yaml::String("url".to_owned()))
+ .ok_or(anyhow!("Missing tab history url"))?
+ .as_str()
+ .ok_or(anyhow!("tab history url not string"))?,
+ )
+ .context("Failed to parse url")?,
+ zoom: hash
+ .get(&Yaml::String("zoom".to_owned()))
+ .unwrap_or(&Yaml::Real("1.0".to_owned()))
+ .as_f64()
+ .ok_or(anyhow!("tab history zoom not 64"))?,
+ })
+ })
+ .collect::<Result<Vec<_>, _>>()?,
+ active: hash
+ .get(&Yaml::String("active".to_owned()))
+ .unwrap_or(&Yaml::Boolean(false))
+ .as_bool()
+ .ok_or(anyhow!("active not bool"))?,
+ })
+ })
+ .collect::<Result<Vec<_>, _>>()?,
+ })
+ })
+ .collect::<Result<Vec<_>, _>>()?,
+ })
}
-#[derive(Deserialize, Debug)]
+#[derive(Debug)]
pub struct SessionStore {
pub windows: Vec<Window>,
}
-
-#[derive(Deserialize, Debug)]
+#[derive(Debug)]
pub struct Window {
+ pub geometry: String,
pub tabs: Vec<Tab>,
- pub selected: usize,
}
-
-#[derive(Deserialize, Debug)]
+#[derive(Debug)]
pub struct Tab {
- pub entries: Vec<TabEntry>,
- #[serde(rename = "lastAccessed")]
- pub last_accessed: u64,
- pub hidden: bool,
- #[serde(rename = "searchMode")]
- pub search_mode: Option<Value>,
- #[serde(rename = "userContextId")]
- pub user_context_id: u32,
- pub attributes: TabAttributes,
- #[serde(rename = "extData")]
- pub ext_data: Option<HashMap<String, Value>>,
- pub index: usize,
- #[serde(rename = "requestedIndex")]
- pub requested_index: Option<u32>,
- pub image: Option<Url>,
+ pub history: Vec<TabHistory>,
+ pub active: bool,
}
-
-#[derive(Deserialize, Debug)]
-pub struct TabEntry {
- pub url: Url,
+#[derive(Debug)]
+pub struct TabHistory {
+ pub active: bool,
+ pub last_visited: NaiveDateTime,
+ pub pinned: bool,
+ // pub scroll-pos:
pub title: String,
- #[serde(rename = "cacheKey")]
- pub cache_key: u32,
- #[serde(rename = "ID")]
- pub id: u32,
- #[serde(rename = "docshellUUID")]
- pub docshell_uuid: Value,
- #[serde(rename = "resultPrincipalURI")]
- pub result_principal_uri: Option<Url>,
- #[serde(rename = "hasUserInteraction")]
- pub has_user_interaction: bool,
- #[serde(rename = "triggeringPrincipal_base64")]
- pub triggering_principal_base64: Value,
- #[serde(rename = "docIdentifier")]
- pub doc_identifier: u32,
- pub persist: bool,
+ pub url: Url,
+ pub zoom: f64,
}
-
-#[derive(Deserialize, Debug, Clone, Copy)]
-pub struct TabAttributes {}
diff --git a/pkgs/by-name/ts/tskm/src/interface/project/handle.rs b/pkgs/by-name/ts/tskm/src/interface/project/handle.rs
index 2b01f5d1..6d44b340 100644
--- a/pkgs/by-name/ts/tskm/src/interface/project/handle.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/project/handle.rs
@@ -1,6 +1,16 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
use std::{env, fs::File, io::Write};
-use anyhow::{anyhow, Context, Result};
+use anyhow::{Context, Result, anyhow};
use log::trace;
use crate::{cli::ProjectCommand, task};
@@ -60,10 +70,12 @@ pub fn handle(command: ProjectCommand) -> Result<()> {
let new_definition = ProjectDefinition::default();
- assert!(definition
- .subprojects
- .insert(segment.clone(), new_definition)
- .is_none());
+ assert!(
+ definition
+ .subprojects
+ .insert(segment.clone(), new_definition)
+ .is_none()
+ );
definition = definition
.subprojects
diff --git a/pkgs/by-name/ts/tskm/src/interface/project/mod.rs b/pkgs/by-name/ts/tskm/src/interface/project/mod.rs
index 62069746..8a7fa1b0 100644
--- a/pkgs/by-name/ts/tskm/src/interface/project/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/project/mod.rs
@@ -1,3 +1,13 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
use std::collections::HashMap;
use anyhow::Result;
diff --git a/pkgs/by-name/ts/tskm/src/main.rs b/pkgs/by-name/ts/tskm/src/main.rs
index 7fc9c0d4..a852bd7b 100644
--- a/pkgs/by-name/ts/tskm/src/main.rs
+++ b/pkgs/by-name/ts/tskm/src/main.rs
@@ -1,64 +1,50 @@
-#![allow(clippy::missing_panics_doc)]
-#![allow(clippy::missing_errors_doc)]
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
use anyhow::Result;
-use clap::Parser;
+use clap::{CommandFactory, Parser};
-use crate::interface::{input, neorg, open, project};
+use crate::{
+ cli::{CliArgs, Command},
+ interface::{input, neorg, open, project},
+ state::State,
+};
+pub mod browser;
pub mod cli;
pub mod interface;
pub mod rofi;
+pub mod state;
pub mod task;
-use crate::cli::{CliArgs, Command};
+#[tokio::main]
+async fn main() -> Result<(), anyhow::Error> {
+ clap_complete::CompleteEnv::with_factory(CliArgs::command).complete();
-fn main() -> Result<(), anyhow::Error> {
- // TODO: Support these completions for the respective types <2025-04-04>
- //
- // ID_GENERATION_FUNCTION
- // ```sh
- // context="$(task _get rc.context)"
- // if [ "$context" ]; then
- // filter="project:$context"
- // else
- // filter="0-10000"
- // fi
- // tasks="$(task "$filter" _ids)"
- //
- // if [ "$tasks" ]; then
- // echo "$tasks" | xargs task _zshids | awk -F: -v q="'" '{gsub(/'\''/, q "\\" q q ); print $1 ":" q $2 q}'
- // fi
- // ```
- //
- // ARGUMENTS:
- // ID | *([0-9]) := [[%ID_GENERATION_FUNCTION]]
- // The function displays all possible IDs of the eligible tasks.
- //
- // WS := %ALL_WORKSPACES
- // All possible workspaces.
- //
- // P := %ALL_PROJECTS_PIPE
- // The possible project.
- //
- // F := [[fd . --max-depth 3]]
- // A URL-Input file to use as source.
let args = CliArgs::parse();
stderrlog::new()
.module(module_path!())
- .quiet(false)
+ .quiet(args.quiet)
.show_module_names(true)
.color(stderrlog::ColorChoice::Auto)
- .verbosity(5)
- .timestamp(stderrlog::Timestamp::Off)
+ .verbosity(usize::from(args.verbosity))
.init()
.expect("Let's just hope that this does not panic");
+ let mut state = State::new_rw().await?;
+
match args.command {
- Command::Inputs { command } => input::handle(command)?,
- Command::Neorg { command } => neorg::handle(command)?,
- Command::Open { command } => open::handle(command)?,
+ Command::Inputs { command } => input::handle(command, &mut state).await?,
+ Command::Neorg { command } => neorg::handle(command, &mut state).await?,
+ Command::Open { command } => open::handle(command, &mut state).await?,
Command::Projects { command } => project::handle(command)?,
}
diff --git a/pkgs/by-name/ts/tskm/src/rofi/mod.rs b/pkgs/by-name/ts/tskm/src/rofi/mod.rs
index a0591b7f..37c2eafa 100644
--- a/pkgs/by-name/ts/tskm/src/rofi/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/rofi/mod.rs
@@ -1,3 +1,13 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
use std::{
io::Write,
process::{Command, Stdio},
diff --git a/pkgs/by-name/ts/tskm/src/state.rs b/pkgs/by-name/ts/tskm/src/state.rs
new file mode 100644
index 00000000..57495bb8
--- /dev/null
+++ b/pkgs/by-name/ts/tskm/src/state.rs
@@ -0,0 +1,53 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+use std::path::PathBuf;
+
+use anyhow::Result;
+use taskchampion::{
+ storage::{sqlite::SqliteStorage, AccessMode},
+ Replica,
+};
+
+pub struct State {
+ replica: Replica<SqliteStorage>,
+}
+
+impl std::fmt::Debug for State {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "State")
+ }
+}
+
+impl State {
+ fn taskdb_dir() -> PathBuf {
+ dirs::data_local_dir().expect("Should exist").join("task")
+ }
+
+ async fn new(taskdb_dir: PathBuf, access_mode: AccessMode) -> Result<Self> {
+ let storage = SqliteStorage::new(taskdb_dir, access_mode, false).await?;
+
+ let replica = Replica::new(storage);
+
+ Ok(Self { replica })
+ }
+
+ pub async fn new_ro() -> Result<Self> {
+ Self::new(Self::taskdb_dir(), AccessMode::ReadOnly).await
+ }
+ pub async fn new_rw() -> Result<Self> {
+ Self::new(Self::taskdb_dir(), AccessMode::ReadWrite).await
+ }
+
+ #[must_use]
+ pub fn replica(&mut self) -> &mut Replica<SqliteStorage> {
+ &mut self.replica
+ }
+}
diff --git a/pkgs/by-name/ts/tskm/src/task/mod.rs b/pkgs/by-name/ts/tskm/src/task/mod.rs
index c3a6d614..1362615d 100644
--- a/pkgs/by-name/ts/tskm/src/task/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/task/mod.rs
@@ -1,3 +1,13 @@
+// nixos-config - My current NixOS configuration
+//
+// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// This file is part of my nixos-config.
+//
+// You should have received a copy of the License along with this program.
+// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
use std::{
fmt::Display,
fs::{self, read_to_string, File},
@@ -9,109 +19,148 @@ use std::{
use anyhow::{bail, Context, Result};
use log::{debug, info, trace};
+use taskchampion::Tag;
-use crate::interface::project::ProjectName;
+use crate::{interface::project::ProjectName, state::State};
/// The `taskwarrior` id of a task.
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq)]
-pub struct Id {
- id: u64,
+pub struct Task {
+ uuid: taskchampion::Uuid,
}
-impl Id {
- /// # Errors
- /// When `task` execution fails
- pub fn get_current() -> Result<Option<Self>> {
- // We have ensured that only one task may be active
- let self_str = run_task(&["+ACTIVE", "_ids"])?;
- if self_str.is_empty() {
- Ok(None)
+impl From<&taskchampion::Task> for Task {
+ fn from(value: &taskchampion::Task) -> Self {
+ Self {
+ uuid: value.get_uuid(),
+ }
+ }
+}
+impl From<&taskchampion::TaskData> for Task {
+ fn from(value: &taskchampion::TaskData) -> Self {
+ Self {
+ uuid: value.get_uuid(),
+ }
+ }
+}
+
+impl Task {
+ pub async fn from_working_set(id: usize, state: &mut State) -> Result<Option<Self>> {
+ Ok(state
+ .replica()
+ .working_set()
+ .await?
+ .by_index(id)
+ .map(|uuid| Self { uuid }))
+ }
+
+ pub async fn get_current(state: &mut State) -> Result<Option<Self>> {
+ let tasks = state
+ .replica()
+ .pending_tasks()
+ .await?
+ .into_iter()
+ .filter(taskchampion::Task::is_active)
+ .collect::<Vec<_>>();
+
+ assert!(
+ tasks.len() <= 1,
+ "We have ensured that only one task may be active, via a hook"
+ );
+ if let Some(active) = tasks.first() {
+ Ok(Some(Self::from(active)))
} else {
- Self::from_str(&self_str).map(Some)
+ Ok(None)
}
}
- /// # Errors
- /// When `task` execution fails
- pub fn to_uuid(&self) -> Result<String> {
- let uuid = run_task(&[self.to_string().as_str(), "uuids"])?;
+ #[must_use]
+ pub fn uuid(&self) -> &taskchampion::Uuid {
+ &self.uuid
+ }
+ pub async fn working_set_id(&self, state: &mut State) -> Result<usize> {
+ Ok(state
+ .replica()
+ .working_set()
+ .await?
+ .by_uuid(self.uuid)
+ .expect("The task should be in the working set"))
+ }
- Ok(uuid)
+ async fn as_task(&self, state: &mut State) -> Result<taskchampion::Task> {
+ Ok(state
+ .replica()
+ .get_task(self.uuid)
+ .await?
+ .expect("We have the task from this replica, it should still be in it"))
}
- /// # Panics
- /// When internal assertions fail.
- /// # Errors
- /// When `task` execution fails
- pub fn annotate(&self, message: &str) -> Result<()> {
- run_task(&["annotate", self.to_string().as_str(), "--", message])?;
+ /// Adds a tag to the task, to show the user that it has additional neorg data.
+ pub async fn mark_neorg_data(&self, state: &mut State) -> Result<()> {
+ let mut ops = vec![];
+ self.as_task(state)
+ .await?
+ .add_tag(&Tag::from_str("neorg_data").expect("Is valid"), &mut ops)?;
+ state.replica().commit_operations(ops).await?;
Ok(())
}
- /// # Panics
- /// When internal assertions fail.
- /// # Errors
- /// When `task` execution fails
- pub fn start(&self) -> Result<()> {
+ /// Try to start this task.
+ /// It will stop previously active tasks.
+ pub async fn start(&self, state: &mut State) -> Result<()> {
info!("Activating {self}");
- let output = run_task(&["start", self.to_string().as_str()])?;
- assert!(output.is_empty());
+ if let Some(active) = Self::get_current(state).await? {
+ active.stop(state).await?;
+ }
+
+ let mut ops = vec![];
+ self.as_task(state).await?.start(&mut ops)?;
+ state.replica().commit_operations(ops).await?;
Ok(())
}
- /// # Panics
- /// When internal assertions fail.
- /// # Errors
- /// When `task` execution fails
- pub fn stop(&self) -> Result<()> {
+
+ /// Stops this task.
+ pub async fn stop(&self, state: &mut State) -> Result<()> {
info!("Stopping {self}");
- let output = run_task(&["stop", self.to_string().as_str()])?;
- assert!(output.is_empty());
+ let mut ops = vec![];
+ self.as_task(state).await?.stop(&mut ops)?;
+ state.replica().commit_operations(ops).await?;
Ok(())
}
- /// # Panics
- /// When internal assertions fail.
- /// # Errors
- /// When `task` execution fails
- pub fn description(&self) -> Result<String> {
- let output = run_task(&["rc.context=none", "_zshids", self.to_string().as_str()])?;
- let (id, desc) = output
- .split_once(':')
- .expect("The output should always contain one colon");
- assert_eq!(id.parse::<Id>().expect("This should be a valid id"), *self);
- Ok(desc.to_owned())
+ pub async fn description(&self, state: &mut State) -> Result<String> {
+ Ok(self.as_task(state).await?.get_description().to_owned())
}
- /// # Panics
- /// When internal assertions fail.
- /// # Errors
- /// When `task` execution fails
- pub fn project(&self) -> Result<Project> {
- let output = run_task(&[
- "rc.context=none",
- "_get",
- format!("{self}.project").as_str(),
- ])?;
- let project = Project::from_project_string(output.as_str())
+ pub async fn project(&self, state: &mut State) -> Result<Project> {
+ let output = {
+ let task = self.as_task(state).await?;
+ let task_data = task.into_task_data();
+ task_data
+ .get("project")
+ .expect("Every task should have a project")
+ .to_owned()
+ };
+ let project = Project::from_project_string(output.as_str().trim())
.expect("This comes from tw, it should be valid");
Ok(project)
}
}
-impl Display for Id {
+impl Display for Task {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- self.id.fmt(f)
+ self.uuid.fmt(f)
}
}
-impl FromStr for Id {
+impl FromStr for Task {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
- let id = u64::from_str(s)?;
- Ok(Self { id })
+ let uuid = taskchampion::Uuid::from_str(s)?;
+ Ok(Self { uuid })
}
}
@@ -259,21 +308,15 @@ impl Project {
/// # Errors
/// When `task` execution fails.
- pub fn get_tasks(&self) -> Result<Vec<Id>> {
- let output = run_task(&[
- "rc.context=none",
- format!("project:{}", self.to_project_display()).as_str(),
- "_ids",
- ])?;
-
- if output.is_empty() {
- Ok(vec![])
- } else {
- output
- .lines()
- .map(Id::from_str)
- .collect::<Result<Vec<Id>>>()
- }
+ pub async fn get_tasks(&self, state: &mut State) -> Result<Vec<Task>> {
+ Ok(state
+ .replica()
+ .pending_task_data()
+ .await?
+ .into_iter()
+ .filter(|t| t.get("project").expect("Is set") == self.to_project_display())
+ .map(|t| Task::from(&t))
+ .collect())
}
/// # Errors
@@ -318,5 +361,13 @@ pub(crate) fn run_task(args: &[&str]) -> Result<String> {
trace!("Output (stdout): '{}'", stdout.trim());
trace!("Output (stderr): '{}'", stderr.trim());
+ if !output.status.success() {
+ bail!(
+ "Command `task {}` failed with status: {}",
+ args.join(" "),
+ output.status
+ );
+ }
+
Ok(stdout.trim().to_owned())
}
diff --git a/pkgs/by-name/ts/tskm/update.sh b/pkgs/by-name/ts/tskm/update.sh
index 9268caf2..5ad524e8 100755
--- a/pkgs/by-name/ts/tskm/update.sh
+++ b/pkgs/by-name/ts/tskm/update.sh
@@ -1,3 +1,14 @@
#!/bin/sh
-cargo update && cargo upgrade
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+[ "$1" = "upgrade" ] && cargo upgrade --incompatible allow --pinned allow --recursive true
+cargo update --recursive