diff --git a/frontend/templates/undo_report.html b/frontend/templates/undo_report.html
index 69b9019..36664b6 100644
--- a/frontend/templates/undo_report.html
+++ b/frontend/templates/undo_report.html
@@ -5,11 +5,11 @@
{{ heading }}
- {% for vv in undo_report %}
- {% for operation in vv.1 %}
+ {% for kv, vv in undo_report %}
+ {% for operation in vv %}
{% if loop.first %}
- | {{ vv.0 }} |
+ {{ kv }} |
|
diff --git a/src/backend/task.rs b/src/backend/task.rs
index 52c7cd5..f1cec5f 100644
--- a/src/backend/task.rs
+++ b/src/backend/task.rs
@@ -74,13 +74,13 @@ impl TryFrom<&str> for TaskProperties {
}
}
-pub fn convert_task_status(task_status: String) -> taskchampion::Status {
- match task_status.as_str() {
+pub fn convert_task_status(task_status: &str) -> taskchampion::Status {
+ match task_status {
"pending" => taskchampion::Status::Pending,
"completed" => taskchampion::Status::Completed,
"deleted" => taskchampion::Status::Deleted,
"recurring" => taskchampion::Status::Recurring,
- &_ => taskchampion::Status::Unknown(task_status),
+ &_ => taskchampion::Status::Unknown(task_status.into()),
}
}
diff --git a/src/core/errors.rs b/src/core/errors.rs
index 9189bab..0e7d8d8 100644
--- a/src/core/errors.rs
+++ b/src/core/errors.rs
@@ -105,6 +105,8 @@ impl FormValidation {
}
}
+ /// Check if any validation errors occured or if no errors were recognized.
+ /// If everything went fine, `is_success` returns `true`.
pub fn is_success(&self) -> bool {
self.success
}
@@ -121,6 +123,9 @@ impl FormValidation {
self
}
+ /// Checks whether errors occured for given `field`.
+ /// If at least one error to the given `field`, a `true`
+ /// is returned.
pub fn has_error(&self, field: &str) -> bool {
self.fields.contains_key(field)
}
diff --git a/src/endpoints/tasks/mod.rs b/src/endpoints/tasks/mod.rs
index 727253d..3b39854 100644
--- a/src/endpoints/tasks/mod.rs
+++ b/src/endpoints/tasks/mod.rs
@@ -10,16 +10,19 @@ use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::process::Command;
use std::str::FromStr;
-use taskchampion::{Operations, Status, Tag, Uuid};
+use task_modify::{
+ task_apply_depends, task_apply_description, task_apply_priority, task_apply_recur,
+ task_apply_status, task_apply_tag_add, task_apply_tag_remove, task_apply_timestamps,
+};
+use taskchampion::{Operations, Replica, Status, Tag, Uuid};
use tera::Context;
use tracing::{debug, error, info, trace};
pub mod task_query_builder;
use crate::backend::task::{
- denotate_task,
- convert_task_status, execute_hooks, get_replica, get_task, Annotation, TaskEvent, TaskProperties,
-
+ convert_task_status, denotate_task, execute_hooks, get_replica, get_task, Annotation,
+ TaskEvent, TaskProperties,
};
use crate::core::app::{get_default_context, AppState};
use crate::core::errors::{FieldError, FormValidation};
@@ -27,6 +30,8 @@ use crate::core::utils::make_shortcut;
use crate::{NewTask, TWGlobalState, TaskUpdateStatus, TEMPLATES};
use task_query_builder::TaskQuery;
+pub(crate) mod task_modify;
+
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct Task {
pub id: i64,
@@ -119,6 +124,7 @@ fn read_task_file(
fn parse_apply_additions(
t: &mut taskchampion::Task,
+ replica: &mut Replica,
mut ops: &mut Vec,
additional: &String,
validation_result: &mut FormValidation,
@@ -137,35 +143,31 @@ fn parse_apply_additions(
// it might be a task operation if it starts with +/- without a value.
if b1.0.starts_with("+") && b1.1.is_none() {
- let tag_name = b1.0.strip_prefix("+").unwrap();
- match &Tag::from_str(tag_name).map_err(|p| FieldError {
- field: "additional".to_string(),
- message: p.to_string(),
- }) {
- Ok(tag) => match t.add_tag(tag, &mut ops).map_err(|p| FieldError {
- field: "additional".to_string(),
- message: p.to_string(),
- }) {
- Ok(_) => (),
- Err(e) => validation_result.push(e),
- },
- Err(e) => validation_result.push(e.to_owned()),
- };
+ task_apply_tag_add(t, ops, validation_result, b1);
} else if b1.0.starts_with("-") && b1.1.is_none() {
- let tag_name = b1.0.strip_prefix("-").unwrap();
- match &Tag::from_str(tag_name).map_err(|p| FieldError {
- field: "additional".to_string(),
- message: p.to_string(),
- }) {
- Ok(tag) => match t.add_tag(tag, &mut ops).map_err(|p| FieldError {
- field: "additional".to_string(),
- message: p.to_string(),
- }) {
- Ok(_) => (),
- Err(e) => validation_result.push(e),
- },
- Err(e) => validation_result.push(e.to_owned()),
- };
+ task_apply_tag_remove(t, ops, validation_result, b1);
+ } else if b1.0.to_lowercase().as_str() == "depends" {
+ task_apply_depends(t, replica, ops, validation_result, b1);
+ } else if b1.0.to_lowercase().trim() == "description" {
+ task_apply_description(t, ops, validation_result, b1);
+ } else if b1.0.to_lowercase().trim() == "priority" {
+ task_apply_priority(t, ops, validation_result, b1);
+ } else if ["entry", "wait", "due"].contains(&b1.0.to_lowercase().trim()) {
+ task_apply_timestamps(t, ops, validation_result, b1);
+ } else if b1.0.to_lowercase().trim() == "status" {
+ task_apply_status(t, ops, validation_result, b1);
+ } else if b1.0.to_lowercase().trim() == "recur" {
+ task_apply_recur(t, ops, validation_result, b1);
+ } else if ["start", "stop", "done", "end", "modified"]
+ .contains(&b1.0.to_lowercase().trim())
+ {
+ validation_result.push(FieldError {
+ field: "additional".into(),
+ message: format!(
+ "Manual modification of the field {} is not allowed.",
+ b1.0
+ ),
+ });
} else if let Ok(_) = TaskProperties::try_from(b1.0.as_str()) {
match t.set_value(b1.0, b1.1, &mut ops).map_err(|p| FieldError {
field: "additional".to_string(),
@@ -279,7 +281,13 @@ pub fn task_add(task: &NewTask, app_state: &AppState) -> Result<(), FormValidati
}
if let Some(additional) = task.additional() {
- parse_apply_additions(&mut t, &mut ops, additional, &mut validation_result);
+ parse_apply_additions(
+ &mut t,
+ &mut replica,
+ &mut ops,
+ additional,
+ &mut validation_result,
+ );
}
match validation_result.is_success() {
@@ -328,6 +336,7 @@ pub fn run_modify_command(
let old_task = t.clone();
parse_apply_additions(
&mut t,
+ &mut replica,
&mut ops,
&cmd_text.to_string(),
&mut validation_result,
@@ -421,12 +430,10 @@ pub fn change_task_status(
let mut ops = Operations::new();
ops.push(taskchampion::Operation::UndoPoint);
- let mut t = replica
- .get_task(task.uuid)?
- .expect("Task does not exist");
+ let mut t = replica.get_task(task.uuid)?.expect("Task does not exist");
let old_task = t.clone();
- let task_status = convert_task_status(task.status);
+ let task_status = convert_task_status(&task.status);
// Stop tasks.
if t.is_active() {
@@ -480,7 +487,11 @@ pub fn fetch_active_task() -> Result