miyabi_github/
pull_requests.rs1use crate::client::GitHubClient;
6use miyabi_types::error::{MiyabiError, Result};
7use miyabi_types::issue::{PRResult, PRState};
8use octocrab::models::pulls::PullRequest as OctoPR;
9use octocrab::params::pulls::State as PullState;
10use octocrab::params::State;
11
12impl GitHubClient {
13 pub async fn get_pull_request(&self, number: u64) -> Result<PRResult> {
18 let pr = self
19 .client
20 .pulls(&self.owner, &self.repo)
21 .get(number)
22 .await
23 .map_err(|e| {
24 MiyabiError::GitHub(format!(
25 "Failed to get pull request #{} from {}/{}: {}",
26 number, self.owner, self.repo, e
27 ))
28 })?;
29
30 convert_pull_request(pr)
31 }
32
33 pub async fn list_pull_requests(&self, state: Option<State>) -> Result<Vec<PRResult>> {
38 let pulls = self.client.pulls(&self.owner, &self.repo);
39 let mut handler = pulls.list();
40
41 if let Some(s) = state {
43 handler = handler.state(s);
44 }
45
46 let page = handler.send().await.map_err(|e| {
47 MiyabiError::GitHub(format!(
48 "Failed to list pull requests for {}/{}: {}",
49 self.owner, self.repo, e
50 ))
51 })?;
52
53 page.items.into_iter().map(convert_pull_request).collect()
54 }
55
56 pub async fn create_pull_request(
65 &self,
66 title: &str,
67 head: &str,
68 base: &str,
69 body: Option<&str>,
70 draft: bool,
71 ) -> Result<PRResult> {
72 let pulls = self.client.pulls(&self.owner, &self.repo);
73 let mut handler = pulls.create(title, head, base);
74
75 if let Some(b) = body {
76 handler = handler.body(b);
77 }
78
79 handler = handler.draft(draft);
80
81 let pr = handler.send().await.map_err(|e| {
82 MiyabiError::GitHub(format!(
83 "Failed to create pull request in {}/{}: {}",
84 self.owner, self.repo, e
85 ))
86 })?;
87
88 convert_pull_request(pr)
89 }
90
91 pub async fn update_pull_request(
99 &self,
100 number: u64,
101 title: Option<&str>,
102 body: Option<&str>,
103 state: Option<PullState>,
104 ) -> Result<PRResult> {
105 let pulls = self.client.pulls(&self.owner, &self.repo);
106 let mut handler = pulls.update(number);
107
108 if let Some(t) = title {
109 handler = handler.title(t);
110 }
111
112 if let Some(b) = body {
113 handler = handler.body(b);
114 }
115
116 if let Some(s) = state {
117 handler = handler.state(s);
118 }
119
120 let pr = handler.send().await.map_err(|e| {
121 MiyabiError::GitHub(format!(
122 "Failed to update pull request #{} in {}/{}: {}",
123 number, self.owner, self.repo, e
124 ))
125 })?;
126
127 convert_pull_request(pr)
128 }
129
130 pub async fn close_pull_request(&self, number: u64) -> Result<PRResult> {
132 self.update_pull_request(number, None, None, Some(PullState::Closed))
133 .await
134 }
135
136 pub async fn merge_pull_request(
143 &self,
144 number: u64,
145 commit_title: Option<&str>,
146 commit_message: Option<&str>,
147 ) -> Result<()> {
148 let pulls = self.client.pulls(&self.owner, &self.repo);
149 let mut handler = pulls.merge(number);
150
151 if let Some(title) = commit_title {
152 handler = handler.title(title);
153 }
154
155 if let Some(message) = commit_message {
156 handler = handler.message(message);
157 }
158
159 handler.send().await.map_err(|e| {
160 MiyabiError::GitHub(format!(
161 "Failed to merge pull request #{} in {}/{}: {}",
162 number, self.owner, self.repo, e
163 ))
164 })?;
165
166 Ok(())
167 }
168
169 pub async fn is_mergeable(&self, number: u64) -> Result<bool> {
171 let pr = self.get_pull_request(number).await?;
172
173 Ok(matches!(pr.state, PRState::Open | PRState::Draft))
175 }
176}
177
178fn convert_pull_request(pr: OctoPR) -> Result<PRResult> {
180 use octocrab::models::IssueState as OctoState;
181
182 let state = if pr.merged_at.is_some() {
183 PRState::Merged
184 } else if pr.draft.unwrap_or(false) {
185 PRState::Draft
186 } else {
187 match pr.state {
188 Some(OctoState::Open) => PRState::Open,
189 Some(OctoState::Closed) => PRState::Closed,
190 Some(ref s) => {
191 return Err(MiyabiError::GitHub(format!(
192 "Unknown pull request state: {:?}",
193 s
194 )))
195 }
196 None => {
197 return Err(MiyabiError::GitHub(
198 "Pull request state is missing".to_string(),
199 ))
200 }
201 }
202 };
203
204 Ok(PRResult {
205 number: pr.number,
206 url: pr.html_url.map(|u| u.to_string()).unwrap_or_default(),
207 state,
208 created_at: pr
209 .created_at
210 .ok_or_else(|| MiyabiError::GitHub("Pull request created_at is missing".to_string()))?,
211 })
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217
218 #[test]
219 fn test_pr_state_conversion() {
220 let _draft = PRState::Draft;
223 let _open = PRState::Open;
224 let _merged = PRState::Merged;
225 let _closed = PRState::Closed;
226 }
227
228 }