Skip to main content

gitee_rs/repos/
mod.rs

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    /// Get repository information
47    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    /// Create a new user repository
68    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    /// Create a new organization repository
99    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    /// Create a new enterprise repository
130    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    /// List user repositories
161    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    /// Fork a repository
182    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    /// Search repositories (Open source)
203    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(&params)
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    /// Delete a repository
241    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    /// Star a repository
261    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    /// Unstar a repository
282    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    /// Watch a repository
302    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    /// Unwatch a repository
324    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}