1use crate::namespace::Namespace;
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7pub enum NoteStatus {
8 Open,
9 Resolved,
10 Approved,
11 Rejected,
12}
13
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15pub struct Note {
16 pub id: Uuid, pub commit: String, pub file: Option<String>, pub line_start: Option<u32>,
20 pub line_end: Option<u32>,
21 pub body: String, pub author: String, pub timestamp: DateTime<Utc>,
24 pub namespace: Namespace,
25 pub thread_id: Option<Uuid>, pub status: NoteStatus,
27 pub tags: Vec<String>,
28}
29
30impl Note {
31 #[allow(clippy::too_many_arguments)]
32 pub fn new(
33 commit: String,
34 file: Option<String>,
35 line_start: Option<u32>,
36 line_end: Option<u32>,
37 body: String,
38 author: String,
39 namespace: Namespace,
40 ) -> Self {
41 Self {
42 id: Uuid::new_v4(),
43 commit,
44 file,
45 line_start,
46 line_end,
47 body,
48 author,
49 timestamp: Utc::now(),
50 namespace,
51 thread_id: None,
52 status: NoteStatus::Open,
53 tags: Vec::new(),
54 }
55 }
56
57 pub fn reply(parent: &Note, body: String, author: String) -> Self {
58 Self {
59 id: Uuid::new_v4(),
60 commit: parent.commit.clone(),
61 file: parent.file.clone(),
62 line_start: parent.line_start,
63 line_end: parent.line_end,
64 body,
65 author,
66 timestamp: Utc::now(),
67 namespace: parent.namespace.clone(),
68 thread_id: Some(parent.id),
69 status: NoteStatus::Open,
70 tags: Vec::new(),
71 }
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn test_note_new_with_all_fields() {
81 let commit = "a1b2c3d4e5f6".to_string();
82 let file = Some("src/main.rs".to_string());
83 let line_start = Some(10);
84 let line_end = Some(20);
85 let body = "This is a test note body.".to_string();
86 let author = "Tester <test@example.com>".to_string();
87 let namespace = Namespace::Comments;
88
89 let note = Note::new(
90 commit.clone(),
91 file.clone(),
92 line_start,
93 line_end,
94 body.clone(),
95 author.clone(),
96 namespace.clone(),
97 );
98
99 assert!(!note.id.is_nil());
100 assert_eq!(note.commit, commit);
101 assert_eq!(note.file, file);
102 assert_eq!(note.line_start, line_start);
103 assert_eq!(note.line_end, line_end);
104 assert_eq!(note.body, body);
105 assert_eq!(note.author, author);
106 assert_eq!(note.namespace, namespace);
107 assert_eq!(note.thread_id, None);
108 assert_eq!(note.status, NoteStatus::Open);
109 assert!(note.tags.is_empty());
110 }
111
112 #[test]
113 fn test_note_new_with_optional_fields_none() {
114 let commit = "a1b2c3d4e5f6".to_string();
115 let body = "Note without file or lines.".to_string();
116 let author = "Tester <test@example.com>".to_string();
117 let namespace = Namespace::Comments;
118
119 let note = Note::new(
120 commit.clone(),
121 None,
122 None,
123 None,
124 body.clone(),
125 author.clone(),
126 namespace.clone(),
127 );
128
129 assert!(!note.id.is_nil());
130 assert_eq!(note.file, None);
131 assert_eq!(note.line_start, None);
132 assert_eq!(note.line_end, None);
133 assert_eq!(note.status, NoteStatus::Open);
134 }
135
136 #[test]
137 fn test_note_reply() {
138 let parent = Note::new(
139 "a1b2c3d4e5f6".to_string(),
140 Some("src/lib.rs".to_string()),
141 Some(1),
142 Some(5),
143 "Parent note".to_string(),
144 "Parent Author <parent@example.com>".to_string(),
145 Namespace::Comments,
146 );
147
148 let reply_body = "This is a reply".to_string();
149 let reply_author = "Replier <replier@example.com>".to_string();
150
151 let reply = Note::reply(&parent, reply_body.clone(), reply_author.clone());
152
153 assert_ne!(reply.id, parent.id);
154 assert!(!reply.id.is_nil());
155 assert_eq!(reply.commit, parent.commit);
156 assert_eq!(reply.file, parent.file);
157 assert_eq!(reply.line_start, parent.line_start);
158 assert_eq!(reply.line_end, parent.line_end);
159 assert_eq!(reply.namespace, parent.namespace);
160 assert_eq!(reply.thread_id, Some(parent.id));
161 assert_eq!(reply.body, reply_body);
162 assert_eq!(reply.author, reply_author);
163 assert_eq!(reply.status, NoteStatus::Open);
164 assert!(reply.tags.is_empty());
165 }
166}