meritocrab_api/
credit_commands.rs1use lazy_static::lazy_static;
2use regex::Regex;
3
4#[derive(Debug, Clone, PartialEq)]
6pub enum CreditCommand {
7 Check { username: String },
9 Override {
11 username: String,
12 delta: i32,
13 reason: String,
14 },
15 Blacklist { username: String },
17}
18
19lazy_static! {
20 static ref CHECK_REGEX: Regex = Regex::new(r#"(?m)^/credit\s+check\s+@(\w+)\s*$"#).unwrap();
22
23 static ref OVERRIDE_REGEX: Regex = Regex::new(r#"(?m)^/credit\s+override\s+@(\w+)\s+([+-]\d+)\s+"([^"]+)"\s*$"#).unwrap();
25
26 static ref BLACKLIST_REGEX: Regex = Regex::new(r#"(?m)^/credit\s+blacklist\s+@(\w+)\s*$"#).unwrap();
28}
29
30pub fn parse_credit_command(comment_body: &str) -> Option<CreditCommand> {
35 if let Some(captures) = CHECK_REGEX.captures(comment_body) {
37 let username = captures.get(1).unwrap().as_str().to_string();
38 return Some(CreditCommand::Check { username });
39 }
40
41 if let Some(captures) = OVERRIDE_REGEX.captures(comment_body) {
43 let username = captures.get(1).unwrap().as_str().to_string();
44 let delta_str = captures.get(2).unwrap().as_str();
45 let reason = captures.get(3).unwrap().as_str().to_string();
46
47 if let Ok(delta) = delta_str.parse::<i32>() {
49 return Some(CreditCommand::Override {
50 username,
51 delta,
52 reason,
53 });
54 }
55 }
56
57 if let Some(captures) = BLACKLIST_REGEX.captures(comment_body) {
59 let username = captures.get(1).unwrap().as_str().to_string();
60 return Some(CreditCommand::Blacklist { username });
61 }
62
63 None
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn test_parse_check_command() {
72 let comment = "/credit check @user123";
73 let cmd = parse_credit_command(comment);
74 assert_eq!(
75 cmd,
76 Some(CreditCommand::Check {
77 username: "user123".to_string()
78 })
79 );
80 }
81
82 #[test]
83 fn test_parse_check_command_with_whitespace() {
84 let comment = "/credit check @user123 ";
85 let cmd = parse_credit_command(comment);
86 assert_eq!(
87 cmd,
88 Some(CreditCommand::Check {
89 username: "user123".to_string()
90 })
91 );
92 }
93
94 #[test]
95 fn test_parse_override_positive() {
96 let comment = r#"/credit override @user123 +10 "good first contribution""#;
97 let cmd = parse_credit_command(comment);
98 assert_eq!(
99 cmd,
100 Some(CreditCommand::Override {
101 username: "user123".to_string(),
102 delta: 10,
103 reason: "good first contribution".to_string()
104 })
105 );
106 }
107
108 #[test]
109 fn test_parse_override_negative() {
110 let comment = r#"/credit override @spammer -20 "spam PR""#;
111 let cmd = parse_credit_command(comment);
112 assert_eq!(
113 cmd,
114 Some(CreditCommand::Override {
115 username: "spammer".to_string(),
116 delta: -20,
117 reason: "spam PR".to_string()
118 })
119 );
120 }
121
122 #[test]
123 fn test_parse_blacklist_command() {
124 let comment = "/credit blacklist @badactor";
125 let cmd = parse_credit_command(comment);
126 assert_eq!(
127 cmd,
128 Some(CreditCommand::Blacklist {
129 username: "badactor".to_string()
130 })
131 );
132 }
133
134 #[test]
135 fn test_parse_no_command() {
136 let comment = "This is just a regular comment";
137 let cmd = parse_credit_command(comment);
138 assert_eq!(cmd, None);
139 }
140
141 #[test]
142 fn test_parse_invalid_command() {
143 let comment = "/credit unknown @user";
144 let cmd = parse_credit_command(comment);
145 assert_eq!(cmd, None);
146 }
147
148 #[test]
149 fn test_parse_command_in_multi_line_comment() {
150 let comment = r#"Some context before
151
152/credit check @user123
153
154Some context after"#;
155 let cmd = parse_credit_command(comment);
156 assert_eq!(
157 cmd,
158 Some(CreditCommand::Check {
159 username: "user123".to_string()
160 })
161 );
162 }
163
164 #[test]
165 fn test_parse_override_with_multiword_reason() {
166 let comment = r#"/credit override @user +5 "excellent bug fix with detailed explanation""#;
167 let cmd = parse_credit_command(comment);
168 assert_eq!(
169 cmd,
170 Some(CreditCommand::Override {
171 username: "user".to_string(),
172 delta: 5,
173 reason: "excellent bug fix with detailed explanation".to_string()
174 })
175 );
176 }
177}