twilight_gateway/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!("../README.md")]
3#![warn(
4    clippy::missing_const_for_fn,
5    clippy::missing_docs_in_private_items,
6    clippy::pedantic,
7    missing_docs,
8    unsafe_code
9)]
10#![allow(
11    clippy::module_name_repetitions,
12    clippy::must_use_candidate,
13    clippy::unnecessary_wraps
14)]
15
16pub mod error;
17
18mod channel;
19mod command;
20#[cfg(any(feature = "zlib", feature = "zstd"))]
21mod compression;
22mod config;
23mod event;
24mod json;
25mod latency;
26mod message;
27mod ratelimiter;
28mod session;
29mod shard;
30mod stream;
31
32pub use self::{
33    channel::MessageSender,
34    command::Command,
35    config::{Config, ConfigBuilder},
36    event::EventTypeFlags,
37    json::parse,
38    latency::Latency,
39    message::Message,
40    ratelimiter::CommandRatelimiter,
41    session::Session,
42    shard::{Shard, ShardState},
43    stream::StreamExt,
44};
45pub use twilight_model::gateway::{CloseFrame, Intents, ShardId};
46
47#[doc(no_inline)]
48pub use twilight_gateway_queue as queue;
49#[doc(no_inline)]
50pub use twilight_model::gateway::event::{Event, EventType};
51
52#[cfg(feature = "twilight-http")]
53use self::error::{StartRecommendedError, StartRecommendedErrorType};
54#[cfg(feature = "twilight-http")]
55use twilight_http::Client;
56
57/// Discord Gateway API version used by this crate.
58pub const API_VERSION: u8 = 10;
59
60/// Create a single bucket's worth of shards.
61///
62/// Passing a primary config is required. Further customization of this config
63/// may be performed in the callback.
64///
65/// Internally calls [`create_iterator`] with `(bucket_id..total).step_by(concurrency)`.
66///
67/// # Panics
68///
69/// Panics if `bucket_id >= total`, `bucket_id >= concurrency`, or `concurrency >= total`.
70///
71/// Panics if loading TLS certificates fails.
72#[track_caller]
73pub fn create_bucket<F, Q>(
74    bucket_id: u16,
75    concurrency: u16,
76    total: u32,
77    config: Config<Q>,
78    per_shard_config: F,
79) -> impl ExactSizeIterator<Item = Shard<Q>>
80where
81    F: Fn(ShardId, ConfigBuilder<Q>) -> Config<Q>,
82    Q: Clone,
83{
84    assert!(
85        u32::from(bucket_id) < total,
86        "bucket id must be less than the total"
87    );
88    assert!(
89        bucket_id < concurrency,
90        "bucket id must be less than concurrency"
91    );
92    assert!(
93        (u32::from(concurrency)) < total,
94        "concurrency must be less than the total"
95    );
96
97    create_iterator(
98        (u32::from(bucket_id)..total).step_by(concurrency.into()),
99        total,
100        config,
101        per_shard_config,
102    )
103}
104
105/// Create a iterator of shards.
106///
107/// Passing a primary config is required. Further customization of this config
108/// may be performed in the callback.
109///
110/// # Examples
111///
112/// Start 10 out of 10 shards and count them:
113///
114/// ```no_run
115/// use std::{collections::HashMap, env, sync::Arc};
116/// use twilight_gateway::{Config, Intents};
117///
118/// let token = env::var("DISCORD_TOKEN")?;
119///
120/// let config = Config::new(token.clone(), Intents::GUILDS);
121/// let shards = twilight_gateway::create_iterator(0..10, 10, config, |_, builder| builder.build());
122///
123/// assert_eq!(shards.len(), 10);
124/// # Ok::<(), Box<dyn std::error::Error>>(())
125/// ```
126///
127/// # Panics
128///
129/// Panics if `range` contains values larger than `total`.
130///
131/// Panics if loading TLS certificates fails.
132#[track_caller]
133pub fn create_iterator<F, Q>(
134    numbers: impl ExactSizeIterator<Item = u32>,
135    total: u32,
136    config: Config<Q>,
137    per_shard_config: F,
138) -> impl ExactSizeIterator<Item = Shard<Q>>
139where
140    F: Fn(ShardId, ConfigBuilder<Q>) -> Config<Q>,
141    Q: Clone,
142{
143    numbers.map(move |index| {
144        let id = ShardId::new(index, total);
145        let config = per_shard_config(id, ConfigBuilder::from(config.clone()));
146
147        Shard::with_config(id, config)
148    })
149}
150
151/// Create a range of shards from Discord's recommendation.
152///
153/// Passing a primary config is required. Further customization of this config
154/// may be performed in the callback.
155///
156/// Internally calls [`create_iterator`] with the values from [`GetGatewayAuthed`].
157///
158/// # Errors
159///
160/// Returns a [`StartRecommendedErrorType::Deserializing`] error type if the
161/// response body failed to deserialize.
162///
163/// Returns a [`StartRecommendedErrorType::Request`] error type if the request
164/// failed to complete.
165///
166/// # Panics
167///
168/// Panics if loading TLS certificates fails.
169///
170/// [`GetGatewayAuthed`]: twilight_http::request::GetGatewayAuthed
171#[cfg(feature = "twilight-http")]
172pub async fn create_recommended<F, Q>(
173    client: &Client,
174    config: Config<Q>,
175    per_shard_config: F,
176) -> Result<impl ExactSizeIterator<Item = Shard<Q>> + use<F, Q>, StartRecommendedError>
177where
178    F: Fn(ShardId, ConfigBuilder<Q>) -> Config<Q>,
179    Q: Clone,
180{
181    let request = client.gateway().authed();
182    let response = request.await.map_err(|source| StartRecommendedError {
183        kind: StartRecommendedErrorType::Request,
184        source: Some(Box::new(source)),
185    })?;
186    let info = response
187        .model()
188        .await
189        .map_err(|source| StartRecommendedError {
190            kind: StartRecommendedErrorType::Deserializing,
191            source: Some(Box::new(source)),
192        })?;
193
194    Ok(create_iterator(
195        0..info.shards,
196        info.shards,
197        config,
198        per_shard_config,
199    ))
200}