Skip to main content

gitee_rs/issues/
mod.rs

1use crate::{error::GiteeError, GiteeClient};
2use reqwest::Method;
3
4mod models;
5pub use models::*;
6
7impl GiteeClient {
8    /// List all issues
9    pub async fn list_issues(&self, options: Option<IssueListOptions>) -> Result<Vec<Issue>, GiteeError> {
10        let url = format!("{}/issues", self.base_url());
11        let mut request = self.client().request(Method::GET, &url)
12            .header("Authorization", self.auth_header());
13        
14        if let Some(opts) = options {
15            request = request.query(&opts);
16        }
17
18        let response = request.send().await?;
19
20        if !response.status().is_success() {
21            return Err(GiteeError::ApiError(format!(
22                "Failed to list issues: {}",
23                response.status()
24            )));
25        }
26
27        let issues: Vec<Issue> = response.json().await?;
28        Ok(issues)
29    }
30
31    /// List repository issues
32    pub async fn list_repo_issues(&self, owner: &str, repo: &str, options: Option<IssueListOptions>) -> Result<Vec<Issue>, GiteeError> {
33        let url = format!("{}/repos/{}/{}/issues", self.base_url(), owner, repo);
34        let mut request = self.client().request(Method::GET, &url)
35            .header("Authorization", self.auth_header());
36        
37        if let Some(opts) = options {
38            request = request.query(&opts);
39        }
40
41        let response = request.send().await?;
42
43        if !response.status().is_success() {
44            return Err(GiteeError::ApiError(format!(
45                "Failed to list repo issues: {}",
46                response.status()
47            )));
48        }
49
50        let issues: Vec<Issue> = response.json().await?;
51        Ok(issues)
52    }
53
54    /// Create a new issue
55    pub async fn create_issue(
56        &self,
57        repo_owner: &str,
58        repo_name: &str,
59        title: &str,
60        body: Option<&str>,
61    ) -> Result<Issue, GiteeError> {
62        let url = format!("{}/repos/{}/issues", self.base_url(), repo_owner);
63
64        let mut payload = std::collections::HashMap::new();
65        payload.insert("repo", repo_name.to_string());
66        payload.insert("title", title.to_string());
67        if let Some(body) = body {
68            payload.insert("body", body.to_string());
69        }
70
71        let response = self
72            .client()
73            .request(Method::POST, &url)
74            .header("Authorization", self.auth_header())
75            .json(&payload)
76            .send()
77            .await?;
78
79        if !response.status().is_success() {
80            return Err(GiteeError::ApiError(format!(
81                "Failed to create issue: {}",
82                response.status()
83            )));
84        }
85
86        let issue: Issue = response.json().await?;
87        Ok(issue)
88    }
89
90    /// Close an issue by setting its state to "closed"
91    pub async fn close_issue(
92        &self,
93        repo_owner: &str,
94        repo_name: &str,
95        issue_number: &str,
96    ) -> Result<Issue, GiteeError> {
97        let url = format!(
98            "{}/repos/{}/{}/issues/{}",
99            self.base_url(),
100            repo_owner,
101            repo_name,
102            issue_number
103        );
104
105        let payload = serde_json::json!({
106            "state": "closed"
107        });
108
109        let response = self
110            .client()
111            .request(Method::PATCH, &url)
112            .header("Authorization", self.auth_header())
113            .json(&payload)
114            .send()
115            .await?;
116
117        if !response.status().is_success() {
118            return Err(GiteeError::ApiError(format!(
119                "Failed to close issue: {}",
120                response.status()
121            )));
122        }
123
124        let issue: Issue = response.json().await?;
125        Ok(issue)
126    }
127
128    /// Update an issue
129    pub async fn update_issue(&self, owner: &str, repo: &str, number: &str, title: Option<&str>, body: Option<&str>, state: Option<&str>) -> Result<Issue, GiteeError> {
130        let url = format!("{}/repos/{}/{}/issues/{}", self.base_url(), owner, repo, number);
131
132        let mut payload = std::collections::HashMap::new();
133        if let Some(t) = title {
134            payload.insert("title", t);
135        }
136        if let Some(b) = body {
137            payload.insert("body", b);
138        }
139        if let Some(s) = state {
140            payload.insert("state", s);  // "open" or "closed"
141        }
142
143        let response = self
144            .client()
145            .request(Method::PATCH, &url)
146            .header("Authorization", self.auth_header())
147            .json(&payload)
148            .send()
149            .await?;
150
151        if !response.status().is_success() {
152            return Err(GiteeError::ApiError(format!(
153                "Failed to update issue: {}",
154                response.status()
155            )));
156        }
157
158        let issue: Issue = response.json().await?;
159        Ok(issue)
160    }
161
162    /// Get issue detail
163    pub async fn get_issue_detail(&self, owner: &str, repo: &str, number: &str) -> Result<Issue, GiteeError> {
164        let url = format!("{}/repos/{}/{}/issues/{}", self.base_url(), owner, repo, number);
165
166        let response = self
167            .client()
168            .request(Method::GET, &url)
169            .header("Authorization", self.auth_header())
170            .send()
171            .await?;
172
173        if !response.status().is_success() {
174            return Err(GiteeError::ApiError(format!(
175                "Failed to get issue detail: {}",
176                response.status()
177            )));
178        }
179
180        let issue: Issue = response.json().await?;
181        Ok(issue)
182    }
183
184    /// Comment on an issue
185    pub async fn comment_issue(&self, owner: &str, repo: &str, number: &str, body: &str) -> Result<Comment, GiteeError> {
186        let url = format!("{}/repos/{}/{}/issues/{}/comments", self.base_url(), owner, repo, number);
187
188        let payload = [("body", body)];
189
190        let response = self
191            .client()
192            .request(Method::POST, &url)
193            .header("Authorization", self.auth_header())
194            .form(&payload)
195            .send()
196            .await?;
197
198        if !response.status().is_success() {
199            return Err(GiteeError::ApiError(format!(
200                "Failed to comment on issue: {}",
201                response.status()
202            )));
203        }
204
205        let comment: Comment = response.json().await?;
206        Ok(comment)
207    }
208
209    /// List issue comments
210    pub async fn list_issue_comments(&self, owner: &str, repo: &str, number: &str) -> Result<Vec<Comment>, GiteeError> {
211        let url = format!("{}/repos/{}/{}/issues/{}/comments", self.base_url(), owner, repo, number);
212
213        let response = self
214            .client()
215            .request(Method::GET, &url)
216            .header("Authorization", self.auth_header())
217            .send()
218            .await?;
219
220        if !response.status().is_success() {
221            return Err(GiteeError::ApiError(format!(
222                "Failed to list issue comments: {}",
223                response.status()
224            )));
225        }
226
227        let comments: Vec<Comment> = response.json().await?;
228        Ok(comments)
229    }
230
231    /// List repository milestones
232    pub async fn list_repo_milestones(&self, owner: &str, repo: &str, state: Option<&str>) -> Result<Vec<Milestone>, GiteeError> {
233        let url = format!("{}/repos/{}/{}/milestones", self.base_url(), owner, repo);
234        let mut request = self.client().request(Method::GET, &url)
235            .header("Authorization", self.auth_header());
236        
237        if let Some(s) = state {
238            request = request.query(&[("state", s)]);
239        }
240
241        let response = request.send().await?;
242
243        if !response.status().is_success() {
244            return Err(GiteeError::ApiError(format!(
245                "Failed to list milestones: {}",
246                response.status()
247            )));
248        }
249
250        let milestones: Vec<Milestone> = response.json().await?;
251        Ok(milestones)
252    }
253
254    /// Create a new milestone
255    pub async fn create_milestone(&self, owner: &str, repo: &str, title: &str, description: Option<&str>, due_on: Option<&str>) -> Result<Milestone, GiteeError> {
256        let url = format!("{}/repos/{}/{}/milestones", self.base_url(), owner, repo);
257        
258        let mut payload = serde_json::json!({
259            "title": title,
260        });
261
262        if let Some(d) = description {
263            payload["description"] = serde_json::Value::String(d.to_string());
264        }
265        if let Some(due) = due_on {
266            payload["due_on"] = serde_json::Value::String(due.to_string());
267        }
268
269        let response = self
270            .client()
271            .request(Method::POST, &url)
272            .header("Authorization", self.auth_header())
273            .json(&payload)
274            .send()
275            .await?;
276
277        if !response.status().is_success() {
278            return Err(GiteeError::ApiError(format!(
279                "Failed to create milestone: {}",
280                response.status()
281            )));
282        }
283
284        let milestone: Milestone = response.json().await?;
285        Ok(milestone)
286    }
287
288    /// Get a milestone by number
289    pub async fn get_milestone(&self, owner: &str, repo: &str, number: i32) -> Result<Milestone, GiteeError> {
290        let url = format!("{}/repos/{}/{}/milestones/{}", self.base_url(), owner, repo, number);
291        let response = self
292            .client()
293            .request(Method::GET, &url)
294            .header("Authorization", self.auth_header())
295            .send()
296            .await?;
297
298        if !response.status().is_success() {
299            return Err(GiteeError::ApiError(format!(
300                "Failed to get milestone: {}",
301                response.status()
302            )));
303        }
304
305        let milestone: Milestone = response.json().await?;
306        Ok(milestone)
307    }
308
309    /// Update a milestone
310    pub async fn update_milestone(&self, owner: &str, repo: &str, number: i32, title: Option<&str>, description: Option<&str>, state: Option<&str>) -> Result<Milestone, GiteeError> {
311        let url = format!("{}/repos/{}/{}/milestones/{}", self.base_url(), owner, repo, number);
312        
313        let mut payload = std::collections::HashMap::new();
314        if let Some(t) = title { payload.insert("title", t); }
315        if let Some(d) = description { payload.insert("description", d); }
316        if let Some(s) = state { payload.insert("state", s); }
317
318        let response = self
319            .client()
320            .request(Method::PATCH, &url)
321            .header("Authorization", self.auth_header())
322            .json(&payload)
323            .send()
324            .await?;
325
326        if !response.status().is_success() {
327            return Err(GiteeError::ApiError(format!(
328                "Failed to update milestone: {}",
329                response.status()
330            )));
331        }
332
333        let milestone: Milestone = response.json().await?;
334        Ok(milestone)
335    }
336
337    /// Delete a milestone
338    pub async fn delete_milestone(&self, owner: &str, repo: &str, number: i32) -> Result<(), GiteeError> {
339        let url = format!("{}/repos/{}/{}/milestones/{}", self.base_url(), owner, repo, number);
340        let response = self
341            .client()
342            .request(Method::DELETE, &url)
343            .header("Authorization", self.auth_header())
344            .send()
345            .await?;
346
347        if !response.status().is_success() {
348            return Err(GiteeError::ApiError(format!(
349                "Failed to delete milestone: {}",
350                response.status()
351            )));
352        }
353
354        Ok(())
355    }
356}