1use crate::{error::GiteeError, GiteeClient};
2use reqwest::Method;
3use serde::{Deserialize, Serialize};
4use crate::users::User;
5use crate::utils::deserialize_string_or_int;
6
7#[derive(Debug, Clone, Deserialize, Serialize)]
8pub struct Repository {
9 #[serde(deserialize_with = "deserialize_string_or_int")]
10 pub id: String,
11 pub name: String,
12 pub full_name: String,
13 pub description: Option<String>,
14 pub html_url: String,
15 pub ssh_url: Option<String>,
16 pub clone_url: Option<String>,
17 pub created_at: String,
18 pub updated_at: String,
19 pub private: bool,
20 pub fork: bool,
21 pub forks_count: i32,
22 pub stargazers_count: i32,
23 pub watchers_count: i32,
24 pub owner: User,
25 #[serde(default)]
26 pub parent: Option<Box<Repository>>,
27 #[serde(default)]
28 pub pull_requests_enabled: bool,
29 #[serde(default)]
30 pub has_issues: bool,
31 #[serde(default)]
32 pub has_wiki: bool,
33 #[serde(default)]
34 pub has_pages: bool,
35 #[serde(default)]
36 pub has_projects: bool,
37 #[serde(default)]
38 pub default_branch: Option<String>,
39 #[serde(default)]
40 pub license: Option<String>,
41 #[serde(default)]
42 pub pushed_at: Option<String>,
43}
44
45impl GiteeClient {
46 pub async fn get_repo(&self, owner: &str, repo: &str) -> Result<Repository, GiteeError> {
48 let url = format!("{}/repos/{}/{}", self.base_url(), owner, repo);
49 let response = self
50 .client()
51 .request(Method::GET, &url)
52 .header("Authorization", self.auth_header())
53 .send()
54 .await?;
55
56 if !response.status().is_success() {
57 return Err(GiteeError::ApiError(format!(
58 "Failed to get repository: {}",
59 response.status()
60 )));
61 }
62
63 let repo: Repository = response.json().await?;
64 Ok(repo)
65 }
66
67 pub async fn create_user_repo(&self, name: &str, description: Option<&str>, private: bool) -> Result<Repository, GiteeError> {
69 let url = format!("{}/user/repos", self.base_url());
70
71 let mut payload = std::collections::HashMap::new();
72 payload.insert("name", name);
73 payload.insert("private", if private { "true" } else { "false" });
74 if let Some(desc) = description {
75 payload.insert("description", desc);
76 }
77 payload.insert("auto_init", "true");
78
79 let response = self
80 .client()
81 .request(Method::POST, &url)
82 .header("Authorization", self.auth_header())
83 .json(&payload)
84 .send()
85 .await?;
86
87 if !response.status().is_success() {
88 return Err(GiteeError::ApiError(format!(
89 "Failed to create repository: {}",
90 response.status()
91 )));
92 }
93
94 let repo: Repository = response.json().await?;
95 Ok(repo)
96 }
97
98 pub async fn create_org_repo(&self, org: &str, name: &str, description: Option<&str>, private: bool) -> Result<Repository, GiteeError> {
100 let url = format!("{}/orgs/{}/repos", self.base_url(), org);
101
102 let mut payload = std::collections::HashMap::new();
103 payload.insert("name", name);
104 payload.insert("private", if private { "true" } else { "false" });
105 if let Some(desc) = description {
106 payload.insert("description", desc);
107 }
108 payload.insert("auto_init", "true");
109
110 let response = self
111 .client()
112 .request(Method::POST, &url)
113 .header("Authorization", self.auth_header())
114 .json(&payload)
115 .send()
116 .await?;
117
118 if !response.status().is_success() {
119 return Err(GiteeError::ApiError(format!(
120 "Failed to create organization repository: {}",
121 response.status()
122 )));
123 }
124
125 let repo: Repository = response.json().await?;
126 Ok(repo)
127 }
128
129 pub async fn create_enterprise_repo(&self, enterprise: &str, name: &str, description: Option<&str>, private: bool) -> Result<Repository, GiteeError> {
131 let url = format!("{}/enterprises/{}/repos", self.base_url(), enterprise);
132
133 let mut payload = std::collections::HashMap::new();
134 payload.insert("name", name);
135 payload.insert("private", if private { "true" } else { "false" });
136 if let Some(desc) = description {
137 payload.insert("description", desc);
138 }
139 payload.insert("auto_init", "true");
140
141 let response = self
142 .client()
143 .request(Method::POST, &url)
144 .header("Authorization", self.auth_header())
145 .json(&payload)
146 .send()
147 .await?;
148
149 if !response.status().is_success() {
150 return Err(GiteeError::ApiError(format!(
151 "Failed to create enterprise repository: {}",
152 response.status()
153 )));
154 }
155
156 let repo: Repository = response.json().await?;
157 Ok(repo)
158 }
159
160 pub async fn list_user_repos(&self) -> Result<Vec<Repository>, GiteeError> {
162 let url = format!("{}/user/repos", self.base_url());
163 let response = self
164 .client()
165 .request(Method::GET, &url)
166 .header("Authorization", self.auth_header())
167 .send()
168 .await?;
169
170 if !response.status().is_success() {
171 return Err(GiteeError::ApiError(format!(
172 "Failed to list user repositories: {}",
173 response.status()
174 )));
175 }
176
177 let repos: Vec<Repository> = response.json().await?;
178 Ok(repos)
179 }
180
181 pub async fn fork_repository(&self, owner: &str, repo: &str) -> Result<Repository, GiteeError> {
183 let url = format!("{}/repos/{}/{}/forks", self.base_url(), owner, repo);
184 let response = self
185 .client()
186 .request(Method::POST, &url)
187 .header("Authorization", self.auth_header())
188 .send()
189 .await?;
190
191 if !response.status().is_success() {
192 return Err(GiteeError::ApiError(format!(
193 "Failed to fork repository: {}",
194 response.status()
195 )));
196 }
197
198 let repo: Repository = response.json().await?;
199 Ok(repo)
200 }
201
202 pub async fn search_repositories(&self, query: &str, from: Option<i32>, size: Option<i32>, sort: Option<&str>) -> Result<Vec<Repository>, GiteeError> {
204 let url = format!("{}/search/repos", self.base_url());
205
206 let mut params = vec![("q", query.to_string())];
207 if let Some(f) = from { params.push(("from", f.to_string())); }
208 if let Some(s) = size { params.push(("size", s.to_string())); }
209 if let Some(st) = sort { params.push(("sort", st.to_string())); }
210
211 let response = self
212 .client()
213 .request(Method::GET, &url)
214 .header("Authorization", self.auth_header())
215 .query(¶ms)
216 .send()
217 .await?;
218
219 if !response.status().is_success() {
220 return Err(GiteeError::ApiError(format!(
221 "Failed to search repositories: {}",
222 response.status()
223 )));
224 }
225
226 let body = response.text().await?;
227 let v: serde_json::Value = serde_json::from_str(&body)?;
228
229 if let Some(items) = v.get("items") {
230 let repos: Vec<Repository> = serde_json::from_value(items.clone())?;
231 Ok(repos)
232 } else if v.is_array() {
233 let repos: Vec<Repository> = serde_json::from_value(v)?;
234 Ok(repos)
235 } else {
236 Ok(vec![])
237 }
238 }
239
240 pub async fn delete_repo(&self, owner: &str, repo: &str) -> Result<(), GiteeError> {
242 let url = format!("{}/repos/{}/{}", self.base_url(), owner, repo);
243 let response = self
244 .client()
245 .request(Method::DELETE, &url)
246 .header("Authorization", self.auth_header())
247 .send()
248 .await?;
249
250 if !response.status().is_success() {
251 return Err(GiteeError::ApiError(format!(
252 "Failed to delete repository: {}",
253 response.status()
254 )));
255 }
256
257 Ok(())
258 }
259
260 pub async fn star_repo(&self, owner: &str, repo: &str) -> Result<(), GiteeError> {
262 let url = format!("{}/user/starred/{}/{}", self.base_url(), owner, repo);
263 let response = self
264 .client()
265 .request(Method::PUT, &url)
266 .header("Authorization", self.auth_header())
267 .header("Content-Length", 0)
268 .send()
269 .await?;
270
271 if !response.status().is_success() {
272 return Err(GiteeError::ApiError(format!(
273 "Failed to star repository: {}",
274 response.status()
275 )));
276 }
277
278 Ok(())
279 }
280
281 pub async fn unstar_repo(&self, owner: &str, repo: &str) -> Result<(), GiteeError> {
283 let url = format!("{}/user/starred/{}/{}", self.base_url(), owner, repo);
284 let response = self
285 .client()
286 .request(Method::DELETE, &url)
287 .header("Authorization", self.auth_header())
288 .send()
289 .await?;
290
291 if !response.status().is_success() {
292 return Err(GiteeError::ApiError(format!(
293 "Failed to unstar repository: {}",
294 response.status()
295 )));
296 }
297
298 Ok(())
299 }
300
301 pub async fn watch_repo(&self, owner: &str, repo: &str) -> Result<(), GiteeError> {
303 let url = format!("{}/user/subscriptions/{}/{}", self.base_url(), owner, repo);
304
305 let response = self
306 .client()
307 .request(Method::PUT, &url)
308 .header("Authorization", self.auth_header())
309 .header("Content-Length", 0)
310 .send()
311 .await?;
312
313 if !response.status().is_success() {
314 return Err(GiteeError::ApiError(format!(
315 "Failed to watch repository: {}",
316 response.status()
317 )));
318 }
319
320 Ok(())
321 }
322
323 pub async fn unwatch_repo(&self, owner: &str, repo: &str) -> Result<(), GiteeError> {
325 let url = format!("{}/user/subscriptions/{}/{}", self.base_url(), owner, repo);
326 let response = self
327 .client()
328 .request(Method::DELETE, &url)
329 .header("Authorization", self.auth_header())
330 .send()
331 .await?;
332
333 if !response.status().is_success() {
334 return Err(GiteeError::ApiError(format!(
335 "Failed to unwatch repository: {}",
336 response.status()
337 )));
338 }
339
340 Ok(())
341 }
342}