rust_cnb/
issues.rs

1//! Issues API 客户端
2
3use crate::error::{ApiError, Result};
4use reqwest::Client;
5use serde_json::Value;
6use url::Url;
7
8/// Issues API 客户端
9pub struct IssuesClient {
10    base_url: String,
11    client: Client,
12}
13
14impl IssuesClient {
15    /// 创建新的 Issues API 客户端
16    pub fn new(base_url: String, client: Client) -> Self {
17        Self { base_url, client }
18    }
19
20    /// 设置认证信息
21    pub fn with_auth(self, token: &str) -> Self {
22        // 这里可以扩展认证逻辑
23        self
24    }
25
26    /// 查询指定的 Issues。Get an issue.
27    pub async fn get_repo_issues_number(
28        &self,
29        repo: String,
30        number: i64,
31    ) -> Result<Value> {
32        let path = format!("/{}/-/issues/{}", repo, number);
33        let url = Url::parse(&format!("{}{}", self.base_url, path))?;
34        
35
36                let request = self.client.request(
37            reqwest::Method::GET,
38            url
39        );
40        
41
42
43
44        let response = request.send().await?;
45        
46        if response.status().is_success() {
47            let json: Value = response.json().await?;
48            Ok(json)
49        } else {
50            Err(ApiError::HttpError(response.status().as_u16()))
51        }
52    }
53
54    /// 更新一个 Issue。Update an issue.
55    pub async fn patch_repo_issues_number(
56        &self,
57        repo: String,
58        number: i64,
59        patch_issue_form: serde_json::Value,
60    ) -> Result<Value> {
61        let path = format!("/{}/-/issues/{}", repo, number);
62        let url = Url::parse(&format!("{}{}", self.base_url, path))?;
63        
64
65        
66        let mut request = self.client.request(
67            reqwest::Method::PATCH,
68            url
69        );
70
71
72
73        request = request.json(&patch_issue_form);
74
75        let response = request.send().await?;
76        
77        if response.status().is_success() {
78            let json: Value = response.json().await?;
79            Ok(json)
80        } else {
81            Err(ApiError::HttpError(response.status().as_u16()))
82        }
83    }
84
85    /// 删除 Issue 标签。Remove a label from an issue.
86    pub async fn delete_repo_issues_number_labels_name(
87        &self,
88        repo: String,
89        number: i64,
90        name: String,
91    ) -> Result<Value> {
92        let path = format!("/{}/-/issues/{}/labels/{}", repo, number, name);
93        let url = Url::parse(&format!("{}{}", self.base_url, path))?;
94        
95
96                let request = self.client.request(
97            reqwest::Method::DELETE,
98            url
99        );
100        
101
102
103
104        let response = request.send().await?;
105        
106        if response.status().is_success() {
107            let json: Value = response.json().await?;
108            Ok(json)
109        } else {
110            Err(ApiError::HttpError(response.status().as_u16()))
111        }
112    }
113
114    /// 查询指定 Issue 的 Assignees。 List repository issue assignees.
115    pub async fn get_repo_issues_number_assignees(
116        &self,
117        repo: String,
118        number: String,
119    ) -> Result<Value> {
120        let path = format!("/{}/-/issues/{}/assignees", repo, number);
121        let url = Url::parse(&format!("{}{}", self.base_url, path))?;
122        
123
124                let request = self.client.request(
125            reqwest::Method::GET,
126            url
127        );
128        
129
130
131
132        let response = request.send().await?;
133        
134        if response.status().is_success() {
135            let json: Value = response.json().await?;
136            Ok(json)
137        } else {
138            Err(ApiError::HttpError(response.status().as_u16()))
139        }
140    }
141
142    /// 添加 Assignees 到指定的 Issue。 Adds up to 10 assignees to an issue. Users already assigned to an issue are not replaced.
143    pub async fn post_repo_issues_number_assignees(
144        &self,
145        repo: String,
146        number: String,
147        post_issue_assignees_form: serde_json::Value,
148    ) -> Result<Value> {
149        let path = format!("/{}/-/issues/{}/assignees", repo, number);
150        let url = Url::parse(&format!("{}{}", self.base_url, path))?;
151        
152
153        
154        let mut request = self.client.request(
155            reqwest::Method::POST,
156            url
157        );
158
159
160
161        request = request.json(&post_issue_assignees_form);
162
163        let response = request.send().await?;
164        
165        if response.status().is_success() {
166            let json: Value = response.json().await?;
167            Ok(json)
168        } else {
169            Err(ApiError::HttpError(response.status().as_u16()))
170        }
171    }
172
173    /// 删除 Issue 中的 Assignees。 Removes one or more assignees from an issue.
174    pub async fn delete_repo_issues_number_assignees(
175        &self,
176        repo: String,
177        number: String,
178        delete_issue_assignees_form: serde_json::Value,
179    ) -> Result<Value> {
180        let path = format!("/{}/-/issues/{}/assignees", repo, number);
181        let url = Url::parse(&format!("{}{}", self.base_url, path))?;
182        
183
184        
185        let mut request = self.client.request(
186            reqwest::Method::DELETE,
187            url
188        );
189
190
191
192        request = request.json(&delete_issue_assignees_form);
193
194        let response = request.send().await?;
195        
196        if response.status().is_success() {
197            let json: Value = response.json().await?;
198            Ok(json)
199        } else {
200            Err(ApiError::HttpError(response.status().as_u16()))
201        }
202    }
203
204    /// 更新 Issue 中的 Assignees。 Updates the assignees of an issue.
205    pub async fn patch_repo_issues_number_assignees(
206        &self,
207        repo: String,
208        number: String,
209        patch_issue_assignees_form: serde_json::Value,
210    ) -> Result<Value> {
211        let path = format!("/{}/-/issues/{}/assignees", repo, number);
212        let url = Url::parse(&format!("{}{}", self.base_url, path))?;
213        
214
215        
216        let mut request = self.client.request(
217            reqwest::Method::PATCH,
218            url
219        );
220
221
222
223        request = request.json(&patch_issue_assignees_form);
224
225        let response = request.send().await?;
226        
227        if response.status().is_success() {
228            let json: Value = response.json().await?;
229            Ok(json)
230        } else {
231            Err(ApiError::HttpError(response.status().as_u16()))
232        }
233    }
234
235    /// 查询仓库的 Issue 评论列表。List repository issue comments.
236    pub async fn get_repo_issues_number_comments(
237        &self,
238        repo: String,
239        number: i64,
240        page: Option<i64>,
241        page_size: Option<i64>,
242    ) -> Result<Value> {
243        let path = format!("/{}/-/issues/{}/comments", repo, number);
244        let mut url = Url::parse(&format!("{}{}", self.base_url, path))?;
245        
246        if let Some(value) = page {
247            url.query_pairs_mut().append_pair("page", &value.to_string());
248        }
249        if let Some(value) = page_size {
250            url.query_pairs_mut().append_pair("page_size", &value.to_string());
251        }
252
253                let request = self.client.request(
254            reqwest::Method::GET,
255            url
256        );
257        
258
259
260
261        let response = request.send().await?;
262        
263        if response.status().is_success() {
264            let json: Value = response.json().await?;
265            Ok(json)
266        } else {
267            Err(ApiError::HttpError(response.status().as_u16()))
268        }
269    }
270
271    /// 创建一个 Issue Comment。Create an issue comment.
272    pub async fn post_repo_issues_number_comments(
273        &self,
274        repo: String,
275        number: i64,
276        post_issue_comment_form: serde_json::Value,
277    ) -> Result<Value> {
278        let path = format!("/{}/-/issues/{}/comments", repo, number);
279        let url = Url::parse(&format!("{}{}", self.base_url, path))?;
280        
281
282        
283        let mut request = self.client.request(
284            reqwest::Method::POST,
285            url
286        );
287
288
289
290        request = request.json(&post_issue_comment_form);
291
292        let response = request.send().await?;
293        
294        if response.status().is_success() {
295            let json: Value = response.json().await?;
296            Ok(json)
297        } else {
298            Err(ApiError::HttpError(response.status().as_u16()))
299        }
300    }
301
302    /// 检查用户是否可以被添加到 Issue 的 Assignees 中。 Checks if a user can be assigned to an issue.
303    pub async fn get_repo_issues_number_assignees_assignee(
304        &self,
305        repo: String,
306        number: String,
307        assignee: String,
308    ) -> Result<Value> {
309        let path = format!("/{}/-/issues/{}/assignees/{}", repo, number, assignee);
310        let url = Url::parse(&format!("{}{}", self.base_url, path))?;
311        
312
313                let request = self.client.request(
314            reqwest::Method::GET,
315            url
316        );
317        
318
319
320
321        let response = request.send().await?;
322        
323        if response.status().is_success() {
324            let json: Value = response.json().await?;
325            Ok(json)
326        } else {
327            Err(ApiError::HttpError(response.status().as_u16()))
328        }
329    }
330
331    /// 查询仓库的 Issues。List issues.
332    pub async fn get_repo_issues(
333        &self,
334        repo: String,
335        page: Option<i64>,
336        page_size: Option<i64>,
337        state: Option<String>,
338        keyword: Option<String>,
339        priority: Option<String>,
340        labels: Option<String>,
341        authors: Option<String>,
342        assignees: Option<String>,
343        updated_time_begin: Option<String>,
344        updated_time_end: Option<String>,
345        close_time_begin: Option<String>,
346        close_time_end: Option<String>,
347        order_by: Option<String>,
348    ) -> Result<Value> {
349        let path = format!("/{}/-/issues", repo);
350        let mut url = Url::parse(&format!("{}{}", self.base_url, path))?;
351        
352        if let Some(value) = page {
353            url.query_pairs_mut().append_pair("page", &value.to_string());
354        }
355        if let Some(value) = page_size {
356            url.query_pairs_mut().append_pair("page_size", &value.to_string());
357        }
358        if let Some(value) = state {
359            url.query_pairs_mut().append_pair("state", &value.to_string());
360        }
361        if let Some(value) = keyword {
362            url.query_pairs_mut().append_pair("keyword", &value.to_string());
363        }
364        if let Some(value) = priority {
365            url.query_pairs_mut().append_pair("priority", &value.to_string());
366        }
367        if let Some(value) = labels {
368            url.query_pairs_mut().append_pair("labels", &value.to_string());
369        }
370        if let Some(value) = authors {
371            url.query_pairs_mut().append_pair("authors", &value.to_string());
372        }
373        if let Some(value) = assignees {
374            url.query_pairs_mut().append_pair("assignees", &value.to_string());
375        }
376        if let Some(value) = updated_time_begin {
377            url.query_pairs_mut().append_pair("updated_time_begin", &value.to_string());
378        }
379        if let Some(value) = updated_time_end {
380            url.query_pairs_mut().append_pair("updated_time_end", &value.to_string());
381        }
382        if let Some(value) = close_time_begin {
383            url.query_pairs_mut().append_pair("close_time_begin", &value.to_string());
384        }
385        if let Some(value) = close_time_end {
386            url.query_pairs_mut().append_pair("close_time_end", &value.to_string());
387        }
388        if let Some(value) = order_by {
389            url.query_pairs_mut().append_pair("order_by", &value.to_string());
390        }
391
392                let request = self.client.request(
393            reqwest::Method::GET,
394            url
395        );
396        
397
398
399
400        let response = request.send().await?;
401        
402        if response.status().is_success() {
403            let json: Value = response.json().await?;
404            Ok(json)
405        } else {
406            Err(ApiError::HttpError(response.status().as_u16()))
407        }
408    }
409
410    /// 创建一个 Issue。Create an issue.
411    pub async fn post_repo_issues(
412        &self,
413        repo: String,
414        post_issue_form: serde_json::Value,
415    ) -> Result<Value> {
416        let path = format!("/{}/-/issues", repo);
417        let url = Url::parse(&format!("{}{}", self.base_url, path))?;
418        
419
420        
421        let mut request = self.client.request(
422            reqwest::Method::POST,
423            url
424        );
425
426
427
428        request = request.json(&post_issue_form);
429
430        let response = request.send().await?;
431        
432        if response.status().is_success() {
433            let json: Value = response.json().await?;
434            Ok(json)
435        } else {
436            Err(ApiError::HttpError(response.status().as_u16()))
437        }
438    }
439
440    /// 查询 Issue 的标签(label) 列表。List labels for an issue.
441    pub async fn get_repo_issues_number_labels(
442        &self,
443        repo: String,
444        number: i64,
445        page: Option<i64>,
446        page_size: Option<i64>,
447    ) -> Result<Value> {
448        let path = format!("/{}/-/issues/{}/labels", repo, number);
449        let mut url = Url::parse(&format!("{}{}", self.base_url, path))?;
450        
451        if let Some(value) = page {
452            url.query_pairs_mut().append_pair("page", &value.to_string());
453        }
454        if let Some(value) = page_size {
455            url.query_pairs_mut().append_pair("page_size", &value.to_string());
456        }
457
458                let request = self.client.request(
459            reqwest::Method::GET,
460            url
461        );
462        
463
464
465
466        let response = request.send().await?;
467        
468        if response.status().is_success() {
469            let json: Value = response.json().await?;
470            Ok(json)
471        } else {
472            Err(ApiError::HttpError(response.status().as_u16()))
473        }
474    }
475
476    /// 新增 Issue 标签。Add labels to an issue.
477    pub async fn post_repo_issues_number_labels(
478        &self,
479        repo: String,
480        number: i64,
481        post_issue_labels_form: serde_json::Value,
482    ) -> Result<Value> {
483        let path = format!("/{}/-/issues/{}/labels", repo, number);
484        let url = Url::parse(&format!("{}{}", self.base_url, path))?;
485        
486
487        
488        let mut request = self.client.request(
489            reqwest::Method::POST,
490            url
491        );
492
493
494
495        request = request.json(&post_issue_labels_form);
496
497        let response = request.send().await?;
498        
499        if response.status().is_success() {
500            let json: Value = response.json().await?;
501            Ok(json)
502        } else {
503            Err(ApiError::HttpError(response.status().as_u16()))
504        }
505    }
506
507    /// 设置 Issue 标签。 Set the new labels for an issue.
508    pub async fn put_repo_issues_number_labels(
509        &self,
510        repo: String,
511        number: i64,
512        put_issue_labels_form: serde_json::Value,
513    ) -> Result<Value> {
514        let path = format!("/{}/-/issues/{}/labels", repo, number);
515        let url = Url::parse(&format!("{}{}", self.base_url, path))?;
516        
517
518        
519        let mut request = self.client.request(
520            reqwest::Method::PUT,
521            url
522        );
523
524
525
526        request = request.json(&put_issue_labels_form);
527
528        let response = request.send().await?;
529        
530        if response.status().is_success() {
531            let json: Value = response.json().await?;
532            Ok(json)
533        } else {
534            Err(ApiError::HttpError(response.status().as_u16()))
535        }
536    }
537
538    /// 清空 Issue 标签。Remove all labels from an issue.
539    pub async fn delete_repo_issues_number_labels(
540        &self,
541        repo: String,
542        number: i64,
543    ) -> Result<Value> {
544        let path = format!("/{}/-/issues/{}/labels", repo, number);
545        let url = Url::parse(&format!("{}{}", self.base_url, path))?;
546        
547
548                let request = self.client.request(
549            reqwest::Method::DELETE,
550            url
551        );
552        
553
554
555
556        let response = request.send().await?;
557        
558        if response.status().is_success() {
559            let json: Value = response.json().await?;
560            Ok(json)
561        } else {
562            Err(ApiError::HttpError(response.status().as_u16()))
563        }
564    }
565
566    /// 获取一个 Issue Comment。Get an issue comment.
567    pub async fn get_repo_issues_number_comments_comment_id(
568        &self,
569        repo: String,
570        number: String,
571        comment_id: String,
572    ) -> Result<Value> {
573        let path = format!("/{}/-/issues/{}/comments/{}", repo, number, comment_id);
574        let url = Url::parse(&format!("{}{}", self.base_url, path))?;
575        
576
577                let request = self.client.request(
578            reqwest::Method::GET,
579            url
580        );
581        
582
583
584
585        let response = request.send().await?;
586        
587        if response.status().is_success() {
588            let json: Value = response.json().await?;
589            Ok(json)
590        } else {
591            Err(ApiError::HttpError(response.status().as_u16()))
592        }
593    }
594
595    /// 修改一个 Issue Comment。Update an issue comment.
596    pub async fn patch_repo_issues_number_comments_comment_id(
597        &self,
598        repo: String,
599        number: i64,
600        comment_id: i64,
601        patch_issue_comment_form: serde_json::Value,
602    ) -> Result<Value> {
603        let path = format!("/{}/-/issues/{}/comments/{}", repo, number, comment_id);
604        let url = Url::parse(&format!("{}{}", self.base_url, path))?;
605        
606
607        
608        let mut request = self.client.request(
609            reqwest::Method::PATCH,
610            url
611        );
612
613
614
615        request = request.json(&patch_issue_comment_form);
616
617        let response = request.send().await?;
618        
619        if response.status().is_success() {
620            let json: Value = response.json().await?;
621            Ok(json)
622        } else {
623            Err(ApiError::HttpError(response.status().as_u16()))
624        }
625    }
626
627}