cargolifter_backend_github/
lib.rs

1mod 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}