1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
pub mod model;
pub mod prelude;


#[cfg(feature = "default")]
pub use async_trait::async_trait;
#[cfg(feature = "default")]
use reqwest::{Client as HttpClient, RequestBuilder};
#[cfg(feature = "default")]
use std::sync::Arc;
#[cfg(feature = "default")]
use reqwest::header::HeaderMap;
#[cfg(feature = "default")]
use serde::de::DeserializeOwned;

#[cfg(feature = "blocking")]
pub mod blocking;
#[cfg(feature = "default")]
pub mod images;
#[cfg(feature = "default")]
pub mod bans;
#[cfg(feature = "default")]
pub mod kumo;
#[cfg(feature = "default")]
pub mod music;
#[cfg(feature = "default")]
use crate::{
    images::Images,
    bans::Bans,
    kumo::Kumo,
    music::Music,
    model::bans::BanUpdate
};

//Asynchronous client
#[cfg(feature = "default")]
pub struct Client {
    pub token: String,
    pub images: Images,
    pub bans: Bans,
    pub kumo: Kumo,
    pub music: Music,
    pub http: Arc<HttpClient>
}

#[cfg(feature = "default")]
impl Client {
    pub fn new(token: impl ToString) -> Self {
        let mut default_auth_header =  HeaderMap::new();
        default_auth_header.insert("Authorization", format!("Bearer {}", token.to_string()).parse().expect("Cannot parse default headers"));
        let http_client = Arc::new(HttpClient::builder()
            .default_headers(default_auth_header)
            .user_agent("KSoft.rs")
            .build()
            .expect("Something went wrong when creating http client"));

        Self {
            token: token.to_string(),
            images: Images::new(Arc::clone(&http_client)),
            bans: Bans::new(Arc::clone(&http_client)),
            kumo: Kumo::new(Arc::clone(&http_client)),
            music: Music::new(Arc::clone(&http_client)),
            http: http_client
        }
    }

    pub fn event_handler(&self, handler: impl EventHandler + Send + Sync + 'static ) {
        self.bans.event_handler(handler);
    }
}

#[cfg(feature = "default")]
pub(crate) async fn make_request<S: DeserializeOwned, E: DeserializeOwned>(c: RequestBuilder) -> HttpResult<S, E> {
    let response = c.send().await?;

    return match response.status().as_u16() {
        c if c == 429u16 => Err(HttpError::RateLimited),
        c if c >= 500u16 => Err(HttpError::InternalServerError(response.text().await?)),
        200u16 => {
            let data = response.json::<S>().await?;
            Ok(Ok(data))
        },
        _ => {
            let err = response.json::<E>().await?;
            Ok(Err(err))
        }
    }
}

const BASE_ENDPOINT: &str = "https://api.ksoft.si";

pub(crate) fn endpoint(to: impl AsRef<str>) -> String {
    format!("{}{}", BASE_ENDPOINT, to.as_ref())
}


pub type HttpResult<S, E> = Result<ApiResponse<S, E>, HttpError>;

/// Result renaming used to difference between an http error and an API error or unsuccessful response
pub type ApiResponse<S, E> = Result<S, E>;

#[derive(Debug)]
pub enum HttpError {
    RequestFailed(reqwest::Error),
    InternalServerError(String),
    RateLimited
}

impl From<reqwest::Error> for HttpError {
    fn from(e: reqwest::Error) -> Self {
        HttpError::RequestFailed(e)
    }
}

#[cfg(feature = "default")]
#[async_trait]
pub trait EventHandler: Send + Sync + 'static {
    ///Event triggered every 5 minutes if there is any ban update
    async fn ban_updated(&self, _data: Vec<BanUpdate>) {}
}