#![allow(
clippy::missing_const_for_fn,
clippy::must_use_candidate,
clippy::cast_precision_loss,
clippy::struct_excessive_bools
)]
use async_trait::async_trait;
use chrono::{DateTime, Duration, Utc};
use crate::error::DotError;
use log::{info, trace};
use reqwest::Response;
use std::sync::Arc;
use tokio::{
sync::Mutex,
time::{sleep, Duration as TkDuration},
};
pub mod board;
mod cat_thread;
mod catalog;
pub mod error;
pub mod post;
pub mod thread;
pub use catalog::Catalog as Catalog;
pub(crate) type Result<T> = std::result::Result<T, DotError>;
#[derive(Debug)]
pub struct Client {
creation_time: DateTime<Utc>,
req_client: reqwest::Client,
pub(crate) last_checked: DateTime<Utc>,
}
impl Client {
pub fn new() -> Arc<Mutex<Self>> {
let req_client = reqwest::Client::new();
let last_checked = Utc::now();
let creation_time = last_checked;
info!("constructed client.");
Arc::new(Mutex::new(Self {
creation_time,
req_client,
last_checked,
}))
}
pub fn req_client(&self) -> &reqwest::Client {
&self.req_client
}
pub async fn get(&mut self, url: &str) -> Result<Response> {
let current_time = Utc::now().signed_duration_since(self.last_checked);
if (current_time < Duration::seconds(1)) && (self.creation_time != self.last_checked) {
trace!("Requesting responses too fast! Slowing down requests to 1 per second");
sleep(TkDuration::from_secs(1)).await;
}
let resp = self.req_client.get(url).send().await?;
self.last_checked = Utc::now();
trace!(
"Updated the client last checked time: {}",
self.last_checked
);
Ok(resp)
}
}
type Dot4chClient = Arc<Mutex<Client>>;
pub(crate) async fn header(client: &Dot4chClient) -> String {
trace!("Sending request with If-Modified-Since header");
format!(
"{}",
client
.lock()
.await
.last_checked
.format("%a, %d %b %Y %T GMT")
)
}
#[async_trait(?Send)]
pub trait IfModifiedSince {
async fn fetch(
client: &Dot4chClient,
url: &str,
header: &str,
) -> Result<Response>;
}
#[async_trait(?Send)]
pub trait Update {
type Output;
async fn update(self) -> Result<Self::Output>;
}
#[async_trait(?Send)]
pub trait Procedures {
type Output;
async fn refresh_time(&mut self) -> Result<()>;
async fn fetch_status(self, response: Response) -> Result<Self::Output>;
async fn from_response(self, response: Response) -> Result<Self::Output>;
}
#[doc(hidden)]
fn default<T: Default>() -> T {
Default::default()
}