guts_collaboration/
review.rs1use serde::{Deserialize, Serialize};
4use std::time::{SystemTime, UNIX_EPOCH};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum ReviewState {
10 Approved,
12 ChangesRequested,
14 Commented,
16 Dismissed,
18}
19
20impl std::fmt::Display for ReviewState {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 match self {
23 ReviewState::Approved => write!(f, "approved"),
24 ReviewState::ChangesRequested => write!(f, "changes_requested"),
25 ReviewState::Commented => write!(f, "commented"),
26 ReviewState::Dismissed => write!(f, "dismissed"),
27 }
28 }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct Review {
34 pub id: u64,
36 pub repo_key: String,
38 pub pr_number: u32,
40 pub author: String,
42 pub state: ReviewState,
44 pub body: Option<String>,
46 pub commit_id: String,
48 pub created_at: u64,
50 pub submitted_at: Option<u64>,
52}
53
54impl Review {
55 pub fn new(
57 id: u64,
58 repo_key: impl Into<String>,
59 pr_number: u32,
60 author: impl Into<String>,
61 state: ReviewState,
62 commit_id: impl Into<String>,
63 ) -> Self {
64 let now = SystemTime::now()
65 .duration_since(UNIX_EPOCH)
66 .unwrap()
67 .as_secs();
68
69 Self {
70 id,
71 repo_key: repo_key.into(),
72 pr_number,
73 author: author.into(),
74 state,
75 body: None,
76 commit_id: commit_id.into(),
77 created_at: now,
78 submitted_at: Some(now),
79 }
80 }
81
82 pub fn with_body(mut self, body: impl Into<String>) -> Self {
84 self.body = Some(body.into());
85 self
86 }
87
88 pub fn is_approved(&self) -> bool {
90 self.state == ReviewState::Approved
91 }
92
93 pub fn requests_changes(&self) -> bool {
95 self.state == ReviewState::ChangesRequested
96 }
97
98 pub fn is_dismissed(&self) -> bool {
100 self.state == ReviewState::Dismissed
101 }
102
103 pub fn dismiss(&mut self) {
105 self.state = ReviewState::Dismissed;
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn test_review_creation() {
115 let review = Review::new(
116 1,
117 "alice/repo",
118 1,
119 "bob_pubkey",
120 ReviewState::Approved,
121 "abc123",
122 )
123 .with_body("LGTM!");
124
125 assert_eq!(review.id, 1);
126 assert_eq!(review.pr_number, 1);
127 assert_eq!(review.author, "bob_pubkey");
128 assert!(review.is_approved());
129 assert!(!review.requests_changes());
130 assert_eq!(review.body, Some("LGTM!".to_string()));
131 }
132
133 #[test]
134 fn test_changes_requested() {
135 let review = Review::new(
136 2,
137 "alice/repo",
138 1,
139 "carol_pubkey",
140 ReviewState::ChangesRequested,
141 "def456",
142 )
143 .with_body("Please add tests");
144
145 assert!(!review.is_approved());
146 assert!(review.requests_changes());
147 assert!(!review.is_dismissed());
148 }
149
150 #[test]
151 fn test_dismiss_review() {
152 let mut review = Review::new(
153 3,
154 "alice/repo",
155 1,
156 "dave_pubkey",
157 ReviewState::ChangesRequested,
158 "ghi789",
159 );
160
161 review.dismiss();
162 assert!(review.is_dismissed());
163 assert!(!review.requests_changes());
164 }
165
166 #[test]
167 fn test_commented_review() {
168 let review = Review::new(
169 4,
170 "alice/repo",
171 2,
172 "eve_pubkey",
173 ReviewState::Commented,
174 "jkl012",
175 )
176 .with_body("Have you considered using a different approach?");
177
178 assert!(!review.is_approved());
179 assert!(!review.requests_changes());
180 assert!(!review.is_dismissed());
181 assert_eq!(review.state, ReviewState::Commented);
182 }
183}