forgejo-sdk 0.1.0

An SDK for the Forgejo REST API
Documentation
#![forbid(unsafe_code)]

pub mod api;

mod error;
use error::ApiError;
pub use error::{Error, Result};
use reqwest::{
    header::{self, HeaderMap},
    Method, RequestBuilder, Response, StatusCode, Url,
};
use serde::de::DeserializeOwned;

pub struct Client {
    http: reqwest::Client,
    token: Option<String>,
    base_url: Url,
}

impl Client {
    pub fn new(token: String, base_url: Url) -> Self {
        let mut headers = HeaderMap::new();
        headers.insert(header::AUTHORIZATION, format!("Bearer {token}").try_into().expect("the token is not a valid header value"));

        Self {
            http: reqwest::Client::builder().default_headers(headers).build().expect("the client was not constructed"),
            token: Some(token),
            base_url,
        }
    }

    pub fn new_unauth(base_url: Url) -> Self {
        Self {
            http: reqwest::Client::new(),
            token: None,
            base_url,
        }
    }

    pub fn token(&self) -> Option<&str> {
        self.token.as_deref()
    }

    pub async fn execute(&self, request: RequestBuilder) -> Result<Response> {
        let response = request.send().await?;
        match response.status() {
            StatusCode::UNAUTHORIZED => Err(Error::Api(ApiError::Unauthorized)),
            StatusCode::NOT_FOUND => Err(Error::Api(ApiError::NotFound)),
            StatusCode::FORBIDDEN => Err(Error::Api(ApiError::Forbidden(response.json().await?))),
            _ => Ok(response),
        }
    }

    pub async fn get<T: DeserializeOwned>(&self, url: &str) -> Result<T> {
        Ok(self
            .execute(
                self.http.request(
                    Method::GET,
                    Url::options()
                        .base_url(Some(&self.base_url))
                        .parse(url)
                        .expect("invalid url"),
                ),
            )
            .await?
            .json()
            .await?)
    }

    pub async fn delete<T: DeserializeOwned>(&self, url: &str) -> Result<T> {
        Ok(self
            .execute(
                self.http.request(
                    Method::DELETE,
                    Url::options()
                        .base_url(Some(&self.base_url))
                        .parse(url)
                        .expect("invalid url"),
                ),
            )
            .await?
            .json()
            .await?)
    }
}