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#[derive(Debug, Clone, Serialize, Deserialize)]
359pub struct CreateProjectRequest {
360 pub title: String,
362 pub notes: Option<String>,
364 pub area_uuid: Option<Uuid>,
366 pub start_date: Option<NaiveDate>,
368 pub deadline: Option<NaiveDate>,
370 pub tags: Option<Vec<String>>,
372}
373
374#[derive(Debug, Clone, Serialize, Deserialize)]
376pub struct UpdateProjectRequest {
377 pub uuid: Uuid,
379 pub title: Option<String>,
381 pub notes: Option<String>,
383 pub area_uuid: Option<Uuid>,
385 pub start_date: Option<NaiveDate>,
387 pub deadline: Option<NaiveDate>,
389 pub tags: Option<Vec<String>>,
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize)]
395pub struct CreateAreaRequest {
396 pub title: String,
398}
399
400#[derive(Debug, Clone, Serialize, Deserialize)]
402pub struct UpdateAreaRequest {
403 pub uuid: Uuid,
405 pub title: String,
407}
408
409#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
411pub enum ProjectChildHandling {
412 #[serde(rename = "error")]
414 #[default]
415 Error,
416 #[serde(rename = "cascade")]
418 Cascade,
419 #[serde(rename = "orphan")]
421 Orphan,
422}
423
424#[derive(Debug, Clone, Serialize, Deserialize)]
430pub struct BulkMoveRequest {
431 pub task_uuids: Vec<Uuid>,
433 pub project_uuid: Option<Uuid>,
435 pub area_uuid: Option<Uuid>,
437}
438
439#[derive(Debug, Clone, Serialize, Deserialize)]
441pub struct BulkUpdateDatesRequest {
442 pub task_uuids: Vec<Uuid>,
444 pub start_date: Option<NaiveDate>,
446 pub deadline: Option<NaiveDate>,
448 pub clear_start_date: bool,
450 pub clear_deadline: bool,
452}
453
454#[derive(Debug, Clone, Serialize, Deserialize)]
456pub struct BulkCompleteRequest {
457 pub task_uuids: Vec<Uuid>,
459}
460
461#[derive(Debug, Clone, Serialize, Deserialize)]
463pub struct BulkDeleteRequest {
464 pub task_uuids: Vec<Uuid>,
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize)]
470pub struct BulkOperationResult {
471 pub success: bool,
473 pub processed_count: usize,
475 pub message: String,
477}
478
479#[cfg(test)]
480mod tests {
481 use super::*;
482 use chrono::NaiveDate;
483
484 #[test]
485 fn test_task_status_serialization() {
486 let status = TaskStatus::Incomplete;
487 let serialized = serde_json::to_string(&status).unwrap();
488 assert_eq!(serialized, "\"incomplete\"");
489
490 let status = TaskStatus::Completed;
491 let serialized = serde_json::to_string(&status).unwrap();
492 assert_eq!(serialized, "\"completed\"");
493
494 let status = TaskStatus::Canceled;
495 let serialized = serde_json::to_string(&status).unwrap();
496 assert_eq!(serialized, "\"canceled\"");
497
498 let status = TaskStatus::Trashed;
499 let serialized = serde_json::to_string(&status).unwrap();
500 assert_eq!(serialized, "\"trashed\"");
501 }
502
503 #[test]
504 fn test_task_status_deserialization() {
505 let deserialized: TaskStatus = serde_json::from_str("\"incomplete\"").unwrap();
506 assert_eq!(deserialized, TaskStatus::Incomplete);
507
508 let deserialized: TaskStatus = serde_json::from_str("\"completed\"").unwrap();
509 assert_eq!(deserialized, TaskStatus::Completed);
510
511 let deserialized: TaskStatus = serde_json::from_str("\"canceled\"").unwrap();
512 assert_eq!(deserialized, TaskStatus::Canceled);
513
514 let deserialized: TaskStatus = serde_json::from_str("\"trashed\"").unwrap();
515 assert_eq!(deserialized, TaskStatus::Trashed);
516 }
517
518 #[test]
519 fn test_task_type_serialization() {
520 let task_type = TaskType::Todo;
521 let serialized = serde_json::to_string(&task_type).unwrap();
522 assert_eq!(serialized, "\"to-do\"");
523
524 let task_type = TaskType::Project;
525 let serialized = serde_json::to_string(&task_type).unwrap();
526 assert_eq!(serialized, "\"project\"");
527
528 let task_type = TaskType::Heading;
529 let serialized = serde_json::to_string(&task_type).unwrap();
530 assert_eq!(serialized, "\"heading\"");
531
532 let task_type = TaskType::Area;
533 let serialized = serde_json::to_string(&task_type).unwrap();
534 assert_eq!(serialized, "\"area\"");
535 }
536
537 #[test]
538 fn test_task_type_deserialization() {
539 let deserialized: TaskType = serde_json::from_str("\"to-do\"").unwrap();
540 assert_eq!(deserialized, TaskType::Todo);
541
542 let deserialized: TaskType = serde_json::from_str("\"project\"").unwrap();
543 assert_eq!(deserialized, TaskType::Project);
544
545 let deserialized: TaskType = serde_json::from_str("\"heading\"").unwrap();
546 assert_eq!(deserialized, TaskType::Heading);
547
548 let deserialized: TaskType = serde_json::from_str("\"area\"").unwrap();
549 assert_eq!(deserialized, TaskType::Area);
550 }
551
552 #[test]
553 fn test_task_creation() {
554 let uuid = Uuid::new_v4();
555 let now = Utc::now();
556 let start_date = NaiveDate::from_ymd_opt(2024, 1, 1).unwrap();
557 let deadline = NaiveDate::from_ymd_opt(2024, 12, 31).unwrap();
558
559 let task = Task {
560 uuid,
561 title: "Test Task".to_string(),
562 task_type: TaskType::Todo,
563 status: TaskStatus::Incomplete,
564 notes: Some("Test notes".to_string()),
565 start_date: Some(start_date),
566 deadline: Some(deadline),
567 created: now,
568 modified: now,
569 stop_date: None,
570 project_uuid: None,
571 area_uuid: None,
572 parent_uuid: None,
573 tags: vec!["work".to_string(), "urgent".to_string()],
574 children: vec![],
575 };
576
577 assert_eq!(task.uuid, uuid);
578 assert_eq!(task.title, "Test Task");
579 assert_eq!(task.task_type, TaskType::Todo);
580 assert_eq!(task.status, TaskStatus::Incomplete);
581 assert_eq!(task.notes, Some("Test notes".to_string()));
582 assert_eq!(task.start_date, Some(start_date));
583 assert_eq!(task.deadline, Some(deadline));
584 assert_eq!(task.tags.len(), 2);
585 assert!(task.tags.contains(&"work".to_string()));
586 assert!(task.tags.contains(&"urgent".to_string()));
587 }
588
589 #[test]
590 fn test_task_serialization() {
591 let uuid = Uuid::new_v4();
592 let now = Utc::now();
593
594 let task = Task {
595 uuid,
596 title: "Test Task".to_string(),
597 task_type: TaskType::Todo,
598 status: TaskStatus::Incomplete,
599 notes: None,
600 start_date: None,
601 deadline: None,
602 created: now,
603 modified: now,
604 stop_date: None,
605 project_uuid: None,
606 area_uuid: None,
607 parent_uuid: None,
608 tags: vec![],
609 children: vec![],
610 };
611
612 let serialized = serde_json::to_string(&task).unwrap();
613 let deserialized: Task = serde_json::from_str(&serialized).unwrap();
614
615 assert_eq!(deserialized.uuid, task.uuid);
616 assert_eq!(deserialized.title, task.title);
617 assert_eq!(deserialized.task_type, task.task_type);
618 assert_eq!(deserialized.status, task.status);
619 }
620
621 #[test]
622 fn test_project_creation() {
623 let uuid = Uuid::new_v4();
624 let area_uuid = Uuid::new_v4();
625 let now = Utc::now();
626
627 let project = Project {
628 uuid,
629 title: "Test Project".to_string(),
630 notes: Some("Project notes".to_string()),
631 start_date: None,
632 deadline: None,
633 created: now,
634 modified: now,
635 area_uuid: Some(area_uuid),
636 tags: vec!["project".to_string()],
637 status: TaskStatus::Incomplete,
638 tasks: vec![],
639 };
640
641 assert_eq!(project.uuid, uuid);
642 assert_eq!(project.title, "Test Project");
643 assert_eq!(project.area_uuid, Some(area_uuid));
644 assert_eq!(project.status, TaskStatus::Incomplete);
645 assert_eq!(project.tags.len(), 1);
646 }
647
648 #[test]
649 fn test_project_serialization() {
650 let uuid = Uuid::new_v4();
651 let now = Utc::now();
652
653 let project = Project {
654 uuid,
655 title: "Test Project".to_string(),
656 notes: None,
657 start_date: None,
658 deadline: None,
659 created: now,
660 modified: now,
661 area_uuid: None,
662 tags: vec![],
663 status: TaskStatus::Incomplete,
664 tasks: vec![],
665 };
666
667 let serialized = serde_json::to_string(&project).unwrap();
668 let deserialized: Project = serde_json::from_str(&serialized).unwrap();
669
670 assert_eq!(deserialized.uuid, project.uuid);
671 assert_eq!(deserialized.title, project.title);
672 assert_eq!(deserialized.status, project.status);
673 }
674
675 #[test]
676 fn test_area_creation() {
677 let uuid = Uuid::new_v4();
678 let now = Utc::now();
679
680 let area = Area {
681 uuid,
682 title: "Test Area".to_string(),
683 notes: Some("Area notes".to_string()),
684 created: now,
685 modified: now,
686 tags: vec!["area".to_string()],
687 projects: vec![],
688 };
689
690 assert_eq!(area.uuid, uuid);
691 assert_eq!(area.title, "Test Area");
692 assert_eq!(area.notes, Some("Area notes".to_string()));
693 assert_eq!(area.tags.len(), 1);
694 }
695
696 #[test]
697 fn test_area_serialization() {
698 let uuid = Uuid::new_v4();
699 let now = Utc::now();
700
701 let area = Area {
702 uuid,
703 title: "Test Area".to_string(),
704 notes: None,
705 created: now,
706 modified: now,
707 tags: vec![],
708 projects: vec![],
709 };
710
711 let serialized = serde_json::to_string(&area).unwrap();
712 let deserialized: Area = serde_json::from_str(&serialized).unwrap();
713
714 assert_eq!(deserialized.uuid, area.uuid);
715 assert_eq!(deserialized.title, area.title);
716 }
717
718 #[test]
719 fn test_tag_creation() {
720 let uuid = Uuid::new_v4();
721 let parent_uuid = Uuid::new_v4();
722 let now = Utc::now();
723
724 let tag = Tag {
725 uuid,
726 title: "work".to_string(),
727 shortcut: Some("w".to_string()),
728 parent_uuid: Some(parent_uuid),
729 created: now,
730 modified: now,
731 usage_count: 5,
732 last_used: Some(now),
733 };
734
735 assert_eq!(tag.uuid, uuid);
736 assert_eq!(tag.title, "work");
737 assert_eq!(tag.shortcut, Some("w".to_string()));
738 assert_eq!(tag.parent_uuid, Some(parent_uuid));
739 assert_eq!(tag.usage_count, 5);
740 assert_eq!(tag.last_used, Some(now));
741 }
742
743 #[test]
744 fn test_tag_serialization() {
745 let uuid = Uuid::new_v4();
746 let now = Utc::now();
747
748 let tag = Tag {
749 uuid,
750 title: "test".to_string(),
751 shortcut: None,
752 parent_uuid: None,
753 created: now,
754 modified: now,
755 usage_count: 0,
756 last_used: None,
757 };
758
759 let serialized = serde_json::to_string(&tag).unwrap();
760 let deserialized: Tag = serde_json::from_str(&serialized).unwrap();
761
762 assert_eq!(deserialized.uuid, tag.uuid);
763 assert_eq!(deserialized.title, tag.title);
764 assert_eq!(deserialized.usage_count, tag.usage_count);
765 }
766
767 #[test]
768 fn test_create_task_request() {
769 let project_uuid = Uuid::new_v4();
770 let area_uuid = Uuid::new_v4();
771 let start_date = NaiveDate::from_ymd_opt(2024, 1, 1).unwrap();
772
773 let request = CreateTaskRequest {
774 title: "New Task".to_string(),
775 task_type: None,
776 notes: Some("Task notes".to_string()),
777 start_date: Some(start_date),
778 deadline: None,
779 project_uuid: Some(project_uuid),
780 area_uuid: Some(area_uuid),
781 parent_uuid: None,
782 tags: Some(vec!["new".to_string()]),
783 status: None,
784 };
785
786 assert_eq!(request.title, "New Task");
787 assert_eq!(request.project_uuid, Some(project_uuid));
788 assert_eq!(request.area_uuid, Some(area_uuid));
789 assert_eq!(request.start_date, Some(start_date));
790 assert_eq!(request.tags.as_ref().unwrap().len(), 1);
791 }
792
793 #[test]
794 fn test_create_task_request_serialization() {
795 let request = CreateTaskRequest {
796 title: "Test".to_string(),
797 task_type: None,
798 notes: None,
799 start_date: None,
800 deadline: None,
801 project_uuid: None,
802 area_uuid: None,
803 parent_uuid: None,
804 tags: None,
805 status: None,
806 };
807
808 let serialized = serde_json::to_string(&request).unwrap();
809 let deserialized: CreateTaskRequest = serde_json::from_str(&serialized).unwrap();
810
811 assert_eq!(deserialized.title, request.title);
812 }
813
814 #[test]
815 fn test_update_task_request() {
816 let uuid = Uuid::new_v4();
817
818 let request = UpdateTaskRequest {
819 uuid,
820 title: Some("Updated Title".to_string()),
821 notes: Some("Updated notes".to_string()),
822 start_date: None,
823 deadline: None,
824 status: Some(TaskStatus::Completed),
825 project_uuid: None,
826 area_uuid: None,
827 tags: Some(vec!["updated".to_string()]),
828 };
829
830 assert_eq!(request.uuid, uuid);
831 assert_eq!(request.title, Some("Updated Title".to_string()));
832 assert_eq!(request.status, Some(TaskStatus::Completed));
833 assert_eq!(request.tags, Some(vec!["updated".to_string()]));
834 }
835
836 #[test]
837 fn test_update_task_request_serialization() {
838 let uuid = Uuid::new_v4();
839
840 let request = UpdateTaskRequest {
841 uuid,
842 title: None,
843 notes: None,
844 start_date: None,
845 deadline: None,
846 status: None,
847 project_uuid: None,
848 area_uuid: None,
849 tags: None,
850 };
851
852 let serialized = serde_json::to_string(&request).unwrap();
853 let deserialized: UpdateTaskRequest = serde_json::from_str(&serialized).unwrap();
854
855 assert_eq!(deserialized.uuid, request.uuid);
856 }
857
858 #[test]
859 fn test_task_filters_default() {
860 let filters = TaskFilters::default();
861
862 assert!(filters.status.is_none());
863 assert!(filters.task_type.is_none());
864 assert!(filters.project_uuid.is_none());
865 assert!(filters.area_uuid.is_none());
866 assert!(filters.tags.is_none());
867 assert!(filters.start_date_from.is_none());
868 assert!(filters.start_date_to.is_none());
869 assert!(filters.deadline_from.is_none());
870 assert!(filters.deadline_to.is_none());
871 assert!(filters.search_query.is_none());
872 assert!(filters.limit.is_none());
873 assert!(filters.offset.is_none());
874 }
875
876 #[test]
877 fn test_task_filters_creation() {
878 let project_uuid = Uuid::new_v4();
879 let start_date = NaiveDate::from_ymd_opt(2024, 1, 1).unwrap();
880
881 let filters = TaskFilters {
882 status: Some(TaskStatus::Incomplete),
883 task_type: Some(TaskType::Todo),
884 project_uuid: Some(project_uuid),
885 area_uuid: None,
886 tags: Some(vec!["work".to_string()]),
887 start_date_from: Some(start_date),
888 start_date_to: None,
889 deadline_from: None,
890 deadline_to: None,
891 search_query: Some("test".to_string()),
892 limit: Some(10),
893 offset: Some(0),
894 };
895
896 assert_eq!(filters.status, Some(TaskStatus::Incomplete));
897 assert_eq!(filters.task_type, Some(TaskType::Todo));
898 assert_eq!(filters.project_uuid, Some(project_uuid));
899 assert_eq!(filters.search_query, Some("test".to_string()));
900 assert_eq!(filters.limit, Some(10));
901 assert_eq!(filters.offset, Some(0));
902 }
903
904 #[test]
905 fn test_task_filters_serialization() {
906 let filters = TaskFilters {
907 status: Some(TaskStatus::Completed),
908 task_type: Some(TaskType::Project),
909 project_uuid: None,
910 area_uuid: None,
911 tags: None,
912 start_date_from: None,
913 start_date_to: None,
914 deadline_from: None,
915 deadline_to: None,
916 search_query: None,
917 limit: None,
918 offset: None,
919 };
920
921 let serialized = serde_json::to_string(&filters).unwrap();
922 let deserialized: TaskFilters = serde_json::from_str(&serialized).unwrap();
923
924 assert_eq!(deserialized.status, filters.status);
925 assert_eq!(deserialized.task_type, filters.task_type);
926 }
927
928 #[test]
929 fn test_task_status_equality() {
930 assert_eq!(TaskStatus::Incomplete, TaskStatus::Incomplete);
931 assert_ne!(TaskStatus::Incomplete, TaskStatus::Completed);
932 assert_ne!(TaskStatus::Completed, TaskStatus::Canceled);
933 assert_ne!(TaskStatus::Canceled, TaskStatus::Trashed);
934 }
935
936 #[test]
937 fn test_task_type_equality() {
938 assert_eq!(TaskType::Todo, TaskType::Todo);
939 assert_ne!(TaskType::Todo, TaskType::Project);
940 assert_ne!(TaskType::Project, TaskType::Heading);
941 assert_ne!(TaskType::Heading, TaskType::Area);
942 }
943
944 #[test]
945 fn test_task_with_children() {
946 let parent_uuid = Uuid::new_v4();
947 let child_uuid = Uuid::new_v4();
948 let now = Utc::now();
949
950 let child_task = Task {
951 uuid: child_uuid,
952 title: "Child Task".to_string(),
953 task_type: TaskType::Todo,
954 status: TaskStatus::Incomplete,
955 notes: None,
956 start_date: None,
957 deadline: None,
958 created: now,
959 modified: now,
960 stop_date: None,
961 project_uuid: None,
962 area_uuid: None,
963 parent_uuid: Some(parent_uuid),
964 tags: vec![],
965 children: vec![],
966 };
967
968 let parent_task = Task {
969 uuid: parent_uuid,
970 title: "Parent Task".to_string(),
971 task_type: TaskType::Heading,
972 status: TaskStatus::Incomplete,
973 notes: None,
974 start_date: None,
975 deadline: None,
976 created: now,
977 modified: now,
978 stop_date: None,
979 project_uuid: None,
980 area_uuid: None,
981 parent_uuid: None,
982 tags: vec![],
983 children: vec![child_task],
984 };
985
986 assert_eq!(parent_task.children.len(), 1);
987 assert_eq!(parent_task.children[0].parent_uuid, Some(parent_uuid));
988 assert_eq!(parent_task.children[0].title, "Child Task");
989 }
990
991 #[test]
992 fn test_project_with_tasks() {
993 let project_uuid = Uuid::new_v4();
994 let task_uuid = Uuid::new_v4();
995 let now = Utc::now();
996
997 let task = Task {
998 uuid: task_uuid,
999 title: "Project Task".to_string(),
1000 task_type: TaskType::Todo,
1001 status: TaskStatus::Incomplete,
1002 notes: None,
1003 start_date: None,
1004 deadline: None,
1005 created: now,
1006 modified: now,
1007 stop_date: None,
1008 project_uuid: Some(project_uuid),
1009 area_uuid: None,
1010 parent_uuid: None,
1011 tags: vec![],
1012 children: vec![],
1013 };
1014
1015 let project = Project {
1016 uuid: project_uuid,
1017 title: "Test Project".to_string(),
1018 notes: None,
1019 start_date: None,
1020 deadline: None,
1021 created: now,
1022 modified: now,
1023 area_uuid: None,
1024 tags: vec![],
1025 status: TaskStatus::Incomplete,
1026 tasks: vec![task],
1027 };
1028
1029 assert_eq!(project.tasks.len(), 1);
1030 assert_eq!(project.tasks[0].project_uuid, Some(project_uuid));
1031 assert_eq!(project.tasks[0].title, "Project Task");
1032 }
1033
1034 #[test]
1035 fn test_area_with_projects() {
1036 let area_uuid = Uuid::new_v4();
1037 let project_uuid = Uuid::new_v4();
1038 let now = Utc::now();
1039
1040 let project = Project {
1041 uuid: project_uuid,
1042 title: "Area Project".to_string(),
1043 notes: None,
1044 start_date: None,
1045 deadline: None,
1046 created: now,
1047 modified: now,
1048 area_uuid: Some(area_uuid),
1049 tags: vec![],
1050 status: TaskStatus::Incomplete,
1051 tasks: vec![],
1052 };
1053
1054 let area = Area {
1055 uuid: area_uuid,
1056 title: "Test Area".to_string(),
1057 notes: None,
1058 created: now,
1059 modified: now,
1060 tags: vec![],
1061 projects: vec![project],
1062 };
1063
1064 assert_eq!(area.projects.len(), 1);
1065 assert_eq!(area.projects[0].area_uuid, Some(area_uuid));
1066 assert_eq!(area.projects[0].title, "Area Project");
1067 }
1068}