git_commit_helper_cli/
review.rs1use anyhow::Result;
2use std::process::Command;
3use crate::config::Config;
4use crate::ai_service;
5use crate::github;
6use crate::gerrit;
7use log::{debug, info};
8use crate::terminal_format::Style;
9
10pub async fn review_remote_changes(config: &Config, url: &str) -> Result<String> {
11 debug!("开始审查远程代码改动: {}", url);
12
13 let (change_message, diff) = if url.contains("github.com") {
15 if url.contains("/pull/") {
16 let pr_info = github::get_pr_info(url).await?;
17 let diff = github::get_pr_diff(url).await?;
18 (pr_info, diff)
19 } else if url.contains("/commit/") {
20 let msg = github::get_commit_info(url).await?;
21 let diff = github::get_commit_diff(url).await?;
22 (msg, diff)
23 } else {
24 return Err(anyhow::anyhow!("无效的GitHub URL,必须是PR或commit链接"));
25 }
26 } else if url.contains("/+/") {
27 let msg = gerrit::get_change_info(url).await?;
29 let diff = gerrit::get_change_diff(url).await?;
30 (msg, diff)
31 } else {
32 return Err(anyhow::anyhow!("无效的URL,必须是GitHub或Gerrit链接"));
33 };
34
35 if diff.trim().is_empty() {
36 return Err(anyhow::anyhow!("未发现任何代码改动"));
37 }
38
39 let mut review = String::new();
41 if !change_message.is_empty() {
42 let mut parts = change_message.splitn(2, "\n描述:\n");
44 let title = parts.next().unwrap().trim_start_matches("标题:").trim();
45 let description = parts.next();
46
47 let mut info = String::new();
48
49 let title_info = if title.chars().any(|c| c.is_ascii_alphabetic()) {
51 let translator = ai_service::create_translator(config).await?;
53 let prompt = format!("请将以下 PR 标题翻译成中文:\n\n{}", title);
54 let chinese = translator.chat("你是一个代码提交信息翻译助手。", &prompt).await?;
55 format!("标题:{}\n中文翻译:{}\n", title, chinese)
56 } else {
57 format!("标题:{}\n", title)
58 };
59 info.push_str(&title_info);
60
61 if let Some(desc) = description {
63 if !desc.trim().is_empty() {
64 if desc.chars().any(|c| c.is_ascii_alphabetic()) {
65 let translator = ai_service::create_translator(config).await?;
67 let prompt = format!("请将以下 PR 描述翻译成中文:\n\n{}", desc);
68 let chinese = translator.chat("你是一个代码提交信息翻译助手。", &prompt).await?;
69 info.push_str(&format!("\n描述:\n{}\n中文翻译:\n{}\n", desc, chinese));
70 } else {
71 info.push_str(&format!("\n描述:\n{}\n", desc));
72 }
73 }
74 }
75
76 review.push_str(&info);
77 review.push('\n');
78 }
79
80 let translator = ai_service::create_translator(config).await?;
82 info!("正在使用 {:?} 服务进行代码审查...", config.default_service);
83
84 let system_prompt = get_review_prompt();
85 let review_result = translator.chat(&system_prompt, &diff).await?;
86 review.push_str(&review_result);
87
88 let review = format_review_for_terminal(&review);
90 Ok(review)
91}
92
93fn format_review_for_terminal(input: &str) -> String {
95 let mut out = String::new();
96 let mut in_report = false;
97 for line in input.lines() {
98 if line.trim().is_empty() {
99 out.push('\n');
100 continue;
101 }
102 if line.starts_with("标题:") {
103 out.push_str(&Style::separator());
104 out.push_str(&Style::title(line));
105 } else if line.starts_with("中文翻译:") {
106 out.push_str(&Style::green(line));
107 } else if line.starts_with("描述:") {
108 out.push_str(&Style::blue(line));
109 } else if line.starts_with("代码审查报告:") {
110 out.push_str(&Style::separator());
111 out.push_str(&Style::yellow(line));
112 in_report = true;
113 } else if line.starts_with("警告") {
114 out.push_str(&Style::yellow(line));
115 } else if line.starts_with("错误") {
116 out.push_str(&Style::red(line));
117 } else if in_report {
118 out.push_str(&Style::plain(line));
119 } else {
120 out.push_str(&Style::plain(line));
121 }
122 }
123 out.push_str(&Style::separator());
124 out
125}
126
127pub async fn review_changes(config: &Config, no_review: bool) -> Result<Option<String>> {
128 if no_review {
130 info!("已通过 --no-review 参数禁用代码审查");
131 return Ok(None);
132 }
133
134 if !config.ai_review {
135 info!("AI代码审查功能已在配置中禁用,可以使用 git-commit-helper ai-review --enable 启用");
136 return Ok(None);
137 }
138
139 let diff = get_staged_changes()?;
141 if diff.trim().is_empty() {
142 info!("没有检测到暂存的代码改动");
143 return Ok(None);
144 }
145
146 let translator = ai_service::create_translator(config).await?;
148 info!("正在使用 {:?} 服务进行代码审查...", config.default_service);
149
150 let system_prompt = get_review_prompt();
151 let review = translator.chat(&system_prompt, &diff).await?;
152
153 Ok(Some(review))
154}
155
156fn get_review_prompt() -> String {
158 let prompt_path = crate::config::Config::config_path()
160 .expect("无法获取配置目录")
161 .parent()
162 .expect("无法获取父目录")
163 .join("review_prompt.txt");
164
165 if prompt_path.exists() {
167 info!("正在使用 {:?} 提示词文件, 进行代码审查...", prompt_path.display());
168 std::fs::read_to_string(&prompt_path).unwrap_or_else(|err| {
169 log::error!("Failed to read review prompt file {:?}: {}", prompt_path.display(), err);
170 DEFAULT_REVIEW_PROMPT.to_string()
171 })
172 } else {
173 DEFAULT_REVIEW_PROMPT.to_string()
174 }
175}
176
177const DEFAULT_REVIEW_PROMPT:&str = r#"您是一位专业的代码审查者,请对以下代码变更进行审查并给出中文评价。请着重关注:
178
1791. 代码质量:
180 - 代码是否清晰易懂
181 - 变量和函数命名是否恰当
182 - 代码结构是否合理
183
1842. 潜在问题:
185 - 可能的bug
186 - 边界条件处理
187 - 异常情况的处理
188 - 资源使用和释放
189
1903. 最佳实践:
191 - 是否符合编程规范
192 - 是否遵循设计模式
193 - 代码重用性
194 - 模块化和解耦
195
1964. 性能考虑:
197 - 算法效率
198 - 资源使用效率
199 - 可能的性能瓶颈
200
2015. 安全性:
202 - 输入验证
203 - 数据安全
204 - 权限检查
205
206请以"代码审查报告:"开头,使用简洁的语言描述发现的问题和改进建议。如果代码符合最佳实践,也请给出正面的评价。
207"#;
208
209fn get_staged_changes() -> Result<String> {
210 let output = Command::new("git")
211 .args(&["diff", "--cached"])
212 .output()?;
213
214 if !output.status.success() {
215 return Err(anyhow::anyhow!("获取暂存区改动失败"));
216 }
217
218 Ok(String::from_utf8(output.stdout)?)
219}
220
221pub fn should_skip_review(message: &str) -> bool {
222 message.starts_with("Merge") ||
223 message.starts_with("Cherry-pick") ||
224 message.starts_with("Revert")
225}
226
227pub async fn review_local_commit(config: &Config, commit_id: &str) -> Result<String> {
228 debug!("开始审查本地commit: {}", commit_id);
229
230 let diff = get_commit_diff(commit_id)?;
232
233 if diff.trim().is_empty() {
234 return Err(anyhow::anyhow!("未发现任何代码改动"));
235 }
236
237 info!("正在使用 {:?} 服务进行代码审查...", config.default_service);
239 let mut review = String::new();
240 let translator = ai_service::create_translator(config).await?;
241 let system_prompt = get_review_prompt();
242 let review_result = translator.chat(&system_prompt, &diff).await?;
243 review.push_str(&review_result);
244
245 Ok(review)
246}
247
248fn get_commit_diff(commit_id: &str) -> Result<String> {
249 let output = Command::new("git")
250 .args(&["show", "--pretty=format:", commit_id])
251 .output()?;
252
253 if !output.status.success() {
254 return Err(anyhow::anyhow!("获取commit差异失败"));
255 }
256
257 Ok(String::from_utf8(output.stdout)?)
258}