aboutsummaryrefslogtreecommitdiffstats
path: root/pkgs
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-04-06 18:36:27 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-04-06 18:38:03 +0200
commita9db63802db2293ac4ee280394568b09f6feaa87 (patch)
tree32b00aa17fda1bf11bf87fdefa71b77d2bc44348 /pkgs
parentfix(modules/taskwarrior/mkHook): Use correct `grep` silencing argument (diff)
downloadnixos-config-a9db63802db2293ac4ee280394568b09f6feaa87.zip
feat(pkgs/tskm/task): Use taskchampion instead of run_task
Diffstat (limited to 'pkgs')
-rw-r--r--pkgs/by-name/ts/tskm/Cargo.lock235
-rw-r--r--pkgs/by-name/ts/tskm/Cargo.toml2
-rw-r--r--pkgs/by-name/ts/tskm/build.rs3
-rw-r--r--pkgs/by-name/ts/tskm/flake.nix4
-rw-r--r--pkgs/by-name/ts/tskm/package.nix2
-rw-r--r--pkgs/by-name/ts/tskm/src/cli.rs20
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs10
-rw-r--r--pkgs/by-name/ts/tskm/src/interface/open/handle.rs27
-rw-r--r--pkgs/by-name/ts/tskm/src/main.rs8
-rw-r--r--pkgs/by-name/ts/tskm/src/state.rs45
-rw-r--r--pkgs/by-name/ts/tskm/src/task/mod.rs176
11 files changed, 429 insertions, 103 deletions
diff --git a/pkgs/by-name/ts/tskm/Cargo.lock b/pkgs/by-name/ts/tskm/Cargo.lock
index 4a064858..68823d3c 100644
--- a/pkgs/by-name/ts/tskm/Cargo.lock
+++ b/pkgs/by-name/ts/tskm/Cargo.lock
@@ -3,6 +3,24 @@
version = 4
[[package]]
+name = "adler2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
+[[package]]
+name = "ahash"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -92,6 +110,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -116,6 +140,7 @@ dependencies = [
"iana-time-zone",
"js-sys",
"num-traits",
+ "serde",
"wasm-bindgen",
"windows-link",
]
@@ -182,6 +207,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
+name = "crc32fast"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
name = "dirs"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -214,6 +248,28 @@ dependencies = [
]
[[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 = "flate2"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
name = "form_urlencoded"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -230,7 +286,37 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasi 0.14.2+wasi-0.2.4",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hashlink"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
+dependencies = [
+ "hashbrown",
]
[[package]]
@@ -458,6 +544,16 @@ dependencies = [
]
[[package]]
+name = "libsqlite3-sys"
+version = "0.30.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
+dependencies = [
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
name = "litemap"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -485,6 +581,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
+name = "miniz_oxide"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430"
+dependencies = [
+ "adler2",
+]
+
+[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -512,6 +617,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
name = "proc-macro2"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -530,17 +641,37 @@ dependencies = [
]
[[package]]
+name = "r-efi"
+version = "5.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
+
+[[package]]
name = "redox_users"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
dependencies = [
- "getrandom",
+ "getrandom 0.2.15",
"libredox",
"thiserror",
]
[[package]]
+name = "rusqlite"
+version = "0.32.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e"
+dependencies = [
+ "bitflags",
+ "fallible-iterator",
+ "fallible-streaming-iterator",
+ "hashlink",
+ "libsqlite3-sys",
+ "smallvec",
+]
+
+[[package]]
name = "rustversion"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -637,6 +768,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
+name = "strum"
+version = "0.27.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
+
+[[package]]
+name = "strum_macros"
+version = "0.27.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn",
+]
+
+[[package]]
name = "syn"
version = "2.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -659,6 +809,26 @@ dependencies = [
]
[[package]]
+name = "taskchampion"
+version = "2.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b010f5ebe51e88ae490691ed2a43b699e3468c8e3838e244accd8526aca7751b"
+dependencies = [
+ "anyhow",
+ "byteorder",
+ "chrono",
+ "flate2",
+ "log",
+ "rusqlite",
+ "serde",
+ "serde_json",
+ "strum",
+ "strum_macros",
+ "thiserror",
+ "uuid",
+]
+
+[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -720,6 +890,7 @@ dependencies = [
"serde",
"serde_json",
"stderrlog",
+ "taskchampion",
"url",
"walkdir",
]
@@ -771,6 +942,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
+name = "uuid"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
+dependencies = [
+ "getrandom 0.3.2",
+ "serde",
+]
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -787,6 +980,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
+name = "wasi"
+version = "0.14.2+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
+dependencies = [
+ "wit-bindgen-rt",
+]
+
+[[package]]
name = "wasm-bindgen"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -942,6 +1144,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
+name = "wit-bindgen-rt"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
name = "write16"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -978,6 +1189,26 @@ dependencies = [
]
[[package]]
+name = "zerocopy"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "zerofrom"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/pkgs/by-name/ts/tskm/Cargo.toml b/pkgs/by-name/ts/tskm/Cargo.toml
index d2990b0c..5b09285c 100644
--- a/pkgs/by-name/ts/tskm/Cargo.toml
+++ b/pkgs/by-name/ts/tskm/Cargo.toml
@@ -14,6 +14,7 @@ lz4_flex = "0.11.3"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"
stderrlog = "0.6.0"
+taskchampion = { version = "2.0.3", default-features = false }
url = { version = "2.5.4", features = ["serde"] }
walkdir = "2.5.0"
@@ -83,5 +84,6 @@ dirs = "6.0.0"
log = "0.4.27"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"
+taskchampion = { version = "2.0.3", default-features = false }
url = "2.5.4"
walkdir = "2.5.0"
diff --git a/pkgs/by-name/ts/tskm/build.rs b/pkgs/by-name/ts/tskm/build.rs
index 8dfb213b..e3b60bb9 100644
--- a/pkgs/by-name/ts/tskm/build.rs
+++ b/pkgs/by-name/ts/tskm/build.rs
@@ -12,6 +12,9 @@ use crate::cli::CliArgs;
pub mod task {
include!("src/task/mod.rs");
}
+pub mod state {
+ include!("src/state.rs");
+}
pub mod interface {
pub mod input {
diff --git a/pkgs/by-name/ts/tskm/flake.nix b/pkgs/by-name/ts/tskm/flake.nix
index 4b1142d6..5a5f628b 100644
--- a/pkgs/by-name/ts/tskm/flake.nix
+++ b/pkgs/by-name/ts/tskm/flake.nix
@@ -10,6 +10,10 @@
pkgs = nixpkgs.legacyPackages."${system}";
in {
devShells."${system}".default = pkgs.mkShell {
+ buildInputs = [
+ pkgs.sqlite
+ ];
+
packages = with pkgs; [
cargo
clippy
diff --git a/pkgs/by-name/ts/tskm/package.nix b/pkgs/by-name/ts/tskm/package.nix
index 1f206abf..3d320772 100644
--- a/pkgs/by-name/ts/tskm/package.nix
+++ b/pkgs/by-name/ts/tskm/package.nix
@@ -8,6 +8,7 @@
git,
rofi,
firefox,
+ sqlite,
}:
rustPlatform.buildRustPackage (finalAttrs: {
pname = "tskm";
@@ -27,6 +28,7 @@ rustPlatform.buildRustPackage (finalAttrs: {
git
rofi
firefox
+ sqlite
];
nativeBuildInputs = [
diff --git a/pkgs/by-name/ts/tskm/src/cli.rs b/pkgs/by-name/ts/tskm/src/cli.rs
index 99c8693e..bc79866a 100644
--- a/pkgs/by-name/ts/tskm/src/cli.rs
+++ b/pkgs/by-name/ts/tskm/src/cli.rs
@@ -1,9 +1,11 @@
use std::path::PathBuf;
-use clap::{Parser, Subcommand};
+use anyhow::{bail, Result};
+use clap::{ArgAction, Parser, Subcommand};
use crate::{
interface::{input::Input, project::ProjectName},
+ state::State,
task,
};
@@ -66,7 +68,21 @@ 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_parser = task_from_working_set_id)]
+ id: task::Task,
+ },
+}
+
+fn task_from_working_set_id(id: &str) -> Result<task::Task> {
+ let id: usize = id.parse()?;
+ let mut state = State::new_ro()?;
+
+ let Some(task) = task::Task::from_working_set(id, &mut state)? else {
+ bail!("Working set id '{id}' is not valid!")
+ };
+ Ok(task)
}
#[derive(Subcommand, Debug)]
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..45e1f916 100644
--- a/pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/neorg/handle.rs
@@ -7,12 +7,12 @@ use std::{
use anyhow::{bail, Result};
-use crate::cli::NeorgCommand;
+use crate::{cli::NeorgCommand, state::State};
-pub fn handle(command: NeorgCommand) -> Result<()> {
+pub fn handle(command: NeorgCommand, state: &mut State) -> Result<()> {
match command {
NeorgCommand::Task { id } => {
- let project = id.project()?;
+ let project = id.project(state)?;
let path = dirs::data_local_dir()
.expect("This should exists")
.join("notes")
@@ -36,7 +36,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!("/% {}", id.uuid()).as_str(),
])
.status()?;
if !status.success() {
@@ -71,7 +71,7 @@ pub fn handle(command: NeorgCommand) -> Result<()> {
}
{
- id.annotate("[neorg data]")?;
+ id.mark_neorg_data(state)?;
}
}
}
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..0b565abd 100644
--- a/pkgs/by-name/ts/tskm/src/interface/open/handle.rs
+++ b/pkgs/by-name/ts/tskm/src/interface/open/handle.rs
@@ -3,15 +3,15 @@ use std::process;
use anyhow::{bail, Context, Result};
use log::{error, info};
-use crate::{cli::OpenCommand, rofi, task};
+use crate::{cli::OpenCommand, rofi, state::State, task};
-pub fn handle(command: OpenCommand) -> Result<()> {
+pub fn handle(command: OpenCommand, state: &mut State) -> Result<()> {
match command {
OpenCommand::Review => {
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(|| {
+ open_in_browser(project, state).with_context(|| {
format!(
"Failed to open project ('{}') in Firefox",
project.to_project_display()
@@ -38,7 +38,7 @@ pub fn handle(command: OpenCommand) -> Result<()> {
};
project.touch().context("Failed to touch project")?;
- open_in_browser(&project).with_context(|| {
+ open_in_browser(&project, state).with_context(|| {
format!("Failed to open project: {}", project.to_project_display())
})?;
}
@@ -60,7 +60,7 @@ 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).context("Failed to open project")?;
}
OpenCommand::ListTabs { project } => {
let project = if let Some(p) = project {
@@ -109,12 +109,11 @@ pub fn handle(command: OpenCommand) -> Result<()> {
Ok(())
}
-fn open_in_browser(selected_project: &task::Project) -> Result<()> {
+fn open_in_browser(selected_project: &task::Project, state: &mut State) -> 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")?;
+ let old_task: Option<task::Task> =
+ task::Task::get_current(state).context("Failed to get currently active task")?;
selected_project.activate().with_context(|| {
format!(
@@ -124,7 +123,7 @@ fn open_in_browser(selected_project: &task::Project) -> Result<()> {
})?;
let tracking_task = {
- let all_tasks = selected_project.get_tasks().with_context(|| {
+ let all_tasks = selected_project.get_tasks(state).with_context(|| {
format!(
"Failed to get assoctiated tasks for project: '{}'",
selected_project.to_project_display()
@@ -132,7 +131,7 @@ fn open_in_browser(selected_project: &task::Project) -> Result<()> {
})?;
let tracking_task = all_tasks.into_iter().find(|t| {
- let maybe_desc = t.description();
+ let maybe_desc = t.description(state);
if let Ok(desc) = maybe_desc {
desc == "tracking"
} else {
@@ -149,7 +148,7 @@ fn open_in_browser(selected_project: &task::Project) -> Result<()> {
"Starting task {} -> tracking",
selected_project.to_project_display()
);
- task.start()
+ task.start(state)
.with_context(|| format!("Failed to start task {task}"))?;
}
tracking_task
@@ -169,11 +168,11 @@ fn open_in_browser(selected_project: &task::Project) -> Result<()> {
}
if let Some(task) = tracking_task {
- task.stop()
+ task.stop(state)
.with_context(|| format!("Failed to stop task {task}"))?;
}
if let Some(task) = old_task {
- task.start()
+ task.start(state)
.with_context(|| format!("Failed to start task {task}"))?;
}
diff --git a/pkgs/by-name/ts/tskm/src/main.rs b/pkgs/by-name/ts/tskm/src/main.rs
index 7fc9c0d4..6e506895 100644
--- a/pkgs/by-name/ts/tskm/src/main.rs
+++ b/pkgs/by-name/ts/tskm/src/main.rs
@@ -3,12 +3,14 @@
use anyhow::Result;
use clap::Parser;
+use state::State;
use crate::interface::{input, neorg, open, project};
pub mod cli;
pub mod interface;
pub mod rofi;
+pub mod state;
pub mod task;
use crate::cli::{CliArgs, Command};
@@ -55,10 +57,12 @@ fn main() -> Result<(), anyhow::Error> {
.init()
.expect("Let's just hope that this does not panic");
+ let mut state = State::new_rw()?;
+
match args.command {
Command::Inputs { command } => input::handle(command)?,
- Command::Neorg { command } => neorg::handle(command)?,
- Command::Open { command } => open::handle(command)?,
+ Command::Neorg { command } => neorg::handle(command, &mut state)?,
+ Command::Open { command } => open::handle(command, &mut state)?,
Command::Projects { command } => project::handle(command)?,
}
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..175a7f03
--- /dev/null
+++ b/pkgs/by-name/ts/tskm/src/state.rs
@@ -0,0 +1,45 @@
+use std::path::PathBuf;
+
+use anyhow::Result;
+use taskchampion::{storage::AccessMode, Replica, StorageConfig};
+
+pub struct State {
+ replica: Replica,
+}
+
+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")
+ }
+
+ fn new(taskdb_dir: PathBuf, access_mode: AccessMode) -> Result<Self> {
+ let storage = StorageConfig::OnDisk {
+ taskdb_dir,
+ create_if_missing: false,
+ access_mode,
+ }
+ .into_storage()?;
+
+ let replica = Replica::new(storage);
+
+ Ok(Self { replica })
+ }
+
+ pub fn new_ro() -> Result<Self> {
+ Self::new(Self::taskdb_dir(), AccessMode::ReadOnly)
+ }
+ pub fn new_rw() -> Result<Self> {
+ Self::new(Self::taskdb_dir(), AccessMode::ReadWrite)
+ }
+
+ #[must_use]
+ pub fn replica(&mut self) -> &mut Replica {
+ &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..03a12faa 100644
--- a/pkgs/by-name/ts/tskm/src/task/mod.rs
+++ b/pkgs/by-name/ts/tskm/src/task/mod.rs
@@ -9,109 +9,136 @@ 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 fn from_working_set(id: usize, state: &mut State) -> Result<Option<Self>> {
+ Ok(state
+ .replica()
+ .working_set()?
+ .by_index(id)
+ .map(|uuid| Self { uuid }))
+ }
+
+ pub fn get_current(state: &mut State) -> Result<Option<Self>> {
+ let tasks = state
+ .replica()
+ .pending_tasks()?
+ .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
+ }
- Ok(uuid)
+ fn as_task(&self, state: &mut State) -> Result<taskchampion::Task> {
+ Ok(state
+ .replica()
+ .get_task(self.uuid)?
+ .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 fn mark_neorg_data(&self, state: &mut State) -> Result<()> {
+ let mut ops = vec![];
+ self.as_task(state)?
+ .add_tag(&Tag::from_str("neorg_data").expect("Is valid"), &mut ops)?;
+ state.replica().commit_operations(ops)?;
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 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)? {
+ active.stop(state)?;
+ }
+
+ let mut ops = vec![];
+ self.as_task(state)?.start(&mut ops)?;
+ state.replica().commit_operations(ops)?;
Ok(())
}
- /// # Panics
- /// When internal assertions fail.
- /// # Errors
- /// When `task` execution fails
- pub fn stop(&self) -> Result<()> {
+
+ /// Stops this task.
+ pub 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)?.stop(&mut ops)?;
+ state.replica().commit_operations(ops)?;
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 fn description(&self, state: &mut State) -> Result<String> {
+ Ok(self.as_task(state)?.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(),
- ])?;
+ pub fn project(&self, state: &mut State) -> Result<Project> {
+ let output = {
+ let task = self.as_task(state)?;
+ 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())
.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 +286,14 @@ 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 fn get_tasks(&self, state: &mut State) -> Result<Vec<Task>> {
+ Ok(state
+ .replica()
+ .pending_task_data()?
+ .into_iter()
+ .filter(|t| t.get("project").expect("Is set") == self.to_project_display())
+ .map(|t| Task::from(&t))
+ .collect())
}
/// # Errors