1use anyhow::Result;
2
3use super::BitbucketClient;
4use crate::models::{
5 CreateIssueCommentRequest, CreateIssueRequest, Issue, IssueComment, IssueState, Paginated,
6};
7
8impl BitbucketClient {
9 pub async fn list_issues(
11 &self,
12 workspace: &str,
13 repo_slug: &str,
14 state: Option<IssueState>,
15 page: Option<u32>,
16 pagelen: Option<u32>,
17 ) -> Result<Paginated<Issue>> {
18 let mut query = Vec::new();
19
20 if let Some(s) = state {
21 query.push(("state", s.to_string()));
22 }
23 if let Some(p) = page {
24 query.push(("page", p.to_string()));
25 }
26 if let Some(len) = pagelen {
27 query.push(("pagelen", len.to_string()));
28 }
29
30 let query_refs: Vec<(&str, &str)> = query.iter().map(|(k, v)| (*k, v.as_str())).collect();
31
32 let path = format!("/repositories/{}/{}/issues", workspace, repo_slug);
33 self.get_with_query(&path, &query_refs).await
34 }
35
36 pub async fn get_issue(
38 &self,
39 workspace: &str,
40 repo_slug: &str,
41 issue_id: u64,
42 ) -> Result<Issue> {
43 let path = format!(
44 "/repositories/{}/{}/issues/{}",
45 workspace, repo_slug, issue_id
46 );
47 self.get(&path).await
48 }
49
50 pub async fn create_issue(
52 &self,
53 workspace: &str,
54 repo_slug: &str,
55 request: &CreateIssueRequest,
56 ) -> Result<Issue> {
57 let path = format!("/repositories/{}/{}/issues", workspace, repo_slug);
58 self.post(&path, request).await
59 }
60
61 pub async fn update_issue(
63 &self,
64 workspace: &str,
65 repo_slug: &str,
66 issue_id: u64,
67 title: Option<&str>,
68 content: Option<&str>,
69 state: Option<IssueState>,
70 ) -> Result<Issue> {
71 #[derive(serde::Serialize)]
72 struct UpdateRequest {
73 #[serde(skip_serializing_if = "Option::is_none")]
74 title: Option<String>,
75 #[serde(skip_serializing_if = "Option::is_none")]
76 content: Option<ContentRequest>,
77 #[serde(skip_serializing_if = "Option::is_none")]
78 state: Option<IssueState>,
79 }
80
81 #[derive(serde::Serialize)]
82 struct ContentRequest {
83 raw: String,
84 }
85
86 let request = UpdateRequest {
87 title: title.map(|t| t.to_string()),
88 content: content.map(|c| ContentRequest { raw: c.to_string() }),
89 state,
90 };
91
92 let path = format!(
93 "/repositories/{}/{}/issues/{}",
94 workspace, repo_slug, issue_id
95 );
96 self.put(&path, &request).await
97 }
98
99 pub async fn delete_issue(
101 &self,
102 workspace: &str,
103 repo_slug: &str,
104 issue_id: u64,
105 ) -> Result<()> {
106 let path = format!(
107 "/repositories/{}/{}/issues/{}",
108 workspace, repo_slug, issue_id
109 );
110 self.delete(&path).await
111 }
112
113 pub async fn list_issue_comments(
115 &self,
116 workspace: &str,
117 repo_slug: &str,
118 issue_id: u64,
119 ) -> Result<Paginated<IssueComment>> {
120 let path = format!(
121 "/repositories/{}/{}/issues/{}/comments",
122 workspace, repo_slug, issue_id
123 );
124 self.get(&path).await
125 }
126
127 pub async fn add_issue_comment(
129 &self,
130 workspace: &str,
131 repo_slug: &str,
132 issue_id: u64,
133 content: &str,
134 ) -> Result<IssueComment> {
135 let request = CreateIssueCommentRequest {
136 content: crate::models::IssueContentRequest {
137 raw: content.to_string(),
138 },
139 };
140
141 let path = format!(
142 "/repositories/{}/{}/issues/{}/comments",
143 workspace, repo_slug, issue_id
144 );
145 self.post(&path, &request).await
146 }
147
148 pub async fn vote_issue(&self, workspace: &str, repo_slug: &str, issue_id: u64) -> Result<()> {
150 let path = format!(
151 "/repositories/{}/{}/issues/{}/vote",
152 workspace, repo_slug, issue_id
153 );
154 self.put::<serde_json::Value, _>(&path, &serde_json::json!({}))
155 .await?;
156 Ok(())
157 }
158
159 pub async fn unvote_issue(
161 &self,
162 workspace: &str,
163 repo_slug: &str,
164 issue_id: u64,
165 ) -> Result<()> {
166 let path = format!(
167 "/repositories/{}/{}/issues/{}/vote",
168 workspace, repo_slug, issue_id
169 );
170 self.delete(&path).await
171 }
172
173 pub async fn watch_issue(&self, workspace: &str, repo_slug: &str, issue_id: u64) -> Result<()> {
175 let path = format!(
176 "/repositories/{}/{}/issues/{}/watch",
177 workspace, repo_slug, issue_id
178 );
179 self.put::<serde_json::Value, _>(&path, &serde_json::json!({}))
180 .await?;
181 Ok(())
182 }
183
184 pub async fn unwatch_issue(
186 &self,
187 workspace: &str,
188 repo_slug: &str,
189 issue_id: u64,
190 ) -> Result<()> {
191 let path = format!(
192 "/repositories/{}/{}/issues/{}/watch",
193 workspace, repo_slug, issue_id
194 );
195 self.delete(&path).await
196 }
197}