rive_http/
lib.rs

1#![doc = include_str!("../README.md")]
2
3mod authentication;
4mod bots;
5mod channels;
6mod customisation;
7mod invites;
8mod miscellaneous;
9mod platform_administration;
10mod revolt;
11mod servers;
12mod users;
13
14use rive_models::{authentication::Authentication, ApiError};
15
16type Result<T> = std::result::Result<T, Error>;
17
18pub(crate) mod prelude {
19    pub(crate) use crate::{ep, Client, RequestBuilderExt, ResponseExt, Result};
20}
21
22/// Base URL of the official Revolt instance API
23pub const BASE_URL: &str = "https://api.revolt.chat";
24
25/// Client error
26#[derive(Debug, thiserror::Error)]
27pub enum Error {
28    /// Data serialization/deserialization error
29    #[error("Serde JSON serialization/deserialization error: {0}")]
30    Serialization(#[from] serde_json::Error),
31
32    /// HTTP error
33    #[error("Error while processing an HTTP request: {0}")]
34    HttpRequest(#[from] reqwest::Error),
35
36    /// An error returned from Revolt API
37    #[error("Error returned from API: {0:#?}")]
38    Api(ApiError),
39}
40
41macro_rules! ep {
42    ($self:ident, $ep:literal, $($args:tt)*) => {
43        format!(concat!("{}", $ep), $self.base_url, $($args)*)
44    };
45
46    ($self:ident, $ep:literal) => {
47        format!(concat!("{}", $ep), $self.base_url)
48    };
49
50    (api_root = $api_root:expr, $ep:literal $($args:tt)*) => {
51        format!(concat!("{}", $ep), $api_root, $($args)*)
52    };
53}
54pub(crate) use ep;
55
56trait RequestBuilderExt {
57    fn auth(self, authentication: &Authentication) -> Self;
58}
59
60impl RequestBuilderExt for reqwest::RequestBuilder {
61    fn auth(self, authentication: &Authentication) -> Self {
62        self.header(authentication.header_key(), authentication.value())
63    }
64}
65
66#[async_trait::async_trait]
67trait ResponseExt {
68    async fn process_error(self) -> Result<Self>
69    where
70        Self: Sized;
71}
72
73#[async_trait::async_trait]
74impl ResponseExt for reqwest::Response {
75    async fn process_error(self) -> Result<Self>
76    where
77        Self: Sized,
78    {
79        match self.status().as_u16() {
80            200..=299 => Ok(self),
81            // NOTE: it's a workaround thing but there are no alternative ways
82            // because API returns some rocket's HTML instead of parseable JSON
83            401 => Err(Error::Api(ApiError::Unauthenticated)),
84            _ => Err(Error::Api(self.json().await?)),
85        }
86    }
87}
88
89#[derive(Debug, Clone)]
90pub struct Client {
91    base_url: String,
92    client: reqwest::Client,
93    authentication: Authentication,
94}
95
96impl Client {
97    /// Create a client instance with the API base URL of Revolt official instance.
98    pub fn new(authentication: Authentication) -> Self {
99        Client::new_base_url(authentication, BASE_URL)
100    }
101
102    /// Create a client instance with given base URL.
103    pub fn new_base_url(authentication: Authentication, base_url: impl Into<String>) -> Self {
104        Client {
105            base_url: base_url.into(),
106            client: reqwest::Client::new(),
107            authentication,
108        }
109    }
110}