Skip to main content

ephemeris_release/
traits.rs

1//! Interfaces to Git, package registries, and the forge.
2
3use std::error::Error as StdError;
4use std::path::Path;
5
6use reqwest::Url;
7
8use crate::Author;
9use crate::remote::{RemoteAsset, RemoteRelease};
10use crate::unit::Unit;
11
12/// Repository operations used by the release lifecycle.
13pub trait GitCommands {
14    /// The failure of a tag or authority operation.
15    type Error: StdError + Send + Sync + 'static;
16
17    /// Commit `branch` points at now.
18    fn authoritative_head(&self, repository: &Path, branch: &str) -> Result<String, Self::Error>;
19    /// Create an annotated tag on `commit` locally, tagged by `author`.
20    fn create_tag(
21        &self,
22        repository: &Path,
23        tag: &str,
24        commit: &str,
25        author: Author<'_>,
26    ) -> Result<(), Self::Error>;
27    /// Push a local tag to `origin`.
28    fn push_tag(&self, repository: &Path, tag: &str) -> Result<(), Self::Error>;
29}
30
31/// Reference lookup.
32pub trait GitRefs {
33    /// The failure of a reference lookup.
34    type Error: StdError + Send + Sync + 'static;
35
36    /// The commit a full reference name points at, or `None` when the
37    /// reference does not exist.
38    fn reference(&self, repository: &Path, name: &str) -> Result<Option<String>, Self::Error>;
39}
40
41/// Ecosystem package publication operations that are safe to retry.
42pub(crate) trait PackagePublisher {
43    /// The failure of a package lookup or upload.
44    type Error: StdError + Send + Sync + 'static;
45
46    /// Whether the unit's version already exists where it is published.
47    async fn is_published(&self, unit: &Unit) -> Result<bool, Self::Error>;
48
49    /// Publish one unit's staged package file.
50    async fn publish(&self, unit: &Unit, file: &Path) -> Result<(), Self::Error>;
51}
52
53/// Remote services whose existence checks make retries safe.
54pub trait PackageRegistry {
55    /// The failure of a registry lookup.
56    type Error: StdError + Send + Sync + 'static;
57
58    /// The registry base URL with a trailing slash, when the registry has
59    /// one that packages can be published to.
60    fn base_url(&self) -> Option<&Url> {
61        None
62    }
63
64    /// Whether `version` of `package` is already in the registry.
65    async fn package_exists(&self, package: &str, version: &str) -> Result<bool, Self::Error>;
66}
67
68impl<T: PackageRegistry + ?Sized> PackageRegistry for &T {
69    type Error = T::Error;
70
71    async fn package_exists(&self, package: &str, version: &str) -> Result<bool, Self::Error> {
72        (**self).package_exists(package, version).await
73    }
74
75    fn base_url(&self) -> Option<&Url> {
76        (**self).base_url()
77    }
78}
79
80/// Tag lookup on the forge.
81pub trait ForgeTags {
82    /// The failure of a forge tag lookup.
83    type Error: StdError + Send + Sync + 'static;
84
85    /// The commit `tag` points at on the forge, with annotated tags peeled,
86    /// or `None` when the forge has no such tag.
87    async fn tag_commit(&self, tag: &str) -> Result<Option<String>, Self::Error>;
88}
89
90/// Forge-hosted releases and release assets.
91pub trait ForgeRelease {
92    /// The failure of a forge release call.
93    type Error: StdError + Send + Sync + 'static;
94
95    /// Download an attached asset for verification.
96    async fn asset_bytes(&self, asset: &RemoteAsset) -> Result<Vec<u8>, Self::Error>;
97    /// Assets already attached to the release.
98    async fn assets(&self, release_id: u64) -> Result<Vec<RemoteAsset>, Self::Error>;
99    /// Create a hidden release on `tag` and return its identifier.
100    async fn create_draft(
101        &self,
102        tag: &str,
103        target_commit: &str,
104        title: &str,
105        notes: &str,
106    ) -> Result<u64, Self::Error>;
107    /// Remove one asset from a draft release.
108    async fn delete_asset(&self, release_id: u64, asset_id: u64) -> Result<(), Self::Error>;
109    /// Make the draft visible.
110    async fn publish(&self, release_id: u64) -> Result<(), Self::Error>;
111    /// The release for `tag`, draft or published, if one exists.
112    async fn release_by_tag(&self, tag: &str) -> Result<Option<RemoteRelease>, Self::Error>;
113    /// Attach one file to the release.
114    async fn upload(
115        &self,
116        release_id: u64,
117        filename: &str,
118        bytes: &[u8],
119    ) -> Result<(), Self::Error>;
120}
121
122/// The release proposal pull request on the forge.
123pub trait ForgeProposal {
124    /// The failure of a forge pull request call.
125    type Error: StdError + Send + Sync + 'static;
126
127    /// Create the pull request from `head` into `base`, or update the open
128    /// one.
129    async fn ensure_proposal(
130        &self,
131        head: &str,
132        base: &str,
133        title: &str,
134        body: &str,
135    ) -> Result<(), Self::Error>;
136
137    /// Description of the open pull request from `head` into `base`, when one
138    /// exists.
139    async fn proposal_body(&self, head: &str, base: &str) -> Result<Option<String>, Self::Error>;
140}