feat: Add TaskWarrior integration with TaskChampion
- Error handling with TaskError enum and TaskResult<T> alias - Data models: Task, TaskPriority, TaskStatus, TaskAnnotation - Advanced filtering: TaskFilter with hierarchical projects, tags (AND/OR), priority, due dates, and search - TaskService: Full CRUD, tags, projects, annotations, dependencies, sync
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TaskPriority {
|
||||
High,
|
||||
Medium,
|
||||
Low,
|
||||
None,
|
||||
}
|
||||
|
||||
impl Default for TaskPriority {
|
||||
fn default() -> Self {
|
||||
TaskPriority::None
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TaskPriority {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TaskPriority::High => write!(f, "High"),
|
||||
TaskPriority::Medium => write!(f, "Medium"),
|
||||
TaskPriority::Low => write!(f, "Low"),
|
||||
TaskPriority::None => write!(f, "None"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for TaskPriority {
|
||||
fn from(s: &str) -> Self {
|
||||
match s {
|
||||
"high" | "High" | "H" | "h" => TaskPriority::High,
|
||||
"medium" | "Medium" | "M" | "m" => TaskPriority::Medium,
|
||||
"low" | "Low" | "L" | "l" => TaskPriority::Low,
|
||||
_ => TaskPriority::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for TaskPriority {
|
||||
fn from(s: String) -> Self {
|
||||
Self::from(s.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum TaskStatus {
|
||||
Pending,
|
||||
Completed,
|
||||
Deleted,
|
||||
Unknown(String),
|
||||
Recurring,
|
||||
}
|
||||
|
||||
impl Default for TaskStatus {
|
||||
fn default() -> Self {
|
||||
TaskStatus::Pending
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for TaskStatus {
|
||||
fn from(s: &str) -> Self {
|
||||
match s {
|
||||
"pending" | "Pending" | "P" | "p" => TaskStatus::Pending,
|
||||
"completed" | "Completed" | "C" | "c" => TaskStatus::Completed,
|
||||
"deleted" | "Deleted" | "D" | "d" => TaskStatus::Deleted,
|
||||
"recurring" | "Recurring" | "R" | "r" => TaskStatus::Recurring,
|
||||
_ => TaskStatus::Unknown(String::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for TaskStatus {
|
||||
fn from(s: String) -> Self {
|
||||
Self::from(s.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<taskchampion::Status> for TaskStatus {
|
||||
fn from(status: taskchampion::Status) -> Self {
|
||||
match status {
|
||||
taskchampion::Status::Pending => TaskStatus::Pending,
|
||||
taskchampion::Status::Completed => TaskStatus::Completed,
|
||||
taskchampion::Status::Deleted => TaskStatus::Deleted,
|
||||
taskchampion::Status::Unknown(reason) => TaskStatus::Unknown(reason),
|
||||
taskchampion::Status::Recurring => TaskStatus::Recurring,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TaskStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TaskStatus::Pending => write!(f, "Pending"),
|
||||
TaskStatus::Completed => write!(f, "Completed"),
|
||||
TaskStatus::Deleted => write!(f, "Deleted"),
|
||||
TaskStatus::Unknown(reason) => write!(f, "Unknown ({})", reason),
|
||||
TaskStatus::Recurring => write!(f, "Recurring"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct TaskAnnotation {
|
||||
pub entry: DateTime<Utc>,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl From<taskchampion::Annotation> for TaskAnnotation {
|
||||
fn from(annotation: taskchampion::Annotation) -> Self {
|
||||
TaskAnnotation {
|
||||
entry: annotation.entry,
|
||||
content: annotation.description,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Task {
|
||||
pub uuid: uuid::Uuid,
|
||||
pub status: TaskStatus,
|
||||
pub description: String,
|
||||
pub project: Option<String>,
|
||||
pub priority: TaskPriority,
|
||||
pub tags: HashSet<String>,
|
||||
pub due: Option<DateTime<Utc>>,
|
||||
pub wait: Option<DateTime<Utc>>,
|
||||
pub entry: Option<DateTime<Utc>>,
|
||||
pub modified: Option<DateTime<Utc>>,
|
||||
pub annotations: Vec<TaskAnnotation>,
|
||||
pub dependencies: HashSet<uuid::Uuid>,
|
||||
pub is_active: bool,
|
||||
pub is_blocked: bool,
|
||||
pub working_id: Option<usize>,
|
||||
}
|
||||
|
||||
impl Task {
|
||||
pub fn new(
|
||||
uuid: uuid::Uuid,
|
||||
status: TaskStatus,
|
||||
description: String,
|
||||
project: Option<String>,
|
||||
priority: TaskPriority,
|
||||
tags: HashSet<String>,
|
||||
due: Option<DateTime<Utc>>,
|
||||
wait: Option<DateTime<Utc>>,
|
||||
entry: Option<DateTime<Utc>>,
|
||||
modified: Option<DateTime<Utc>>,
|
||||
annotations: Vec<TaskAnnotation>,
|
||||
dependencies: HashSet<uuid::Uuid>,
|
||||
is_active: bool,
|
||||
is_blocked: bool,
|
||||
working_id: Option<usize>,
|
||||
) -> Self {
|
||||
Self {
|
||||
uuid,
|
||||
status,
|
||||
description,
|
||||
project,
|
||||
priority,
|
||||
tags,
|
||||
due,
|
||||
wait,
|
||||
entry,
|
||||
modified,
|
||||
annotations,
|
||||
dependencies,
|
||||
is_active,
|
||||
is_blocked,
|
||||
working_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_overdue(&self) -> bool {
|
||||
self.due.map_or(false, |due| due < Utc::now())
|
||||
}
|
||||
|
||||
pub fn is_due_today(&self) -> bool {
|
||||
self.due
|
||||
.map_or(false, |due| due.date_naive() == Utc::now().date_naive())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<taskchampion::Task> for Task {
|
||||
fn from(task: taskchampion::Task) -> Self {
|
||||
Self {
|
||||
uuid: task.get_uuid(),
|
||||
status: task.get_status().into(),
|
||||
description: task.get_description().to_string(),
|
||||
project: task.get_value("project").map(|v| v.to_string()),
|
||||
priority: task.get_priority().into(),
|
||||
tags: task.get_tags().map(|t| t.to_string()).collect(),
|
||||
due: task.get_due().map(Into::into),
|
||||
wait: task.get_wait().map(Into::into),
|
||||
entry: task.get_entry().map(Into::into),
|
||||
modified: task.get_modified().map(Into::into),
|
||||
annotations: task.get_annotations().map(Into::into).collect(),
|
||||
dependencies: task.get_dependencies().map(Into::into).collect(),
|
||||
is_active: task.is_active(),
|
||||
is_blocked: task.is_blocked(),
|
||||
working_id: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TaskUpdate {
|
||||
pub description: Option<String>,
|
||||
pub project: Option<String>,
|
||||
pub priority: Option<String>,
|
||||
pub tags: Option<HashSet<String>>,
|
||||
pub due: Option<DateTime<Utc>>,
|
||||
pub wait: Option<DateTime<Utc>>,
|
||||
pub annotations: Option<Vec<String>>,
|
||||
pub dependencies: Option<Vec<String>>,
|
||||
}
|
||||
Reference in New Issue
Block a user