about summary refs log tree commit diff stats
path: root/pkgs/by-name/ts/tskm/src/task/mod.rs
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/by-name/ts/tskm/src/task/mod.rs
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 '')
-rw-r--r--pkgs/by-name/ts/tskm/src/task/mod.rs176
1 files changed, 98 insertions, 78 deletions
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