use std::{future::Future, ops::Deref, sync::Arc};
use composure::{
Credentials,
command::{BackgroundWork, HasData, Platform},
rest::BotClient,
};
use tokio_util::task::TaskTracker;
use crate::HttpTransport;
pub struct Host<T = ()> {
core: Arc<Core>,
data: Arc<T>,
}
#[derive(Clone)]
pub(crate) struct Core {
pub(crate) credentials: Credentials,
pub(crate) transport: HttpTransport,
pub(crate) tracker: TaskTracker,
}
impl<T> Clone for Host<T> {
fn clone(&self) -> Self {
Self {
core: self.core.clone(),
data: self.data.clone(),
}
}
}
impl Host {
pub(crate) fn new(credentials: Credentials) -> Self {
Self {
core: Arc::new(Core {
credentials,
transport: HttpTransport::new(),
tracker: TaskTracker::new(),
}),
data: Arc::new(()),
}
}
}
impl<T> Host<T> {
pub(crate) fn with_data<U>(self, data: U) -> Host<U> {
Host {
core: self.core,
data: Arc::new(data),
}
}
pub(crate) fn core_mut(&mut self) -> &mut Core {
Arc::make_mut(&mut self.core)
}
pub(crate) fn tracker(&self) -> &TaskTracker {
&self.core.tracker
}
pub fn data(&self) -> &T {
&self.data
}
pub fn transport(&self) -> HttpTransport {
self.core.transport.clone()
}
pub fn bot(&self) -> Option<BotClient<HttpTransport>> {
self.core.credentials.bot_client(self.transport())
}
pub fn spawn(&self, work: impl Future<Output = ()> + Send + 'static) {
self.core.tracker.spawn(work);
}
}
impl<T> Deref for Host<T> {
type Target = T;
fn deref(&self) -> &T {
&self.data
}
}
impl<T: Send + Sync + 'static> Platform for Host<T> {
type Transport = HttpTransport;
fn transport(&self) -> HttpTransport {
self.core.transport.clone()
}
fn spawn(&self, work: BackgroundWork) {
let task = tokio::spawn(work);
self.core.tracker.spawn(async move {
if let Err(error) = task.await
&& error.is_panic()
{
tracing::error!(%error, "background work panicked");
}
});
}
fn bot_token(&self) -> Option<String> {
self.core.credentials.bot_token().map(str::to_owned)
}
}
impl<T> HasData for Host<T> {
type Data = T;
fn data(&self) -> &T {
&self.data
}
}