Skip to main content

oxinbox_core/
lib.rs

1use chrono::{DateTime, NaiveDate, Utc};
2use serde::{Deserialize, Serialize};
3
4pub use uuid::Uuid;
5
6pub use chrono;
7pub use serde;
8pub use uuid;
9
10#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
11#[serde(rename_all = "lowercase")]
12pub enum TaskStatus {
13    Inbox,
14    Todo,
15    Doing,
16    Done,
17    Someday,
18}
19
20#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
21pub struct Task {
22    pub id: Uuid,
23    pub completed: bool,
24    pub priority: Option<char>,
25    pub description: String,
26    pub projects: Vec<String>,
27    pub contexts: Vec<String>,
28    pub status: TaskStatus,
29    pub created_at: DateTime<Utc>,
30    pub updated_at: DateTime<Utc>,
31    pub completed_at: Option<DateTime<Utc>>,
32    pub due_date: Option<NaiveDate>,
33}
34
35#[derive(Debug, Serialize, Deserialize, Clone)]
36pub struct TaskHistory {
37    pub id: i32,
38    pub task_id: Uuid,
39    pub from_status: Option<TaskStatus>,
40    pub to_status: TaskStatus,
41    pub changed_at: DateTime<Utc>,
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    #[test]
49    fn task_creation_with_uuid_v7() {
50        let task = Task {
51            id: Uuid::now_v7(),
52            completed: false,
53            priority: None,
54            description: "Test task".into(),
55            projects: vec![],
56            contexts: vec![],
57            status: TaskStatus::Inbox,
58            created_at: Utc::now(),
59            updated_at: Utc::now(),
60            completed_at: None,
61            due_date: None,
62        };
63        assert_eq!(task.id.get_version(), Some(uuid::Version::SortRand));
64    }
65
66    #[test]
67    fn task_serialize_deserialize_roundtrip() {
68        let task = Task {
69            id: Uuid::now_v7(),
70            completed: false,
71            priority: Some('A'),
72            description: "Buy milk +proyecto @casa".into(),
73            projects: vec!["proyecto".into()],
74            contexts: vec!["casa".into()],
75            status: TaskStatus::Todo,
76            created_at: Utc::now(),
77            updated_at: Utc::now(),
78            completed_at: None,
79            due_date: Some(NaiveDate::from_ymd_opt(2026, 7, 10).unwrap()),
80        };
81        let json = serde_json::to_string(&task).unwrap();
82        let deserialized: Task = serde_json::from_str(&json).unwrap();
83        assert_eq!(task.id, deserialized.id);
84        assert_eq!(task.description, deserialized.description);
85        assert_eq!(task.status, deserialized.status);
86    }
87
88    #[test]
89    fn task_status_serialization() {
90        let statuses = vec![
91            (TaskStatus::Inbox, "\"inbox\""),
92            (TaskStatus::Todo, "\"todo\""),
93            (TaskStatus::Doing, "\"doing\""),
94            (TaskStatus::Done, "\"done\""),
95            (TaskStatus::Someday, "\"someday\""),
96        ];
97        for (status, expected) in statuses {
98            assert_eq!(serde_json::to_string(&status).unwrap(), expected);
99        }
100    }
101
102    #[test]
103    fn task_history_creation() {
104        let history = TaskHistory {
105            id: 1,
106            task_id: Uuid::now_v7(),
107            from_status: Some(TaskStatus::Inbox),
108            to_status: TaskStatus::Doing,
109            changed_at: Utc::now(),
110        };
111        assert_eq!(history.from_status, Some(TaskStatus::Inbox));
112        assert_eq!(history.to_status, TaskStatus::Doing);
113    }
114
115    #[test]
116    fn task_default_status_is_inbox() {
117        let task = Task {
118            id: Uuid::now_v7(),
119            completed: false,
120            priority: None,
121            description: "test".into(),
122            projects: vec![],
123            contexts: vec![],
124            status: TaskStatus::Inbox,
125            created_at: Utc::now(),
126            updated_at: Utc::now(),
127            completed_at: None,
128            due_date: None,
129        };
130        assert_eq!(task.status, TaskStatus::Inbox);
131        assert!(!task.completed);
132    }
133
134    #[test]
135    fn completed_task_has_completed_at() {
136        let task = Task {
137            id: Uuid::now_v7(),
138            completed: true,
139            priority: None,
140            description: "Done task".into(),
141            projects: vec![],
142            contexts: vec![],
143            status: TaskStatus::Done,
144            created_at: Utc::now(),
145            updated_at: Utc::now(),
146            completed_at: Some(Utc::now()),
147            due_date: None,
148        };
149        assert!(task.completed);
150        assert!(task.completed_at.is_some());
151    }
152}