1use chrono::{DateTime, NaiveDate, Utc};
4use serde::{Deserialize, Serialize};
5use uuid::Uuid;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
9pub enum TaskStatus {
10 #[serde(rename = "incomplete")]
11 Incomplete,
12 #[serde(rename = "completed")]
13 Completed,
14 #[serde(rename = "canceled")]
15 Canceled,
16 #[serde(rename = "trashed")]
17 Trashed,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
22pub enum TaskType {
23 #[serde(rename = "to-do")]
24 Todo,
25 #[serde(rename = "project")]
26 Project,
27 #[serde(rename = "heading")]
28 Heading,
29 #[serde(rename = "area")]
30 Area,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
35pub enum DeleteChildHandling {
36 #[serde(rename = "error")]
38 Error,
39 #[serde(rename = "cascade")]
41 Cascade,
42 #[serde(rename = "orphan")]
44 Orphan,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct Task {
50 pub uuid: Uuid,
52 pub title: String,
54 pub task_type: TaskType,
56 pub status: TaskStatus,
58 pub notes: Option<String>,
60 pub start_date: Option<NaiveDate>,
62 pub deadline: Option<NaiveDate>,
64 pub created: DateTime<Utc>,
66 pub modified: DateTime<Utc>,
68 pub stop_date: Option<DateTime<Utc>>,
70 pub project_uuid: Option<Uuid>,
72 pub area_uuid: Option<Uuid>,
74 pub parent_uuid: Option<Uuid>,
76 pub tags: Vec<String>,
78 pub children: Vec<Task>,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct Project {
85 pub uuid: Uuid,
87 pub title: String,
89 pub notes: Option<String>,
91 pub start_date: Option<NaiveDate>,
93 pub deadline: Option<NaiveDate>,
95 pub created: DateTime<Utc>,
97 pub modified: DateTime<Utc>,
99 pub area_uuid: Option<Uuid>,
101 pub tags: Vec<String>,
103 pub status: TaskStatus,
105 pub tasks: Vec<Task>,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct Area {
112 pub uuid: Uuid,
114 pub title: String,
116 pub notes: Option<String>,
118 pub created: DateTime<Utc>,
120 pub modified: DateTime<Utc>,
122 pub tags: Vec<String>,
124 pub projects: Vec<Project>,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct Tag {
131 pub uuid: Uuid,
133 pub title: String,
135 pub shortcut: Option<String>,
137 pub parent_uuid: Option<Uuid>,
139 pub created: DateTime<Utc>,
141 pub modified: DateTime<Utc>,
143 pub usage_count: u32,
145 pub last_used: Option<DateTime<Utc>>,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct CreateTagRequest {
152 pub title: String,
154 pub shortcut: Option<String>,
156 pub parent_uuid: Option<Uuid>,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct UpdateTagRequest {
163 pub uuid: Uuid,
165 pub title: Option<String>,
167 pub shortcut: Option<String>,
169 pub parent_uuid: Option<Uuid>,
171}
172
173#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
175pub enum TagMatchType {
176 #[serde(rename = "exact")]
178 Exact,
179 #[serde(rename = "case_mismatch")]
181 CaseMismatch,
182 #[serde(rename = "similar")]
184 Similar,
185 #[serde(rename = "partial")]
187 PartialMatch,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct TagMatch {
193 pub tag: Tag,
195 pub similarity_score: f32,
197 pub match_type: TagMatchType,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
203pub enum TagCreationResult {
204 #[serde(rename = "created")]
206 Created {
207 uuid: Uuid,
209 is_new: bool,
211 },
212 #[serde(rename = "existing")]
214 Existing {
215 tag: Tag,
217 is_new: bool,
219 },
220 #[serde(rename = "similar_found")]
222 SimilarFound {
223 similar_tags: Vec<TagMatch>,
225 requested_title: String,
227 },
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
232pub enum TagAssignmentResult {
233 #[serde(rename = "assigned")]
235 Assigned {
236 tag_uuid: Uuid,
238 },
239 #[serde(rename = "suggestions")]
241 Suggestions {
242 similar_tags: Vec<TagMatch>,
244 },
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct TagCompletion {
250 pub tag: Tag,
252 pub score: f32,
254}
255
256#[derive(Debug, Clone, Serialize, Deserialize)]
258pub struct TagStatistics {
259 pub uuid: Uuid,
261 pub title: String,
263 pub usage_count: u32,
265 pub task_uuids: Vec<Uuid>,
267 pub related_tags: Vec<(String, u32)>, }
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
273pub struct TagPair {
274 pub tag1: Tag,
276 pub tag2: Tag,
278 pub similarity: f32,
280}
281
282#[derive(Debug, Clone, Serialize, Deserialize)]
284pub struct CreateTaskRequest {
285 pub title: String,
287 pub task_type: Option<TaskType>,
289 pub notes: Option<String>,
291 pub start_date: Option<NaiveDate>,
293 pub deadline: Option<NaiveDate>,
295 pub project_uuid: Option<Uuid>,
297 pub area_uuid: Option<Uuid>,
299 pub parent_uuid: Option<Uuid>,
301 pub tags: Option<Vec<String>>,
303 pub status: Option<TaskStatus>,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize)]
309pub struct UpdateTaskRequest {
310 pub uuid: Uuid,
312 pub title: Option<String>,
314 pub notes: Option<String>,
316 pub start_date: Option<NaiveDate>,
318 pub deadline: Option<NaiveDate>,
320 pub status: Option<TaskStatus>,
322 pub project_uuid: Option<Uuid>,
324 pub area_uuid: Option<Uuid>,
326 pub tags: Option<Vec<String>>,
328}
329
330#[derive(Debug, Clone, Serialize, Deserialize, Default)]
332pub struct TaskFilters {
333 pub status: Option<TaskStatus>,
335 pub task_type: Option<TaskType>,
337 pub project_uuid: Option<Uuid>,
339 pub area_uuid: Option<Uuid>,
341 pub tags: Option<Vec<String>>,
343 pub start_date_from: Option<NaiveDate>,
345 pub start_date_to: Option<NaiveDate>,
346 pub deadline_from: Option<NaiveDate>,
348 pub deadline_to: Option<NaiveDate>,
349 pub search_query: Option<String>,
351 pub limit: Option<usize>,
353 pub offset: Option<usize>,
355}
356
357#[cfg(feature = "advanced-queries")]
363#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct RankedTask {
365 pub task: Task,
367 pub score: f32,
369}
370
371#[derive(Debug, Clone, Serialize, Deserialize)]
373pub struct CreateProjectRequest {
374 pub title: String,
376 pub notes: Option<String>,
378 pub area_uuid: Option<Uuid>,
380 pub start_date: Option<NaiveDate>,
382 pub deadline: Option<NaiveDate>,
384 pub tags: Option<Vec<String>>,
386}
387
388#[derive(Debug, Clone, Serialize, Deserialize)]
390pub struct UpdateProjectRequest {
391 pub uuid: Uuid,
393 pub title: Option<String>,
395 pub notes: Option<String>,
397 pub area_uuid: Option<Uuid>,
399 pub start_date: Option<NaiveDate>,
401 pub deadline: Option<NaiveDate>,
403 pub tags: Option<Vec<String>>,
405}
406
407#[derive(Debug, Clone, Serialize, Deserialize)]
409pub struct CreateAreaRequest {
410 pub title: String,
412}
413
414#[derive(Debug, Clone, Serialize, Deserialize)]
416pub struct UpdateAreaRequest {
417 pub uuid: Uuid,
419 pub title: String,
421}
422
423#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
425pub enum ProjectChildHandling {
426 #[serde(rename = "error")]
428 #[default]
429 Error,
430 #[serde(rename = "cascade")]
432 Cascade,
433 #[serde(rename = "orphan")]
435 Orphan,
436}
437
438#[derive(Debug, Clone, Serialize, Deserialize)]
444pub struct BulkMoveRequest {
445 pub task_uuids: Vec<Uuid>,
447 pub project_uuid: Option<Uuid>,
449 pub area_uuid: Option<Uuid>,
451}
452
453#[derive(Debug, Clone, Serialize, Deserialize)]
455pub struct BulkUpdateDatesRequest {
456 pub task_uuids: Vec<Uuid>,
458 pub start_date: Option<NaiveDate>,
460 pub deadline: Option<NaiveDate>,
462 pub clear_start_date: bool,
464 pub clear_deadline: bool,
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize)]
470pub struct BulkCompleteRequest {
471 pub task_uuids: Vec<Uuid>,
473}
474
475#[derive(Debug, Clone, Serialize, Deserialize)]
477pub struct BulkDeleteRequest {
478 pub task_uuids: Vec<Uuid>,
480}
481
482#[derive(Debug, Clone, Serialize, Deserialize)]
484pub struct BulkOperationResult {
485 pub success: bool,
487 pub processed_count: usize,
489 pub message: String,
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496 use chrono::NaiveDate;
497
498 #[test]
499 fn test_task_status_serialization() {
500 let status = TaskStatus::Incomplete;
501 let serialized = serde_json::to_string(&status).unwrap();
502 assert_eq!(serialized, "\"incomplete\"");
503
504 let status = TaskStatus::Completed;
505 let serialized = serde_json::to_string(&status).unwrap();
506 assert_eq!(serialized, "\"completed\"");
507
508 let status = TaskStatus::Canceled;
509 let serialized = serde_json::to_string(&status).unwrap();
510 assert_eq!(serialized, "\"canceled\"");
511
512 let status = TaskStatus::Trashed;
513 let serialized = serde_json::to_string(&status).unwrap();
514 assert_eq!(serialized, "\"trashed\"");
515 }
516
517 #[test]
518 fn test_task_status_deserialization() {
519 let deserialized: TaskStatus = serde_json::from_str("\"incomplete\"").unwrap();
520 assert_eq!(deserialized, TaskStatus::Incomplete);
521
522 let deserialized: TaskStatus = serde_json::from_str("\"completed\"").unwrap();
523 assert_eq!(deserialized, TaskStatus::Completed);
524
525 let deserialized: TaskStatus = serde_json::from_str("\"canceled\"").unwrap();
526 assert_eq!(deserialized, TaskStatus::Canceled);
527
528 let deserialized: TaskStatus = serde_json::from_str("\"trashed\"").unwrap();
529 assert_eq!(deserialized, TaskStatus::Trashed);
530 }
531
532 #[test]
533 fn test_task_type_serialization() {
534 let task_type = TaskType::Todo;
535 let serialized = serde_json::to_string(&task_type).unwrap();
536 assert_eq!(serialized, "\"to-do\"");
537
538 let task_type = TaskType::Project;
539 let serialized = serde_json::to_string(&task_type).unwrap();
540 assert_eq!(serialized, "\"project\"");
541
542 let task_type = TaskType::Heading;
543 let serialized = serde_json::to_string(&task_type).unwrap();
544 assert_eq!(serialized, "\"heading\"");
545
546 let task_type = TaskType::Area;
547 let serialized = serde_json::to_string(&task_type).unwrap();
548 assert_eq!(serialized, "\"area\"");
549 }
550
551 #[test]
552 fn test_task_type_deserialization() {
553 let deserialized: TaskType = serde_json::from_str("\"to-do\"").unwrap();
554 assert_eq!(deserialized, TaskType::Todo);
555
556 let deserialized: TaskType = serde_json::from_str("\"project\"").unwrap();
557 assert_eq!(deserialized, TaskType::Project);
558
559 let deserialized: TaskType = serde_json::from_str("\"heading\"").unwrap();
560 assert_eq!(deserialized, TaskType::Heading);
561
562 let deserialized: TaskType = serde_json::from_str("\"area\"").unwrap();
563 assert_eq!(deserialized, TaskType::Area);
564 }
565
566 #[test]
567 fn test_task_creation() {
568 let uuid = Uuid::new_v4();
569 let now = Utc::now();
570 let start_date = NaiveDate::from_ymd_opt(2024, 1, 1).unwrap();
571 let deadline = NaiveDate::from_ymd_opt(2024, 12, 31).unwrap();
572
573 let task = Task {
574 uuid,
575 title: "Test Task".to_string(),
576 task_type: TaskType::Todo,
577 status: TaskStatus::Incomplete,
578 notes: Some("Test notes".to_string()),
579 start_date: Some(start_date),
580 deadline: Some(deadline),
581 created: now,
582 modified: now,
583 stop_date: None,
584 project_uuid: None,
585 area_uuid: None,
586 parent_uuid: None,
587 tags: vec!["work".to_string(), "urgent".to_string()],
588 children: vec![],
589 };
590
591 assert_eq!(task.uuid, uuid);
592 assert_eq!(task.title, "Test Task");
593 assert_eq!(task.task_type, TaskType::Todo);
594 assert_eq!(task.status, TaskStatus::Incomplete);
595 assert_eq!(task.notes, Some("Test notes".to_string()));
596 assert_eq!(task.start_date, Some(start_date));
597 assert_eq!(task.deadline, Some(deadline));
598 assert_eq!(task.tags.len(), 2);
599 assert!(task.tags.contains(&"work".to_string()));
600 assert!(task.tags.contains(&"urgent".to_string()));
601 }
602
603 #[test]
604 fn test_task_serialization() {
605 let uuid = Uuid::new_v4();
606 let now = Utc::now();
607
608 let task = Task {
609 uuid,
610 title: "Test Task".to_string(),
611 task_type: TaskType::Todo,
612 status: TaskStatus::Incomplete,
613 notes: None,
614 start_date: None,
615 deadline: None,
616 created: now,
617 modified: now,
618 stop_date: None,
619 project_uuid: None,
620 area_uuid: None,
621 parent_uuid: None,
622 tags: vec![],
623 children: vec![],
624 };
625
626 let serialized = serde_json::to_string(&task).unwrap();
627 let deserialized: Task = serde_json::from_str(&serialized).unwrap();
628
629 assert_eq!(deserialized.uuid, task.uuid);
630 assert_eq!(deserialized.title, task.title);
631 assert_eq!(deserialized.task_type, task.task_type);
632 assert_eq!(deserialized.status, task.status);
633 }
634
635 #[test]
636 fn test_project_creation() {
637 let uuid = Uuid::new_v4();
638 let area_uuid = Uuid::new_v4();
639 let now = Utc::now();
640
641 let project = Project {
642 uuid,
643 title: "Test Project".to_string(),
644 notes: Some("Project notes".to_string()),
645 start_date: None,
646 deadline: None,
647 created: now,
648 modified: now,
649 area_uuid: Some(area_uuid),
650 tags: vec!["project".to_string()],
651 status: TaskStatus::Incomplete,
652 tasks: vec![],
653 };
654
655 assert_eq!(project.uuid, uuid);
656 assert_eq!(project.title, "Test Project");
657 assert_eq!(project.area_uuid, Some(area_uuid));
658 assert_eq!(project.status, TaskStatus::Incomplete);
659 assert_eq!(project.tags.len(), 1);
660 }
661
662 #[test]
663 fn test_project_serialization() {
664 let uuid = Uuid::new_v4();
665 let now = Utc::now();
666
667 let project = Project {
668 uuid,
669 title: "Test Project".to_string(),
670 notes: None,
671 start_date: None,
672 deadline: None,
673 created: now,
674 modified: now,
675 area_uuid: None,
676 tags: vec![],
677 status: TaskStatus::Incomplete,
678 tasks: vec![],
679 };
680
681 let serialized = serde_json::to_string(&project).unwrap();
682 let deserialized: Project = serde_json::from_str(&serialized).unwrap();
683
684 assert_eq!(deserialized.uuid, project.uuid);
685 assert_eq!(deserialized.title, project.title);
686 assert_eq!(deserialized.status, project.status);
687 }
688
689 #[test]
690 fn test_area_creation() {
691 let uuid = Uuid::new_v4();
692 let now = Utc::now();
693
694 let area = Area {
695 uuid,
696 title: "Test Area".to_string(),
697 notes: Some("Area notes".to_string()),
698 created: now,
699 modified: now,
700 tags: vec!["area".to_string()],
701 projects: vec![],
702 };
703
704 assert_eq!(area.uuid, uuid);
705 assert_eq!(area.title, "Test Area");
706 assert_eq!(area.notes, Some("Area notes".to_string()));
707 assert_eq!(area.tags.len(), 1);
708 }
709
710 #[test]
711 fn test_area_serialization() {
712 let uuid = Uuid::new_v4();
713 let now = Utc::now();
714
715 let area = Area {
716 uuid,
717 title: "Test Area".to_string(),
718 notes: None,
719 created: now,
720 modified: now,
721 tags: vec![],
722 projects: vec![],
723 };
724
725 let serialized = serde_json::to_string(&area).unwrap();
726 let deserialized: Area = serde_json::from_str(&serialized).unwrap();
727
728 assert_eq!(deserialized.uuid, area.uuid);
729 assert_eq!(deserialized.title, area.title);
730 }
731
732 #[test]
733 fn test_tag_creation() {
734 let uuid = Uuid::new_v4();
735 let parent_uuid = Uuid::new_v4();
736 let now = Utc::now();
737
738 let tag = Tag {
739 uuid,
740 title: "work".to_string(),
741 shortcut: Some("w".to_string()),
742 parent_uuid: Some(parent_uuid),
743 created: now,
744 modified: now,
745 usage_count: 5,
746 last_used: Some(now),
747 };
748
749 assert_eq!(tag.uuid, uuid);
750 assert_eq!(tag.title, "work");
751 assert_eq!(tag.shortcut, Some("w".to_string()));
752 assert_eq!(tag.parent_uuid, Some(parent_uuid));
753 assert_eq!(tag.usage_count, 5);
754 assert_eq!(tag.last_used, Some(now));
755 }
756
757 #[test]
758 fn test_tag_serialization() {
759 let uuid = Uuid::new_v4();
760 let now = Utc::now();
761
762 let tag = Tag {
763 uuid,
764 title: "test".to_string(),
765 shortcut: None,
766 parent_uuid: None,
767 created: now,
768 modified: now,
769 usage_count: 0,
770 last_used: None,
771 };
772
773 let serialized = serde_json::to_string(&tag).unwrap();
774 let deserialized: Tag = serde_json::from_str(&serialized).unwrap();
775
776 assert_eq!(deserialized.uuid, tag.uuid);
777 assert_eq!(deserialized.title, tag.title);
778 assert_eq!(deserialized.usage_count, tag.usage_count);
779 }
780
781 #[test]
782 fn test_create_task_request() {
783 let project_uuid = Uuid::new_v4();
784 let area_uuid = Uuid::new_v4();
785 let start_date = NaiveDate::from_ymd_opt(2024, 1, 1).unwrap();
786
787 let request = CreateTaskRequest {
788 title: "New Task".to_string(),
789 task_type: None,
790 notes: Some("Task notes".to_string()),
791 start_date: Some(start_date),
792 deadline: None,
793 project_uuid: Some(project_uuid),
794 area_uuid: Some(area_uuid),
795 parent_uuid: None,
796 tags: Some(vec!["new".to_string()]),
797 status: None,
798 };
799
800 assert_eq!(request.title, "New Task");
801 assert_eq!(request.project_uuid, Some(project_uuid));
802 assert_eq!(request.area_uuid, Some(area_uuid));
803 assert_eq!(request.start_date, Some(start_date));
804 assert_eq!(request.tags.as_ref().unwrap().len(), 1);
805 }
806
807 #[test]
808 fn test_create_task_request_serialization() {
809 let request = CreateTaskRequest {
810 title: "Test".to_string(),
811 task_type: None,
812 notes: None,
813 start_date: None,
814 deadline: None,
815 project_uuid: None,
816 area_uuid: None,
817 parent_uuid: None,
818 tags: None,
819 status: None,
820 };
821
822 let serialized = serde_json::to_string(&request).unwrap();
823 let deserialized: CreateTaskRequest = serde_json::from_str(&serialized).unwrap();
824
825 assert_eq!(deserialized.title, request.title);
826 }
827
828 #[test]
829 fn test_update_task_request() {
830 let uuid = Uuid::new_v4();
831
832 let request = UpdateTaskRequest {
833 uuid,
834 title: Some("Updated Title".to_string()),
835 notes: Some("Updated notes".to_string()),
836 start_date: None,
837 deadline: None,
838 status: Some(TaskStatus::Completed),
839 project_uuid: None,
840 area_uuid: None,
841 tags: Some(vec!["updated".to_string()]),
842 };
843
844 assert_eq!(request.uuid, uuid);
845 assert_eq!(request.title, Some("Updated Title".to_string()));
846 assert_eq!(request.status, Some(TaskStatus::Completed));
847 assert_eq!(request.tags, Some(vec!["updated".to_string()]));
848 }
849
850 #[test]
851 fn test_update_task_request_serialization() {
852 let uuid = Uuid::new_v4();
853
854 let request = UpdateTaskRequest {
855 uuid,
856 title: None,
857 notes: None,
858 start_date: None,
859 deadline: None,
860 status: None,
861 project_uuid: None,
862 area_uuid: None,
863 tags: None,
864 };
865
866 let serialized = serde_json::to_string(&request).unwrap();
867 let deserialized: UpdateTaskRequest = serde_json::from_str(&serialized).unwrap();
868
869 assert_eq!(deserialized.uuid, request.uuid);
870 }
871
872 #[test]
873 fn test_task_filters_default() {
874 let filters = TaskFilters::default();
875
876 assert!(filters.status.is_none());
877 assert!(filters.task_type.is_none());
878 assert!(filters.project_uuid.is_none());
879 assert!(filters.area_uuid.is_none());
880 assert!(filters.tags.is_none());
881 assert!(filters.start_date_from.is_none());
882 assert!(filters.start_date_to.is_none());
883 assert!(filters.deadline_from.is_none());
884 assert!(filters.deadline_to.is_none());
885 assert!(filters.search_query.is_none());
886 assert!(filters.limit.is_none());
887 assert!(filters.offset.is_none());
888 }
889
890 #[test]
891 fn test_task_filters_creation() {
892 let project_uuid = Uuid::new_v4();
893 let start_date = NaiveDate::from_ymd_opt(2024, 1, 1).unwrap();
894
895 let filters = TaskFilters {
896 status: Some(TaskStatus::Incomplete),
897 task_type: Some(TaskType::Todo),
898 project_uuid: Some(project_uuid),
899 area_uuid: None,
900 tags: Some(vec!["work".to_string()]),
901 start_date_from: Some(start_date),
902 start_date_to: None,
903 deadline_from: None,
904 deadline_to: None,
905 search_query: Some("test".to_string()),
906 limit: Some(10),
907 offset: Some(0),
908 };
909
910 assert_eq!(filters.status, Some(TaskStatus::Incomplete));
911 assert_eq!(filters.task_type, Some(TaskType::Todo));
912 assert_eq!(filters.project_uuid, Some(project_uuid));
913 assert_eq!(filters.search_query, Some("test".to_string()));
914 assert_eq!(filters.limit, Some(10));
915 assert_eq!(filters.offset, Some(0));
916 }
917
918 #[test]
919 fn test_task_filters_serialization() {
920 let filters = TaskFilters {
921 status: Some(TaskStatus::Completed),
922 task_type: Some(TaskType::Project),
923 project_uuid: None,
924 area_uuid: None,
925 tags: None,
926 start_date_from: None,
927 start_date_to: None,
928 deadline_from: None,
929 deadline_to: None,
930 search_query: None,
931 limit: None,
932 offset: None,
933 };
934
935 let serialized = serde_json::to_string(&filters).unwrap();
936 let deserialized: TaskFilters = serde_json::from_str(&serialized).unwrap();
937
938 assert_eq!(deserialized.status, filters.status);
939 assert_eq!(deserialized.task_type, filters.task_type);
940 }
941
942 #[test]
943 fn test_task_status_equality() {
944 assert_eq!(TaskStatus::Incomplete, TaskStatus::Incomplete);
945 assert_ne!(TaskStatus::Incomplete, TaskStatus::Completed);
946 assert_ne!(TaskStatus::Completed, TaskStatus::Canceled);
947 assert_ne!(TaskStatus::Canceled, TaskStatus::Trashed);
948 }
949
950 #[test]
951 fn test_task_type_equality() {
952 assert_eq!(TaskType::Todo, TaskType::Todo);
953 assert_ne!(TaskType::Todo, TaskType::Project);
954 assert_ne!(TaskType::Project, TaskType::Heading);
955 assert_ne!(TaskType::Heading, TaskType::Area);
956 }
957
958 #[test]
959 fn test_task_with_children() {
960 let parent_uuid = Uuid::new_v4();
961 let child_uuid = Uuid::new_v4();
962 let now = Utc::now();
963
964 let child_task = Task {
965 uuid: child_uuid,
966 title: "Child Task".to_string(),
967 task_type: TaskType::Todo,
968 status: TaskStatus::Incomplete,
969 notes: None,
970 start_date: None,
971 deadline: None,
972 created: now,
973 modified: now,
974 stop_date: None,
975 project_uuid: None,
976 area_uuid: None,
977 parent_uuid: Some(parent_uuid),
978 tags: vec![],
979 children: vec![],
980 };
981
982 let parent_task = Task {
983 uuid: parent_uuid,
984 title: "Parent Task".to_string(),
985 task_type: TaskType::Heading,
986 status: TaskStatus::Incomplete,
987 notes: None,
988 start_date: None,
989 deadline: None,
990 created: now,
991 modified: now,
992 stop_date: None,
993 project_uuid: None,
994 area_uuid: None,
995 parent_uuid: None,
996 tags: vec![],
997 children: vec![child_task],
998 };
999
1000 assert_eq!(parent_task.children.len(), 1);
1001 assert_eq!(parent_task.children[0].parent_uuid, Some(parent_uuid));
1002 assert_eq!(parent_task.children[0].title, "Child Task");
1003 }
1004
1005 #[test]
1006 fn test_project_with_tasks() {
1007 let project_uuid = Uuid::new_v4();
1008 let task_uuid = Uuid::new_v4();
1009 let now = Utc::now();
1010
1011 let task = Task {
1012 uuid: task_uuid,
1013 title: "Project Task".to_string(),
1014 task_type: TaskType::Todo,
1015 status: TaskStatus::Incomplete,
1016 notes: None,
1017 start_date: None,
1018 deadline: None,
1019 created: now,
1020 modified: now,
1021 stop_date: None,
1022 project_uuid: Some(project_uuid),
1023 area_uuid: None,
1024 parent_uuid: None,
1025 tags: vec![],
1026 children: vec![],
1027 };
1028
1029 let project = Project {
1030 uuid: project_uuid,
1031 title: "Test Project".to_string(),
1032 notes: None,
1033 start_date: None,
1034 deadline: None,
1035 created: now,
1036 modified: now,
1037 area_uuid: None,
1038 tags: vec![],
1039 status: TaskStatus::Incomplete,
1040 tasks: vec![task],
1041 };
1042
1043 assert_eq!(project.tasks.len(), 1);
1044 assert_eq!(project.tasks[0].project_uuid, Some(project_uuid));
1045 assert_eq!(project.tasks[0].title, "Project Task");
1046 }
1047
1048 #[test]
1049 fn test_area_with_projects() {
1050 let area_uuid = Uuid::new_v4();
1051 let project_uuid = Uuid::new_v4();
1052 let now = Utc::now();
1053
1054 let project = Project {
1055 uuid: project_uuid,
1056 title: "Area Project".to_string(),
1057 notes: None,
1058 start_date: None,
1059 deadline: None,
1060 created: now,
1061 modified: now,
1062 area_uuid: Some(area_uuid),
1063 tags: vec![],
1064 status: TaskStatus::Incomplete,
1065 tasks: vec![],
1066 };
1067
1068 let area = Area {
1069 uuid: area_uuid,
1070 title: "Test Area".to_string(),
1071 notes: None,
1072 created: now,
1073 modified: now,
1074 tags: vec![],
1075 projects: vec![project],
1076 };
1077
1078 assert_eq!(area.projects.len(), 1);
1079 assert_eq!(area.projects[0].area_uuid, Some(area_uuid));
1080 assert_eq!(area.projects[0].title, "Area Project");
1081 }
1082}