Skip to main content

ephemeris_forge_forgejo/
release.rs

1//! Forgejo release and pull request facade for the release CLI.
2
3use ephemeris_forge::RepoRef;
4use ephemeris_release::{
5    ForgeProposal, ForgeRelease as ReleaseForge, ForgeTags, RemoteAsset, RemoteRelease,
6};
7use reqwest::Method;
8
9use crate::api::{ForgeError, HttpApi};
10
11/// Forgejo release and pull request facade for the release CLI.
12#[derive(Debug)]
13pub struct Release {
14    api: HttpApi,
15    repository: RepoRef,
16}
17
18impl Release {
19    /// Bind an authenticated API and repository.
20    #[must_use]
21    pub const fn new(api: HttpApi, repository: RepoRef) -> Self {
22        Self { api, repository }
23    }
24}
25
26impl ForgeProposal for Release {
27    type Error = ForgeError;
28
29    async fn proposal_body(&self, head: &str, _base: &str) -> Result<Option<String>, Self::Error> {
30        Ok(self
31            .api
32            .open_pull_request(&self.repository, head)
33            .await?
34            .and_then(|pull| pull.body))
35    }
36
37    async fn ensure_proposal(
38        &self,
39        head: &str,
40        base: &str,
41        title: &str,
42        body: &str,
43    ) -> Result<(), Self::Error> {
44        self.api
45            .ensure_pull_request(&self.repository, head, base, title, body)
46            .await
47    }
48}
49
50impl ForgeTags for Release {
51    type Error = ForgeError;
52
53    async fn tag_commit(&self, tag: &str) -> Result<Option<String>, Self::Error> {
54        self.api.tag_commit(&self.repository, tag).await
55    }
56}
57
58impl ReleaseForge for Release {
59    type Error = ForgeError;
60
61    async fn delete_asset(&self, release_id: u64, asset_id: u64) -> Result<(), Self::Error> {
62        self.api
63            .delete_asset(&self.repository, release_id, asset_id)
64            .await
65    }
66
67    async fn release_by_tag(&self, tag: &str) -> Result<Option<RemoteRelease>, Self::Error> {
68        let release = self.api.release_by_tag(&self.repository, tag).await?;
69        Ok(release.map(|release| RemoteRelease {
70            id: release.id,
71            draft: release.draft,
72        }))
73    }
74
75    async fn create_draft(
76        &self,
77        tag: &str,
78        target_commit: &str,
79        title: &str,
80        notes: &str,
81    ) -> Result<u64, Self::Error> {
82        self.api
83            .create_draft_release(&self.repository, tag, target_commit, title, notes)
84            .await
85    }
86
87    async fn assets(&self, release_id: u64) -> Result<Vec<RemoteAsset>, Self::Error> {
88        Ok(self
89            .api
90            .release_assets(&self.repository, release_id)
91            .await?
92            .into_iter()
93            .map(|asset| RemoteAsset {
94                id: asset.id,
95                name: asset.name,
96                size: asset.size,
97                sha256: asset.digest,
98                download_url: asset.browser_download_url,
99            })
100            .collect())
101    }
102
103    async fn upload(
104        &self,
105        release_id: u64,
106        filename: &str,
107        bytes: &[u8],
108    ) -> Result<(), Self::Error> {
109        self.api
110            .upload_asset(&self.repository, release_id, filename, bytes.to_vec())
111            .await
112    }
113
114    async fn asset_bytes(&self, asset: &RemoteAsset) -> Result<Vec<u8>, Self::Error> {
115        Ok(self
116            .api
117            .http
118            .bytes(Method::GET, &asset.download_url, None)
119            .await?)
120    }
121
122    async fn publish(&self, release_id: u64) -> Result<(), Self::Error> {
123        self.api.publish_release(&self.repository, release_id).await
124    }
125}