cargolifter_backend_github/
lib.rs1mod api;
2mod models;
3
4use async_trait::async_trait;
5use cargolifter_core::models::PublishedVersion;
6use cargolifter_core::Backend;
7
8pub struct Github {
9 cargoliter_token: Option<String>,
10 project_id: String,
11 host: Option<String>,
12 default_branch: String,
13}
14
15impl Github {
16 pub fn from(config: cargolifter_core::config::GithubConfig) -> Self {
17 Self {
18 cargoliter_token: config.cargolifter_token,
19 project_id: [config.owner, config.repo].join("/"),
20 host: config.host.clone(),
21 default_branch: config
22 .default_branch
23 .unwrap_or_else(|| String::from("main")),
24 }
25 }
26
27 fn host(&self) -> String {
28 let default_host = String::from("https://api.github.com");
29 self.host.as_ref().unwrap_or(&default_host).into()
30 }
31
32 fn config(&self, token: &str) -> (String, String, String) {
33 let credentials = token.split(':').collect::<Vec<_>>();
34 (credentials[0].into(), credentials[1].into(), self.host())
35 }
36
37 fn merge_config(&self, token: &str) -> (String, String, String) {
38 let owned_token = token.to_owned();
39 let merge_credentials = self
40 .cargoliter_token
41 .as_ref()
42 .unwrap_or(&owned_token)
43 .split(':')
44 .collect::<Vec<_>>();
45 (merge_credentials[0].into(), merge_credentials[1].into(), self.host())
46 }
47}
48
49#[async_trait]
50impl Backend for Github {
51 async fn get_file(
52 &self,
53 token: &str,
54 crate_path: &str,
55 ) -> Result<(String, String, String), reqwest::Error> {
56 let (username, password, host) = self.config(token);
57
58 match api::get_file(
59 &host,
60 &username,
61 &password,
62 &self.project_id,
63 &crate_path,
64 &self.default_branch,
65 )
66 .await
67 {
68 Ok(response) => Ok((
69 response.content,
70 response.encoding,
71 response.sha,
72 )),
73 Err(e) => Err(e),
74 }
75 }
76
77 async fn create_file(
78 &self,
79 token: &str,
80 crate_path: &str,
81 branch_name: &str,
82 initial_version: &PublishedVersion,
83 ) -> Result<(), reqwest::Error> {
84 let (username, token, host) = self.config(token);
85
86 let main_branch = api::get_branch(
87 &host,
88 &username,
89 &token,
90 &self.project_id,
91 &self.default_branch,
92 )
93 .await?;
94 api::create_branch(
95 &host,
96 &username,
97 &token,
98 &self.project_id,
99 crate::models::create_branch::Request {
100 r#ref: format!("refs/heads/{}", branch_name),
101 sha: main_branch.commit.sha,
102 },
103 )
104 .await?;
105
106 let json = serde_json::to_string(&initial_version).unwrap();
107 let encoded_content = base64::encode(json);
108 let create_request = crate::models::update_file::Request {
109 branch: Some(branch_name.into()),
110 content: encoded_content,
111 message: format!("Adding {} {}", initial_version.name, initial_version.vers),
112 ..Default::default()
113 };
114
115 match api::update_file(
116 &host,
117 &username,
118 &token,
119 &self.project_id,
120 &crate_path,
121 &create_request,
122 )
123 .await
124 {
125 Ok(_) => Ok(()),
126 Err(e) => Err(e),
127 }
128 }
129
130 async fn update_file(
131 &self,
132 token: &str,
133 crate_path: &str,
134 branch_name: &str,
135 versions: &[PublishedVersion],
136 current_sha: &str,
137 ) -> Result<(), reqwest::Error> {
138 let (username, token, host) = self.config(token);
139
140 let new_content = versions
141 .iter()
142 .map(|v| serde_json::to_string(v).unwrap())
143 .collect::<Vec<String>>()
144 .join("\n");
145
146 let update_request = crate::models::update_file::Request {
147 branch: Some(branch_name.into()),
148 content: base64::encode(new_content),
149 message: format!("Adding {} {}", versions[0].name, versions[0].vers),
150 sha: Some(current_sha.into()),
151 ..Default::default()
152 };
153
154 let main_branch = api::get_branch(
155 &host,
156 &username,
157 &token,
158 &self.project_id,
159 &self.default_branch,
160 )
161 .await?;
162 api::create_branch(
163 &host,
164 &username,
165 &token,
166 &self.project_id,
167 crate::models::create_branch::Request {
168 r#ref: format!("refs/heads/{}", branch_name),
169 sha: main_branch.commit.sha,
170 },
171 )
172 .await?;
173
174 match api::update_file(
175 &host,
176 &username,
177 &token,
178 &self.project_id,
179 &crate_path,
180 &update_request,
181 )
182 .await
183 {
184 Ok(_) => Ok(()),
185 Err(e) => Err(e),
186 }
187 }
188
189 async fn delete_branch(
190 &self,
191 token: &str,
192 branch_name: &str,
193 ) -> Result<(), reqwest::Error>
194 {
195 let (username, token, host) = self.config(token);
196
197 match api::delete_branch(
198 &host,
199 &username,
200 &token,
201 &self.project_id,
202 &branch_name,
203 ).await {
204 Ok(_) => Ok(()),
205 Err(e) => Err(e),
206 }
207 }
208
209 async fn create_pull_request(
210 &self,
211 token: &str,
212 title: &str,
213 branch_name: &str,
214 ) -> Result<u64, reqwest::Error> {
215 let (username, token, host) = self.config(token);
216
217 let pull_request = models::create_pull_request::Request {
218 title: title.into(),
219 head: branch_name.into(),
220 base: self.default_branch.clone(),
221 ..Default::default()
222 };
223
224 match api::create_pull_request(
225 &host,
226 &username,
227 &token,
228 &self.project_id,
229 pull_request,
230 )
231 .await {
232 Ok(response) => Ok(response.number),
233 Err(e) => Err(e),
234 }
235 }
236
237 async fn merge_pull_request(
238 &self,
239 token: &str,
240 id: u64,
241 ) -> Result<(), reqwest::Error> {
242 let (username, token, host) = self.merge_config(token);
243
244 let merge_request = crate::models::merge_pull_request::Request::default();
245
246 match api::merge_pull_request(
247 &host,
248 &username,
249 &token,
250 &self.project_id,
251 id,
252 merge_request,
253 ).await {
254 Ok(_) => Ok(()),
255 Err(e) => Err(e),
256 }
257 }
258
259 async fn delete_pull_request(
260 &self,
261 token: &str,
262 id: u64,
263 ) -> Result<(), reqwest::Error> {
264 let (username, token, host) = self.config(token);
265
266 api::close_pull_request(
267 &host,
268 &username,
269 &token,
270 &self.project_id,
271 id,
272 )
273 .await?;
274
275 todo!()
276 }
277}