git_commit_helper_cli/
gerrit.rs1use anyhow::Result;
2use reqwest::Client;
3use log::debug;
4use base64::Engine;
5use serde::Deserialize;
6use std::collections::HashMap;
7
8#[derive(Debug, Deserialize)]
9struct CommitInfo {
10 message: String,
11}
12
13#[derive(Debug, Deserialize)]
14struct RevisionInfo {
15 #[serde(rename = "commit")]
16 commit_info: CommitInfo,
17}
18
19#[derive(Debug, Deserialize)]
20struct ChangeInfo {
21 subject: String,
22 revisions: HashMap<String, RevisionInfo>,
23 #[serde(rename = "current_revision")]
24 current_revision: String,
25}
26
27use crate::terminal_format::print_progress;
28
29pub async fn get_change_info(url: &str) -> Result<String> {
30 debug!("开始获取 Gerrit 改动信息: {}", url);
31
32 let parts: Vec<&str> = url.split("/+/").collect();
35 if parts.len() != 2 {
36 return Err(anyhow::anyhow!("无效的 Gerrit URL"));
37 }
38
39 let parts: Vec<&str> = url.split("/c/").collect();
41 if parts.len() != 2 {
42 return Err(anyhow::anyhow!("无效的 Gerrit URL"));
43 }
44
45 let base_url = parts[0];
46 let mut path_parts = parts[1].split("/+/");
47
48 let _project = path_parts.next()
49 .ok_or_else(|| anyhow::anyhow!("无法解析项目路径"))?
50 .trim_end_matches('/');
51
52 let change_id = path_parts.next()
53 .ok_or_else(|| anyhow::anyhow!("无法解析改动ID"))?;
54
55 let api_url = format!(
57 "{}/a/changes/{}?o=CURRENT_REVISION&o=CURRENT_COMMIT",
58 base_url,
59 change_id
60 );
61
62 print_progress("正在请求 gerrit 获取改动信息", None);
64
65 let mut request = Client::new()
67 .get(&api_url)
68 .header("Accept", "application/json");
69
70 request = add_auth(request);
72
73 let response = request.send().await?;
74
75 print_progress("正在请求 gerrit 获取改动信息", Some(100));
76
77 if !response.status().is_success() {
78 return Err(anyhow::anyhow!(
79 "获取 Gerrit 改动信息失败: HTTP {}",
80 response.status()
81 ));
82 }
83
84 let json_text = response.text().await?;
85 let json = json_text.trim_start_matches(")]}'\n");
87
88 let info: ChangeInfo = serde_json::from_str(json)?;
89 let current_revision = info.revisions.get(&info.current_revision)
90 .ok_or_else(|| anyhow::anyhow!("未找到当前版本信息"))?;
91
92 let mut result = format!("标题:{}", info.subject);
93 let message = current_revision.commit_info.message.lines()
94 .skip(1) .collect::<Vec<&str>>()
96 .join("\n")
97 .trim()
98 .to_string();
99
100 if !message.is_empty() {
101 result.push_str(&format!("\n\n描述:\n{}", message));
102 }
103 Ok(result)
104}
105
106pub async fn get_change_diff(url: &str) -> Result<String> {
107 debug!("开始获取 Gerrit 改动内容: {}", url);
108
109 let parts: Vec<&str> = url.split("/+/").collect();
112 if parts.len() != 2 {
113 return Err(anyhow::anyhow!("无效的 Gerrit URL"));
114 }
115
116 let parts: Vec<&str> = url.split("/c/").collect();
118 if parts.len() != 2 {
119 return Err(anyhow::anyhow!("无效的 Gerrit URL"));
120 }
121
122 let base_url = parts[0];
123 let mut path_parts = parts[1].split("/+/");
124
125 let project = path_parts.next()
126 .ok_or_else(|| anyhow::anyhow!("无法解析项目路径"))?
127 .trim_end_matches('/');
128
129 let change_id = path_parts.next()
130 .ok_or_else(|| anyhow::anyhow!("无法解析改动ID"))?;
131
132 let api_url = format!(
135 "{}/a/changes/{}~{}/revisions/current/patch", base_url,
137 project.replace("/", "%2F"),
138 change_id
139 );
140
141 debug!("Gerrit API URL: {}", api_url);
142
143 print_progress("正在请求 gerrit 获取改动内容", None);
145
146 let mut request = Client::new()
148 .get(&api_url)
149 .header("Accept", "text/plain");
150
151 request = add_auth(request);
152
153 let response = request.send().await?;
154
155 print_progress("正在请求 gerrit 获取改动内容", Some(100));
156
157 if !response.status().is_success() {
158 return Err(anyhow::anyhow!(
159 "获取 Gerrit 改动失败: HTTP {}",
160 response.status()
161 ));
162 }
163
164 let encoded_diff = response.text().await?;
165 if encoded_diff.trim().is_empty() {
166 return Err(anyhow::anyhow!("未发现任何代码改动"));
167 }
168
169 let diff = match base64::engine::general_purpose::STANDARD.decode(encoded_diff.trim()) {
171 Ok(bytes) => String::from_utf8_lossy(&bytes).into_owned(),
172 Err(e) => return Err(anyhow::anyhow!("解析 patch 内容失败: {}", e)),
173 };
174
175 debug!("获取到的 Gerrit patch 内容:\n{}", diff);
176
177 Ok(diff)
178}
179
180fn add_auth(mut request: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
182 let mut auth_added = false;
184 if let Ok(config) = crate::config::Config::load() {
185 if let Some(gerrit_config) = config.gerrit {
186 if let Some(token) = gerrit_config.token {
187 debug!("使用配置文件中的 Token 认证");
188 request = request.bearer_auth(&token);
189 auth_added = true;
190 } else if let Some(username) = &gerrit_config.username {
191 if let Some(password) = &gerrit_config.password {
192 debug!("使用配置文件中的用户名密码认证: {}", username);
193 request = request.basic_auth(username, Some(password));
194 auth_added = true;
195 }
196 }
197 }
198 }
199
200 if !auth_added {
202 if let Ok(username) = std::env::var("GERRIT_USERNAME") {
203 if let Ok(password) = std::env::var("GERRIT_PASSWORD") {
204 debug!("使用环境变量中的用户名认证: {}", username);
205 request = request.basic_auth(&username, Some(&password));
206 auth_added = true;
207 }
208 } else if let Ok(token) = std::env::var("GERRIT_TOKEN") {
209 debug!("使用环境变量中的 Token 认证");
210 request = request.bearer_auth(&token);
211 auth_added = true;
212 }
213 }
214
215 if !auth_added {
216 debug!("未找到任何认证信息,尝试匿名访问");
217 }
218
219 request
220}