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