use crate::{Result, Stats};
use core::{
ops::{Deref, DerefMut},
time::Duration,
};
use std::sync::Arc;
use tokio::{
sync::{mpsc, RwLock, RwLockWriteGuard, Semaphore},
task::{spawn, JoinHandle},
time::sleep,
};
mod client;
pub use client::AsClient;
pub(crate) use client::AsClientSealed;
cfg_if::cfg_if! {
if #[cfg(feature = "serenity")] {
mod serenity_impl;
#[cfg_attr(docsrs, doc(cfg(feature = "serenity")))]
pub use serenity_impl::Serenity;
}
}
cfg_if::cfg_if! {
if #[cfg(feature = "twilight")] {
mod twilight_impl;
#[cfg_attr(docsrs, doc(cfg(feature = "twilight")))]
pub use twilight_impl::Twilight;
}
}
pub struct SharedStats {
sem: Semaphore,
stats: RwLock<Stats>,
}
pub struct SharedStatsGuard<'a> {
sem: &'a Semaphore,
guard: RwLockWriteGuard<'a, Stats>,
}
impl SharedStatsGuard<'_> {
#[inline(always)]
pub fn replace(&mut self, other: Stats) {
let ref_mut = self.guard.deref_mut();
*ref_mut = other;
}
#[inline(always)]
pub fn set_server_count(&mut self, server_count: usize) {
self.guard.server_count = Some(server_count);
}
#[inline(always)]
pub fn set_shard_count(&mut self, shard_count: usize) {
self.guard.shard_count = Some(shard_count);
}
}
impl Deref for SharedStatsGuard<'_> {
type Target = Stats;
#[inline(always)]
fn deref(&self) -> &Self::Target {
self.guard.deref()
}
}
impl DerefMut for SharedStatsGuard<'_> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
self.guard.deref_mut()
}
}
impl Drop for SharedStatsGuard<'_> {
#[inline(always)]
fn drop(&mut self) {
if self.sem.available_permits() < 1 {
self.sem.add_permits(1);
}
}
}
impl SharedStats {
#[inline(always)]
pub fn new() -> Self {
Self {
sem: Semaphore::const_new(0),
stats: RwLock::new(Stats::from(0)),
}
}
#[inline(always)]
pub async fn write<'a>(&'a self) -> SharedStatsGuard<'a> {
SharedStatsGuard {
sem: &self.sem,
guard: self.stats.write().await,
}
}
#[inline(always)]
async fn wait(&self) {
self.sem.acquire().await.unwrap().forget();
}
}
pub trait Handler: Send + Sync + 'static {
fn stats(&self) -> &SharedStats;
}
#[must_use]
pub struct Autoposter<H> {
handler: Arc<H>,
thread: JoinHandle<()>,
receiver: Option<mpsc::UnboundedReceiver<Result<()>>>,
}
impl<H> Autoposter<H>
where
H: Handler,
{
pub fn new<C>(client: &C, handler: H, interval: Duration) -> Self
where
C: AsClient,
{
assert!(
interval.as_secs() >= 900,
"The interval mustn't be shorter than 15 minutes."
);
let client = client.as_client();
let handler = Arc::new(handler);
let (sender, receiver) = mpsc::unbounded_channel();
Self {
handler: Arc::clone(&handler),
thread: spawn(async move {
loop {
handler.stats().wait().await;
{
let stats = handler.stats().stats.read().await;
if sender.send(client.post_stats(&stats).await).is_err() {
break;
}
};
sleep(interval).await;
}
}),
receiver: Some(receiver),
}
}
#[inline(always)]
pub fn handler(&self) -> Arc<H> {
Arc::clone(&self.handler)
}
#[inline(always)]
pub async fn recv(&mut self) -> Option<Result<()>> {
self.receiver.as_mut().expect("receiver is already taken from the receiver() method. please call recv() directly from the receiver.").recv().await
}
#[inline(always)]
pub fn receiver(&mut self) -> mpsc::UnboundedReceiver<Result<()>> {
self.receiver.take().expect("receiver() can only be called once.")
}
}
impl<H> Deref for Autoposter<H> {
type Target = H;
#[inline(always)]
fn deref(&self) -> &Self::Target {
self.handler.deref()
}
}
#[cfg(feature = "serenity")]
#[cfg_attr(docsrs, doc(cfg(feature = "serenity")))]
impl Autoposter<Serenity> {
#[inline(always)]
pub fn serenity<C>(client: &C, interval: Duration) -> Self
where
C: AsClient,
{
Self::new(client, Serenity::new(), interval)
}
}
#[cfg(feature = "twilight")]
#[cfg_attr(docsrs, doc(cfg(feature = "twilight")))]
impl Autoposter<Twilight> {
#[inline(always)]
pub fn twilight<C>(client: &C, interval: Duration) -> Self
where
C: AsClient,
{
Self::new(client, Twilight::new(), interval)
}
}
impl<H> Drop for Autoposter<H> {
#[inline(always)]
fn drop(&mut self) {
self.thread.abort();
}
}