use super::{config::Config, Events, Shard};
use crate::EventTypeFlags;
use std::{
error::Error,
fmt::{Display, Formatter, Result as FmtResult},
sync::Arc,
};
use twilight_gateway_queue::{LocalQueue, Queue};
use twilight_http::Client as HttpClient;
use twilight_model::gateway::{
payload::outgoing::{identify::IdentifyProperties, update_presence::UpdatePresencePayload},
Intents,
};
#[derive(Debug)]
pub struct LargeThresholdError {
kind: LargeThresholdErrorType,
}
impl LargeThresholdError {
#[must_use = "retrieving the type has no effect if left unused"]
pub const fn kind(&self) -> &LargeThresholdErrorType {
&self.kind
}
#[allow(clippy::unused_self)]
#[must_use = "consuming the error and retrieving the source has no effect if left unused"]
pub fn into_source(self) -> Option<Box<dyn Error + Send + Sync>> {
None
}
#[must_use = "consuming the error into its parts has no effect if left unused"]
pub fn into_parts(
self,
) -> (
LargeThresholdErrorType,
Option<Box<dyn Error + Send + Sync>>,
) {
(self.kind, None)
}
}
impl Display for LargeThresholdError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match &self.kind {
LargeThresholdErrorType::TooFew { .. } => {
f.write_str("provided large threshold value is fewer than 50")
}
LargeThresholdErrorType::TooMany { .. } => {
f.write_str("provided large threshold value is more than 250")
}
}
}
}
impl Error for LargeThresholdError {}
#[derive(Debug)]
#[non_exhaustive]
pub enum LargeThresholdErrorType {
TooFew {
value: u64,
},
TooMany {
value: u64,
},
}
#[derive(Debug)]
pub struct ShardIdError {
kind: ShardIdErrorType,
}
impl ShardIdError {
#[must_use = "retrieving the type has no effect if left unused"]
pub const fn kind(&self) -> &ShardIdErrorType {
&self.kind
}
#[allow(clippy::unused_self)]
#[must_use = "consuming the error and retrieving the source has no effect if left unused"]
pub fn into_source(self) -> Option<Box<dyn Error + Send + Sync>> {
None
}
#[must_use = "consuming the error into its parts has no effect if left unused"]
pub fn into_parts(self) -> (ShardIdErrorType, Option<Box<dyn Error + Send + Sync>>) {
(self.kind, None)
}
}
impl Display for ShardIdError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match &self.kind {
ShardIdErrorType::IdTooLarge { id, total } => {
f.write_str("provided shard ID ")?;
Display::fmt(id, f)?;
f.write_str(" is larger than the total ")?;
Display::fmt(total, f)
}
}
}
}
impl Error for ShardIdError {}
#[derive(Debug)]
pub enum ShardIdErrorType {
IdTooLarge {
id: u64,
total: u64,
},
}
#[derive(Debug)]
pub struct ShardBuilder(pub(crate) Config);
impl ShardBuilder {
pub fn new(token: impl Into<String>, intents: Intents) -> Self {
Self::_new(token.into(), intents)
}
fn _new(mut token: String, intents: Intents) -> Self {
if !token.starts_with("Bot ") {
token.insert_str(0, "Bot ");
}
Self(Config {
event_types: EventTypeFlags::default(),
gateway_url: None,
http_client: Arc::new(HttpClient::new(token.clone())),
identify_properties: None,
intents,
large_threshold: 50,
presence: None,
queue: Arc::new(LocalQueue::new()),
shard: [0, 1],
token: token.into_boxed_str(),
session_id: None,
sequence: None,
})
}
pub fn build(self) -> (Shard, Events) {
Shard::new_with_config(self.0)
}
pub const fn event_types(mut self, event_types: EventTypeFlags) -> Self {
self.0.event_types = event_types;
self
}
pub fn gateway_url(mut self, gateway_url: Option<String>) -> Self {
self.0.gateway_url = gateway_url.map(String::into_boxed_str);
self
}
#[allow(clippy::missing_const_for_fn)]
pub fn http_client(mut self, http_client: Arc<HttpClient>) -> Self {
self.0.http_client = http_client;
self
}
#[allow(clippy::missing_const_for_fn)]
pub fn identify_properties(mut self, identify_properties: IdentifyProperties) -> Self {
self.0.identify_properties = Some(identify_properties);
self
}
#[allow(clippy::missing_const_for_fn)]
pub fn large_threshold(mut self, large_threshold: u64) -> Result<Self, LargeThresholdError> {
match large_threshold {
0..=49 => {
return Err(LargeThresholdError {
kind: LargeThresholdErrorType::TooFew {
value: large_threshold,
},
})
}
50..=250 => {}
251..=u64::MAX => {
return Err(LargeThresholdError {
kind: LargeThresholdErrorType::TooMany {
value: large_threshold,
},
})
}
}
self.0.large_threshold = large_threshold;
Ok(self)
}
pub fn presence(mut self, presence: UpdatePresencePayload) -> Self {
self.0.presence.replace(presence);
self
}
pub fn queue(mut self, queue: Arc<dyn Queue>) -> Self {
self.0.queue = queue;
self
}
#[allow(clippy::missing_const_for_fn)]
pub fn shard(mut self, shard_id: u64, shard_total: u64) -> Result<Self, ShardIdError> {
if shard_id >= shard_total {
return Err(ShardIdError {
kind: ShardIdErrorType::IdTooLarge {
id: shard_id,
total: shard_total,
},
});
}
self.0.shard = [shard_id, shard_total];
Ok(self)
}
}
impl<T: Into<String>> From<(T, Intents)> for ShardBuilder {
fn from((token, intents): (T, Intents)) -> Self {
Self::new(token, intents)
}
}
#[cfg(test)]
mod tests {
use super::{
LargeThresholdError, LargeThresholdErrorType, ShardBuilder, ShardIdError, ShardIdErrorType,
};
use crate::Intents;
use static_assertions::{assert_fields, assert_impl_all};
use std::{error::Error, fmt::Debug};
assert_impl_all!(LargeThresholdErrorType: Debug, Send, Sync);
assert_fields!(LargeThresholdErrorType::TooFew: value);
assert_fields!(LargeThresholdErrorType::TooMany: value);
assert_impl_all!(LargeThresholdError: Error, Send, Sync);
assert_impl_all!(ShardBuilder: Debug, From<(String, Intents)>, Send, Sync);
assert_impl_all!(ShardIdErrorType: Debug, Send, Sync);
assert_fields!(ShardIdErrorType::IdTooLarge: id, total);
assert_impl_all!(ShardIdError: Error, Send, Sync);
}