Skip to main content

ephemeris_forge_forgejo/api/
http.rs

1mod access;
2mod actions_variables;
3mod branch_rules;
4mod deploy_keys;
5mod labels;
6mod release;
7mod repository;
8mod tag_protection;
9mod webhooks;
10
11use std::time::Duration;
12
13use ephemeris_forge::{Http, RetryPolicy, TransportBuildError, TransportError};
14use ephemeris_token::Token;
15use facet::Facet;
16use reqwest::{Method, Url};
17
18use crate::api::error::ForgeError;
19
20const PAGE_SIZE: usize = 50;
21
22/// Forgejo's HTTP implementation of [`Api`](super::Api).
23///
24/// `Debug` is derived, which stays safe because the transport's token redacts
25/// itself.
26#[derive(Clone, Debug)]
27pub struct HttpApi {
28    pub(crate) http: Http,
29}
30
31impl HttpApi {
32    /// Build an adapter for the Forgejo instance at `forge_url`.
33    ///
34    /// # Errors
35    ///
36    /// Fails when the HTTP stack cannot be built.
37    pub fn new(forge_url: &Url, token: Token) -> Result<Self, TransportBuildError> {
38        let mut base = forge_url.clone();
39        let trimmed = base.path().trim_end_matches('/').to_owned();
40        base.set_path(&format!("{trimmed}/api/v1/"));
41        let http = Http::new(base, token, Duration::from_secs(30))?;
42        Ok(Self { http })
43    }
44
45    /// Replace the retry policy, which keeps tests from sleeping.
46    #[must_use]
47    pub fn with_retry(mut self, retry: RetryPolicy) -> Self {
48        self.http = self.http.with_retry(retry);
49        self
50    }
51
52    /// Send a write whose only Forgejo-specific status meaning is 423
53    /// (archived repository).
54    async fn send_write(
55        &self,
56        method: Method,
57        path: &str,
58        body: Option<&str>,
59    ) -> Result<(), ForgeError> {
60        match self.http.send(method, path, body).await {
61            Ok(_) => Ok(()),
62            Err(TransportError::Unexpected {
63                status: 423,
64                request: _,
65                body: _,
66            }) => Err(ForgeError::Archived),
67            Err(error) => Err(error.into()),
68        }
69    }
70
71    async fn paged<T: for<'a> Facet<'a>>(&self, path: &str) -> Result<Vec<T>, ForgeError> {
72        Ok(self
73            .http
74            .paged(PAGE_SIZE, path, |page| {
75                format!("page={page}&limit={PAGE_SIZE}")
76            })
77            .await?)
78    }
79}