pub mod api;
mod api_request;
pub mod auth;
pub mod errors;
pub mod objects;
pub mod rate_limit;
mod utils;
use std::sync::Arc;
pub use oauth2::{
AccessToken, AuthorizationCode, ClientId, ClientSecret, CsrfToken, RedirectUrl, RefreshToken,
Scope,
};
use reqwest::{Client, ClientBuilder};
use tokio::runtime::{Builder, Runtime};
use crate::{
api::{
account::AccountApi, anime::AnimeApi, animelists::AnimeListsApi, category::CategoryApi,
timetables::TimetablesApi,
},
auth::Auth,
utils::LazyLock,
};
use self::{api_request::ApiRequest, errors::BuilderError};
pub use auth::AppToken;
const API_URL: &str = "https://animeschedule.net/api/v3";
static RUNTIME: LazyLock<Runtime> = LazyLock::new(|| {
Builder::new_multi_thread()
.enable_all()
.build()
.expect("Failed building the Runtime")
});
#[derive(Clone)]
pub struct AnimeScheduleClient {
http: ApiRequest,
pub auth: Arc<Auth>,
}
impl AnimeScheduleClient {
pub fn anime(&self) -> AnimeApi {
AnimeApi::new(self.clone())
}
pub fn animelists(&self) -> AnimeListsApi {
AnimeListsApi::new(self.clone())
}
pub fn categories(&self, category: &str) -> CategoryApi {
CategoryApi::new(self.clone(), category)
}
pub fn timetables(&self) -> TimetablesApi {
TimetablesApi::new(self.clone())
}
pub fn account(&self) -> AccountApi {
AccountApi::new(self.clone())
}
}
#[derive(Default)]
pub struct AnimeScheduleBuilder {
auth: Option<Arc<Auth>>,
client_id: Option<ClientId>,
client_secret: Option<ClientSecret>,
app_token: Option<AppToken>,
redirect_url: Option<RedirectUrl>,
#[allow(clippy::complexity)]
http_cb: Option<Box<dyn FnOnce(ClientBuilder) -> Result<Client, reqwest::Error> + 'static>>,
}
impl AnimeScheduleBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn auth(mut self, auth: Auth) -> Self {
self.auth = Some(Arc::new(auth));
self
}
pub fn auth_shared(mut self, auth: Arc<Auth>) -> Self {
self.auth = Some(auth);
self
}
pub fn client_id(mut self, client_id: ClientId) -> Self {
self.client_id = Some(client_id);
self
}
pub fn client_secret(mut self, client_secret: ClientSecret) -> Self {
self.client_secret = Some(client_secret);
self
}
pub fn app_token(mut self, app_token: AppToken) -> Self {
self.app_token = Some(app_token);
self
}
pub fn redirect_url(mut self, redirect_url: RedirectUrl) -> Self {
self.redirect_url = Some(redirect_url);
self
}
pub fn http_builder(
mut self,
cb: impl FnOnce(ClientBuilder) -> Result<Client, reqwest::Error> + 'static,
) -> Self {
self.http_cb = Some(Box::new(cb));
self
}
pub fn build(self) -> Result<AnimeScheduleClient, BuilderError> {
let auth = if let Some(auth) = self.auth {
auth
} else {
let Some(client_id) = self.client_id else {
return Err(BuilderError::Builder("client_id".to_owned()));
};
let Some(client_secret) = self.client_secret else {
return Err(BuilderError::Builder("client_secret".to_owned()));
};
let Some(app_token) = self.app_token else {
return Err(BuilderError::Builder("app_token".to_owned()));
};
let Some(redirect_url) = self.redirect_url else {
return Err(BuilderError::Builder("redirect_url".to_owned()));
};
Arc::new(Auth::new(client_id, client_secret, app_token, redirect_url))
};
let http = if let Some(cb) = self.http_cb {
let builder = ClientBuilder::new();
cb(builder)?
} else {
ClientBuilder::new()
.user_agent(concat!(
env!("CARGO_PKG_NAME"),
"/",
env!("CARGO_PKG_VERSION"),
))
.build()?
};
let http = ApiRequest::new(auth.clone(), http);
let mal_client = AnimeScheduleClient { auth, http };
Ok(mal_client)
}
}