diff --git a/Cargo.toml b/Cargo.toml index 84a7fc6f8c0..97d9363286e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ edition.workspace = true rust-version.workspace = true [workspace] -members = ["examples/*"] +members = ["examples/*", "serenity-core", "serenity-voice-model"] [workspace.package] documentation = "https://docs.rs/serenity" @@ -26,51 +26,50 @@ license = "ISC" edition = "2024" rust-version = "1.88" -[dependencies] -# Required dependencies -bitflags = "2.4.2" -serde_json = { version = "1.0.108", features = ["raw_value"] } -async-trait = "0.1.74" -tracing = { version = "0.1.40", features = ["log"] } -serde = { version = "1.0.192", features = ["derive", "rc"] } -url = { version = "2.4.1", features = ["serde"] } -tokio = { version = "1.34.0", features = ["macros", "rt", "sync", "time", "io-util"] } +[workspace.dependencies] +aformat = "0.1.3" +dashmap = "6.1.0" +extract_map = { version = "0.3.0", features = ["serde"] } futures = { version = "0.3.29", default-features = false, features = ["std"] } -time = { version = "0.3.36", features = ["formatting", "parsing", "serde-well-known"] } -base64 = { version = "0.22.0" } -zeroize = { version = "1.7" } # Not used in serenity, but bumps the minimal version from secrecy -arrayvec = { version = "0.7.4", features = ["serde"] } -serde_cow = { version = "0.1.0" } +parking_lot = "0.12.1" +reqwest = { version = "0.12.2", default-features = false, features = ["multipart", "stream", "json"] } +serde = { version = "1.0.192", features = ["derive", "rc"] } +serde_json = { version = "1.0.108", features = ["raw_value"] } small-fixed-array = { version = "0.4.10", features = ["serde"] } -bool_to_bitflags = { version = "0.1.2" } -nonmax = { version = "0.5.5", features = ["serde"] } strum = { version = "0.26", features = ["derive"] } to-arraystring = "0.2.0" -extract_map = { version = "0.3.0", features = ["serde"] } -aformat = "0.1.3" -bytes = "1.5.0" -ref-cast = "1.0.23" -# Optional dependencies -foldhash = { version = "0.1.4", optional = true } -chrono = { version = "0.4.31", default-features = false, features = ["clock", "serde"], optional = true } +tokio = { version = "1.34.0", features = ["macros", "rt", "sync", "time", "io-util"] } +tracing = { version = "0.1.40", features = ["log"] } +typesize = { version = "0.1.13", features = ["url", "time", "serde_json", "secrecy", "parking_lot", "nonmax"] } +url = { version = "2.4.1", features = ["serde"] } + +[dependencies] +# Serenity workspace crates +serenity-core = { path = "serenity-core" } + +# Workspace dependencies +aformat = { workspace = true, optional = true } +dashmap = { workspace = true, optional = true } +futures = { workspace = true } +parking_lot = { workspace = true } +reqwest = { workspace = true, optional = true } +serde = { workspace = true } +serde_json = { workspace = true } +small-fixed-array = { workspace = true } +strum = { workspace = true, optional = true } +to-arraystring = { workspace = true, optional = true } +tokio = { workspace = true } +tracing = { workspace = true, optional = true } +typesize = { workspace = true, optional = true } +url = { workspace = true, optional = true } + +# Additional dependencies +async-trait = "0.1.74" +ed25519-dalek = { version = "2.0.0", optional = true } +extract_map = { workspace = true, optional = true } flate2 = { version = "1.0.28", optional = true } -zstd-safe = { version = "7.2.1", optional = true } -reqwest = { version = "0.12.2", default-features = false, features = ["multipart", "stream", "json"], optional = true } tokio-tungstenite = { version = "0.26.1", features = ["url"], optional = true } -percent-encoding = { version = "2.3.0", optional = true } -mini-moka = { version = "0.10.2", optional = true } -mime_guess = { version = "2.0.4", optional = true } -dashmap = { version = "6.1.0", features = ["serde"], optional = true } -parking_lot = { version = "0.12.1"} -ed25519-dalek = { version = "2.0.0", optional = true } -typesize = { version = "0.1.13", optional = true, features = ["url", "time", "serde_json", "secrecy", "parking_lot", "nonmax"] } -# serde feature only allows for serialisation, -# Serenity workspace crates -serenity-voice-model = { version = "0.2.0", path = "./voice-model", optional = true } - -[dev-dependencies.http_crate] -version = "1.1.0" -package = "http" +zstd-safe = { version = "7.2.1", optional = true } [features] # Defaults with different backends @@ -88,40 +87,50 @@ default_no_backend = [ # Enables builder structs to configure Discord HTTP requests. Without this feature, you have to # construct JSON manually at some places. -builder = ["tokio/fs"] +builder = ["serenity-core/builder"] # Enables the cache, which stores the data received from Discord gateway to provide access to # complete guild data, channels, users and more without needing HTTP requests. -cache = ["foldhash", "dashmap"] +cache = ["serenity-core/cache"] # Enables collectors, a utility feature that lets you await interaction events in code with # zero setup, without needing to setup an InteractionCreate event listener. -collector = ["gateway"] +collector = ["gateway", "to-arraystring"] # Enables the Framework trait which is an abstraction for old-style text commands. framework = ["gateway"] # Enables gateway support, which allows bots to listen for Discord events. -gateway = ["model", "flate2", "dashmap"] +gateway = [ + "model", + "aformat", + "extract_map", + "flate2", + "dashmap", + "reqwest", + "strum", + "tracing", + "url" +] # Enables HTTP, which enables bots to execute actions on Discord. -http = ["dashmap", "mime_guess", "percent-encoding"] +http = ["serenity-core/http"] # Enables wrapper methods around HTTP requests on model types. # Requires "builder" to configure the requests and "http" to execute them. # Note: the model type definitions themselves are always active, regardless of this feature. # TODO: remove dependeny on utils feature -model = ["builder", "http", "utils"] -voice_model = ["serenity-voice-model"] +model = ["serenity-core/model", "builder", "http", "utils"] +voice_model = ["serenity-core/voice_model"] # Enables zlib-stream transport compression of incoming gateway events. transport_compression_zlib = ["flate2", "gateway"] # Enables zstd-stream transport compression of incoming gateway events. transport_compression_zstd = ["zstd-safe", "gateway"] # Enables support for Discord API functionality that's not stable yet, as well as serenity APIs that # are allowed to change even in semver non-breaking updates. -unstable = [] +unstable = ["serenity-core/unstable"] # Enables some utility functions that can be useful for bot creators. -utils = [] +utils = ["serenity-core/utils"] voice = ["gateway"] # Enables unstable tokio features to give explicit names to internally spawned tokio tasks -tokio_task_builder = ["tokio/tracing"] +tokio_task_builder = ["tokio/tracing", "serenity-core/tokio_task_builder"] interactions_endpoint = ["ed25519-dalek"] # Uses chrono for Timestamp, instead of time -chrono = ["dep:chrono", "typesize?/chrono"] +chrono = ["serenity-core/chrono"] # This enables all parts of the serenity codebase # (Note: all feature-gated APIs to be documented should have their features listed here!) @@ -130,27 +139,26 @@ chrono = ["dep:chrono", "typesize?/chrono"] full = ["default", "collector", "voice", "voice_model", "interactions_endpoint"] # Enables temporary caching in functions that retrieve data via the HTTP API. -temp_cache = ["cache", "mini-moka", "typesize?/mini_moka", "typesize?/dashmap"] +temp_cache = ["serenity-core/temp_cache"] -typesize = ["dep:typesize", "dashmap/typesize", "small-fixed-array/typesize", "bool_to_bitflags/typesize", "extract_map/typesize"] +typesize = ["dep:typesize", "serenity-core/typesize"] # Enables compile-time heavy instrument macros from tracing -tracing_instrument = ["tracing/attributes"] +tracing_instrument = ["serenity-core/tracing_instrument", "tracing/attributes"] # Backends to pick from: # - Rustls Backends rustls_backend = [ - "reqwest/rustls-tls", + "serenity-core/rustls_backend", "tokio-tungstenite/rustls-tls-webpki-roots", ] # - Native TLS Backends native_tls_backend = [ - "reqwest/native-tls", + "serenity-core/native_tls_backend", "tokio-tungstenite/native-tls", ] - [package.metadata.docs.rs] features = ["full"] rustdoc-args = ["--cfg", "docsrs"] diff --git a/examples/testing/src/model_type_sizes.rs b/examples/testing/src/model_type_sizes.rs index ef5c77c5a1f..72677c02006 100644 --- a/examples/testing/src/model_type_sizes.rs +++ b/examples/testing/src/model_type_sizes.rs @@ -1,3 +1,4 @@ +use serenity::gateway::GatewayEvent; use serenity::model::prelude::*; pub fn print_ranking() { diff --git a/serenity-core/Cargo.toml b/serenity-core/Cargo.toml new file mode 100644 index 00000000000..3f54a36892c --- /dev/null +++ b/serenity-core/Cargo.toml @@ -0,0 +1,75 @@ +[package] +name = "serenity-core" +version = "0.12.4" + +documentation.workspace = true +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +license.workspace = true +edition.workspace = true +rust-version.workspace = true + +[dependencies] +# Serenity workspace crates +serenity-voice-model = { version = "0.2.0", path = "../serenity-voice-model", optional = true } + +# Workspace dependencies +aformat = { workspace = true } +dashmap = { workspace = true, optional = true } +extract_map = { workspace = true } +futures = { workspace = true, optional = true } +parking_lot = { workspace = true } +reqwest = { workspace = true, optional = true } +serde = { workspace = true } +serde_json = { workspace = true } +small-fixed-array = { workspace = true } +strum = { workspace = true } +to-arraystring = { workspace = true } +tokio = { workspace = true } +tracing = { workspace = true } +typesize = { workspace = true, optional = true } +url = { workspace = true } + +# Required dependencies +arrayvec = { version = "0.7.4", features = ["serde"] } +base64 = "0.22.0" +bitflags = "2.4.2" +bool_to_bitflags = "0.1.2" +nonmax = { version = "0.5.5", features = ["serde"] } +ref-cast = "1.0.23" +serde_cow = "0.1.0" +time = { version = "0.3.36", features = ["formatting", "parsing", "serde-well-known"] } +zeroize = "1.7" + +# Optional dependencies +bytes = { version = "1.5.0", optional = true } +chrono = { version = "0.4.31", default-features = false, features = ["clock", "serde"], optional = true } +foldhash = { version = "0.1.4", optional = true } +mime_guess = { version = "2.0.4", optional = true } +percent-encoding = { version = "2.3.0", optional = true } +mini-moka = { version = "0.10.2", optional = true } + +[dev-dependencies.http_crate] +version = "1.1.0" +package = "http" + +[features] +builder = ["tokio/fs", "bytes"] +cache = ["foldhash", "dashmap"] +http = ["dashmap", "mime_guess", "percent-encoding"] +model = ["builder", "http", "utils", "futures"] +utils = [] +chrono = ["dep:chrono", "typesize?/chrono"] +typesize = ["dep:typesize", "dashmap/typesize", "small-fixed-array/typesize", "bool_to_bitflags/typesize", "extract_map/typesize"] + +unstable = [] +tracing_instrument = ["tracing/attributes"] +tokio_task_builder = ["tokio/tracing"] +voice_model = ["serenity-voice-model"] + +temp_cache = ["cache", "mini-moka", "typesize?/mini_moka", "typesize?/dashmap"] + +rustls_backend = ["reqwest/rustls-tls"] + +native_tls_backend = ["reqwest/native-tls"] diff --git a/serenity-core/build.rs b/serenity-core/build.rs new file mode 100644 index 00000000000..0b97e08c250 --- /dev/null +++ b/serenity-core/build.rs @@ -0,0 +1,13 @@ +#[cfg(all(feature = "http", not(any(feature = "rustls_backend", feature = "native_tls_backend"))))] +compile_error!( + "You have the `http` feature enabled; either the `rustls_backend` or `native_tls_backend` \ + feature must be enabled to let Serenity make requests over the network.\n\ + - `rustls_backend` uses Rustls, a pure Rust TLS-implemenation.\n\ + - `native_tls_backend` uses SChannel on Windows, Secure Transport on macOS, and OpenSSL on \ + other platforms.\n\ + If you are unsure, go with `rustls_backend`." +); + +fn main() { + println!("cargo:rustc-check-cfg=cfg(tokio_unstable, ignore_serenity_deprecated)"); +} diff --git a/src/builder/add_member.rs b/serenity-core/src/builder/add_member.rs similarity index 100% rename from src/builder/add_member.rs rename to serenity-core/src/builder/add_member.rs diff --git a/src/builder/bot_auth_parameters.rs b/serenity-core/src/builder/bot_auth_parameters.rs similarity index 100% rename from src/builder/bot_auth_parameters.rs rename to serenity-core/src/builder/bot_auth_parameters.rs diff --git a/src/builder/create_allowed_mentions.rs b/serenity-core/src/builder/create_allowed_mentions.rs similarity index 100% rename from src/builder/create_allowed_mentions.rs rename to serenity-core/src/builder/create_allowed_mentions.rs diff --git a/src/builder/create_attachment.rs b/serenity-core/src/builder/create_attachment.rs similarity index 100% rename from src/builder/create_attachment.rs rename to serenity-core/src/builder/create_attachment.rs diff --git a/src/builder/create_channel.rs b/serenity-core/src/builder/create_channel.rs similarity index 100% rename from src/builder/create_channel.rs rename to serenity-core/src/builder/create_channel.rs diff --git a/src/builder/create_command.rs b/serenity-core/src/builder/create_command.rs similarity index 100% rename from src/builder/create_command.rs rename to serenity-core/src/builder/create_command.rs diff --git a/src/builder/create_command_permission.rs b/serenity-core/src/builder/create_command_permission.rs similarity index 100% rename from src/builder/create_command_permission.rs rename to serenity-core/src/builder/create_command_permission.rs diff --git a/src/builder/create_components.rs b/serenity-core/src/builder/create_components.rs similarity index 100% rename from src/builder/create_components.rs rename to serenity-core/src/builder/create_components.rs diff --git a/src/builder/create_embed.rs b/serenity-core/src/builder/create_embed.rs similarity index 100% rename from src/builder/create_embed.rs rename to serenity-core/src/builder/create_embed.rs diff --git a/src/builder/create_forum_post.rs b/serenity-core/src/builder/create_forum_post.rs similarity index 100% rename from src/builder/create_forum_post.rs rename to serenity-core/src/builder/create_forum_post.rs diff --git a/src/builder/create_forum_tag.rs b/serenity-core/src/builder/create_forum_tag.rs similarity index 100% rename from src/builder/create_forum_tag.rs rename to serenity-core/src/builder/create_forum_tag.rs diff --git a/src/builder/create_interaction_response.rs b/serenity-core/src/builder/create_interaction_response.rs similarity index 100% rename from src/builder/create_interaction_response.rs rename to serenity-core/src/builder/create_interaction_response.rs diff --git a/src/builder/create_interaction_response_followup.rs b/serenity-core/src/builder/create_interaction_response_followup.rs similarity index 100% rename from src/builder/create_interaction_response_followup.rs rename to serenity-core/src/builder/create_interaction_response_followup.rs diff --git a/src/builder/create_invite.rs b/serenity-core/src/builder/create_invite.rs similarity index 100% rename from src/builder/create_invite.rs rename to serenity-core/src/builder/create_invite.rs diff --git a/src/builder/create_message.rs b/serenity-core/src/builder/create_message.rs similarity index 100% rename from src/builder/create_message.rs rename to serenity-core/src/builder/create_message.rs diff --git a/src/builder/create_poll.rs b/serenity-core/src/builder/create_poll.rs similarity index 100% rename from src/builder/create_poll.rs rename to serenity-core/src/builder/create_poll.rs diff --git a/src/builder/create_scheduled_event.rs b/serenity-core/src/builder/create_scheduled_event.rs similarity index 100% rename from src/builder/create_scheduled_event.rs rename to serenity-core/src/builder/create_scheduled_event.rs diff --git a/src/builder/create_soundboard.rs b/serenity-core/src/builder/create_soundboard.rs similarity index 100% rename from src/builder/create_soundboard.rs rename to serenity-core/src/builder/create_soundboard.rs diff --git a/src/builder/create_stage_instance.rs b/serenity-core/src/builder/create_stage_instance.rs similarity index 100% rename from src/builder/create_stage_instance.rs rename to serenity-core/src/builder/create_stage_instance.rs diff --git a/src/builder/create_sticker.rs b/serenity-core/src/builder/create_sticker.rs similarity index 100% rename from src/builder/create_sticker.rs rename to serenity-core/src/builder/create_sticker.rs diff --git a/src/builder/create_thread.rs b/serenity-core/src/builder/create_thread.rs similarity index 100% rename from src/builder/create_thread.rs rename to serenity-core/src/builder/create_thread.rs diff --git a/src/builder/create_webhook.rs b/serenity-core/src/builder/create_webhook.rs similarity index 100% rename from src/builder/create_webhook.rs rename to serenity-core/src/builder/create_webhook.rs diff --git a/src/builder/edit_automod_rule.rs b/serenity-core/src/builder/edit_automod_rule.rs similarity index 100% rename from src/builder/edit_automod_rule.rs rename to serenity-core/src/builder/edit_automod_rule.rs diff --git a/src/builder/edit_channel.rs b/serenity-core/src/builder/edit_channel.rs similarity index 100% rename from src/builder/edit_channel.rs rename to serenity-core/src/builder/edit_channel.rs diff --git a/src/builder/edit_command.rs b/serenity-core/src/builder/edit_command.rs similarity index 100% rename from src/builder/edit_command.rs rename to serenity-core/src/builder/edit_command.rs diff --git a/src/builder/edit_guild.rs b/serenity-core/src/builder/edit_guild.rs similarity index 100% rename from src/builder/edit_guild.rs rename to serenity-core/src/builder/edit_guild.rs diff --git a/src/builder/edit_guild_incident_actions.rs b/serenity-core/src/builder/edit_guild_incident_actions.rs similarity index 100% rename from src/builder/edit_guild_incident_actions.rs rename to serenity-core/src/builder/edit_guild_incident_actions.rs diff --git a/src/builder/edit_guild_welcome_screen.rs b/serenity-core/src/builder/edit_guild_welcome_screen.rs similarity index 100% rename from src/builder/edit_guild_welcome_screen.rs rename to serenity-core/src/builder/edit_guild_welcome_screen.rs diff --git a/src/builder/edit_guild_widget.rs b/serenity-core/src/builder/edit_guild_widget.rs similarity index 100% rename from src/builder/edit_guild_widget.rs rename to serenity-core/src/builder/edit_guild_widget.rs diff --git a/src/builder/edit_interaction_response.rs b/serenity-core/src/builder/edit_interaction_response.rs similarity index 100% rename from src/builder/edit_interaction_response.rs rename to serenity-core/src/builder/edit_interaction_response.rs diff --git a/src/builder/edit_member.rs b/serenity-core/src/builder/edit_member.rs similarity index 100% rename from src/builder/edit_member.rs rename to serenity-core/src/builder/edit_member.rs diff --git a/src/builder/edit_message.rs b/serenity-core/src/builder/edit_message.rs similarity index 100% rename from src/builder/edit_message.rs rename to serenity-core/src/builder/edit_message.rs diff --git a/src/builder/edit_profile.rs b/serenity-core/src/builder/edit_profile.rs similarity index 100% rename from src/builder/edit_profile.rs rename to serenity-core/src/builder/edit_profile.rs diff --git a/src/builder/edit_role.rs b/serenity-core/src/builder/edit_role.rs similarity index 100% rename from src/builder/edit_role.rs rename to serenity-core/src/builder/edit_role.rs diff --git a/src/builder/edit_scheduled_event.rs b/serenity-core/src/builder/edit_scheduled_event.rs similarity index 100% rename from src/builder/edit_scheduled_event.rs rename to serenity-core/src/builder/edit_scheduled_event.rs diff --git a/src/builder/edit_soundboard.rs b/serenity-core/src/builder/edit_soundboard.rs similarity index 100% rename from src/builder/edit_soundboard.rs rename to serenity-core/src/builder/edit_soundboard.rs diff --git a/src/builder/edit_stage_instance.rs b/serenity-core/src/builder/edit_stage_instance.rs similarity index 100% rename from src/builder/edit_stage_instance.rs rename to serenity-core/src/builder/edit_stage_instance.rs diff --git a/src/builder/edit_sticker.rs b/serenity-core/src/builder/edit_sticker.rs similarity index 100% rename from src/builder/edit_sticker.rs rename to serenity-core/src/builder/edit_sticker.rs diff --git a/src/builder/edit_thread.rs b/serenity-core/src/builder/edit_thread.rs similarity index 100% rename from src/builder/edit_thread.rs rename to serenity-core/src/builder/edit_thread.rs diff --git a/src/builder/edit_voice_state.rs b/serenity-core/src/builder/edit_voice_state.rs similarity index 100% rename from src/builder/edit_voice_state.rs rename to serenity-core/src/builder/edit_voice_state.rs diff --git a/src/builder/edit_webhook.rs b/serenity-core/src/builder/edit_webhook.rs similarity index 100% rename from src/builder/edit_webhook.rs rename to serenity-core/src/builder/edit_webhook.rs diff --git a/src/builder/edit_webhook_message.rs b/serenity-core/src/builder/edit_webhook_message.rs similarity index 100% rename from src/builder/edit_webhook_message.rs rename to serenity-core/src/builder/edit_webhook_message.rs diff --git a/src/builder/execute_webhook.rs b/serenity-core/src/builder/execute_webhook.rs similarity index 100% rename from src/builder/execute_webhook.rs rename to serenity-core/src/builder/execute_webhook.rs diff --git a/src/builder/get_entitlements.rs b/serenity-core/src/builder/get_entitlements.rs similarity index 97% rename from src/builder/get_entitlements.rs rename to serenity-core/src/builder/get_entitlements.rs index 1301ffc5bc5..530c15d7e3b 100644 --- a/src/builder/get_entitlements.rs +++ b/serenity-core/src/builder/get_entitlements.rs @@ -4,8 +4,10 @@ use nonmax::NonMaxU8; #[cfg(feature = "http")] use crate::http::Http; -use crate::internal::prelude::Result; +#[cfg(feature = "http")] +use crate::internal::prelude::*; use crate::model::id::{EntitlementId, GuildId, SkuId, UserId}; +#[cfg(feature = "http")] use crate::model::monetization::Entitlement; /// Builds a request to fetch active and ended [`Entitlement`]s. diff --git a/src/builder/get_messages.rs b/serenity-core/src/builder/get_messages.rs similarity index 100% rename from src/builder/get_messages.rs rename to serenity-core/src/builder/get_messages.rs diff --git a/src/builder/mod.rs b/serenity-core/src/builder/mod.rs similarity index 100% rename from src/builder/mod.rs rename to serenity-core/src/builder/mod.rs diff --git a/src/cache/cache_update.rs b/serenity-core/src/cache/cache_update.rs similarity index 100% rename from src/cache/cache_update.rs rename to serenity-core/src/cache/cache_update.rs diff --git a/src/cache/event.rs b/serenity-core/src/cache/event.rs similarity index 100% rename from src/cache/event.rs rename to serenity-core/src/cache/event.rs diff --git a/src/cache/mod.rs b/serenity-core/src/cache/mod.rs similarity index 96% rename from src/cache/mod.rs rename to serenity-core/src/cache/mod.rs index 1f7ab08df75..75d0ddca800 100644 --- a/src/cache/mod.rs +++ b/serenity-core/src/cache/mod.rs @@ -1,4 +1,4 @@ -//! A cache containing data received from [`Shard`]s. +//! A cache containing data received from the API. //! //! Using the cache allows to avoid REST API requests via the [`http`] module where possible. //! Issuing too many requests will lead to ratelimits. @@ -20,7 +20,6 @@ //! is "definitely no". If you do not care about RAM and want your bot to be able to access data //! while needing to hit the REST API as little as possible, then the answer is "yes". //! -//! [`Shard`]: crate::gateway::Shard //! [`http`]: crate::http //! [Manage Guild]: Permissions::MANAGE_GUILD @@ -125,7 +124,7 @@ struct CachedShardData { has_sent_shards_ready: bool, } -/// A cache containing data received from [`Shard`]s. +/// A cache containing data received from the API. /// /// Using the cache allows to avoid REST API requests via the [`http`] module where possible. /// Issuing too many requests will lead to ratelimits. @@ -141,7 +140,6 @@ struct CachedShardData { /// /// The documentation of each event contains the required gateway intents. /// -/// [`Shard`]: crate::gateway::Shard /// [`http`]: crate::http #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] #[derive(Debug)] @@ -268,10 +266,8 @@ impl Cache { /// data received. A single [`User`] may have multiple associated member objects that have not /// been received. /// - /// This can be used in combination with [`Shard::chunk_guild`], and can be used to determine - /// how many members have not yet been received. - /// - /// [`Shard::chunk_guild`]: crate::gateway::Shard::chunk_guild + /// This can be used in combination with guild chunking, and can be used to determine how many + /// members have not yet been received. pub fn unknown_members(&self) -> u64 { let mut total = 0; @@ -290,9 +286,9 @@ impl Cache { /// Fetches a vector of all [`Guild`]s' Ids that are stored in the cache. /// - /// Note that if you are utilizing multiple [`Shard`]s, then the guilds retrieved over all - /// shards are included in this count -- not just the current [`Context`]'s shard, if accessing - /// from one. + /// Note that if you are utilizing multiple shards, then the guilds retrieved over all shards + /// are included in this count -- not just the current shard (assuming you are connecting via + /// the gateway). /// /// # Examples /// @@ -319,9 +315,6 @@ impl Cache { /// } /// } /// ``` - /// - /// [`Context`]: crate::gateway::client::Context - /// [`Shard`]: crate::gateway::Shard pub fn guilds(&self) -> Vec { let unavailable_guilds = self.unavailable_guilds(); diff --git a/src/cache/settings.rs b/serenity-core/src/cache/settings.rs similarity index 100% rename from src/cache/settings.rs rename to serenity-core/src/cache/settings.rs diff --git a/src/cache/wrappers.rs b/serenity-core/src/cache/wrappers.rs similarity index 100% rename from src/cache/wrappers.rs rename to serenity-core/src/cache/wrappers.rs diff --git a/serenity-core/src/constants.rs b/serenity-core/src/constants.rs new file mode 100644 index 00000000000..ba15a0e433d --- /dev/null +++ b/serenity-core/src/constants.rs @@ -0,0 +1,30 @@ +//! A set of constants used by the library. + +use nonmax::NonMaxU16; + +/// The maximum length of the textual size of an embed. +pub const EMBED_MAX_LENGTH: usize = 6000; + +/// The maximum number of embeds in a message. +pub const EMBED_MAX_COUNT: usize = 10; + +/// The maximum number of stickers in a message. +pub const STICKER_MAX_COUNT: usize = 3; + +/// The maximum unicode code points allowed within a message by Discord. +pub const MESSAGE_CODE_LIMIT: usize = 2000; + +/// The maximum number of members the bot can fetch at once +pub const MEMBER_FETCH_LIMIT: NonMaxU16 = match NonMaxU16::new(1000) { + Some(m) => m, + None => unreachable!(), +}; + +/// The [UserAgent] sent along with every request. +/// +/// [UserAgent]: ::reqwest::header::USER_AGENT +pub const USER_AGENT: &str = concat!( + "DiscordBot (https://github.com/serenity-rs/serenity, ", + env!("CARGO_PKG_VERSION"), + ")" +); diff --git a/serenity-core/src/error.rs b/serenity-core/src/error.rs new file mode 100644 index 00000000000..3c22fa213a5 --- /dev/null +++ b/serenity-core/src/error.rs @@ -0,0 +1,155 @@ +use std::error::Error as StdError; +use std::fmt; +use std::io::Error as IoError; + +#[cfg(feature = "http")] +use reqwest::{Error as ReqwestError, header::InvalidHeaderValue}; + +#[cfg(feature = "http")] +use crate::http::HttpError; +use crate::internal::prelude::*; +use crate::model::ModelError; +use crate::secrets::TokenError; + +/// The common result type between most library functions. +/// +/// The library exposes functions which, for a result type, exposes only one type, rather than the +/// usual 2 (`Result`). This is because all functions that return a result return +/// serenity's [`Error`], so this is implied, and a "simpler" result is used. +pub type Result = StdResult; + +/// A common error enum returned by most of the library's functionality within a custom [`Result`]. +#[derive(Debug)] +#[non_exhaustive] +pub enum Error { + /// An [`std::io`] error. + Io(IoError), + /// An error from the [`serde_json`] crate. + Json(serde_json::Error), + /// An error from the [`model`] module. + /// + /// [`model`]: crate::model + Model(ModelError), + /// An error from the [`http`] module. + /// + /// [`http`]: crate::http + #[cfg(feature = "http")] + Http(HttpError), + /// An error from the [`secrets`] module. + /// + /// [`secrets`]: crate::secrets + Token(TokenError), + /// When parsing a URL failed due to invalid input. + Url(UrlError), +} + +#[derive(Debug)] +#[non_exhaustive] +pub enum UrlError { + Parsing(url::ParseError), + InvalidDataURI, +} + +impl fmt::Display for UrlError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Parsing(inner) => fmt::Display::fmt(&inner, f), + Self::InvalidDataURI => f.write_str("Provided string is not a valid data URI"), + } + } +} + +impl StdError for UrlError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Self::Parsing(inner) => Some(inner), + _ => None, + } + } +} + +impl From for Error { + fn from(e: IoError) -> Error { + Error::Io(e) + } +} + +impl From for Error { + fn from(e: serde_json::Error) -> Error { + Error::Json(e) + } +} + +impl From for Error { + fn from(e: ModelError) -> Error { + Error::Model(e) + } +} + +#[cfg(feature = "http")] +impl From for Error { + fn from(e: HttpError) -> Error { + Error::Http(e) + } +} + +impl From for Error { + fn from(e: TokenError) -> Error { + Error::Token(e) + } +} + +impl From for Error { + fn from(e: UrlError) -> Error { + Error::Url(e) + } +} + +impl From for Error { + fn from(e: url::ParseError) -> Error { + UrlError::Parsing(e).into() + } +} + +#[cfg(feature = "http")] +impl From for Error { + fn from(e: InvalidHeaderValue) -> Error { + HttpError::InvalidHeader(e).into() + } +} + +#[cfg(feature = "http")] +impl From for Error { + fn from(e: ReqwestError) -> Error { + HttpError::Request(e).into() + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Io(inner) => fmt::Display::fmt(&inner, f), + Self::Json(inner) => fmt::Display::fmt(&inner, f), + Self::Model(inner) => fmt::Display::fmt(&inner, f), + #[cfg(feature = "http")] + Self::Http(inner) => fmt::Display::fmt(&inner, f), + Self::Token(inner) => fmt::Display::fmt(&inner, f), + Self::Url(inner) => fmt::Display::fmt(&inner, f), + } + } +} + +impl StdError for Error { + #[cfg_attr(feature = "tracing_instrument", instrument)] + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Self::Io(inner) => Some(inner), + Self::Json(inner) => Some(inner), + Self::Model(inner) => Some(inner), + #[cfg(feature = "http")] + Self::Http(inner) => Some(inner), + Self::Token(inner) => Some(inner), + Self::Url(inner) => Some(inner), + } + } +} diff --git a/src/http/client.rs b/serenity-core/src/http/client.rs similarity index 99% rename from src/http/client.rs rename to serenity-core/src/http/client.rs index 03d0d534696..412e13dfc6f 100644 --- a/src/http/client.rs +++ b/serenity-core/src/http/client.rs @@ -521,9 +521,7 @@ impl Http { /// Creates an application emoji with the given data. /// - /// See [`Context::create_application_emoji`] for required fields. - /// - /// [`Context::create_application_emoji`]: crate::gateway::client::Context::create_application_emoji + /// See [`GuildId::create_emoji`] for required fields. pub async fn create_application_emoji(&self, map: &impl serde::Serialize) -> Result { self.fire(Request { body: Some(to_vec(map)?), @@ -627,10 +625,8 @@ impl Http { /// Creates a guild with the data provided. /// - /// Only a [`PartialGuild`] will be immediately returned, and a full [`Guild`] will be received - /// over a [`Shard`], if at least one is running. - /// - /// [`Shard`]: crate::gateway::Shard + /// Only a [`PartialGuild`] will be immediately returned, and a full [`Guild`] will later be + /// sent over the gateway via a [`GuildCreateEvent`], if at least one shard is running. #[deprecated = "This endpoint has been deprecated by Discord and will stop functioning after July 15, 2025. For more information, see: https://discord.com/developers/docs/change-log#deprecating-guild-creation-by-apps"] pub async fn create_guild(&self, map: &impl serde::Serialize) -> Result { self.fire(Request { @@ -1485,9 +1481,7 @@ impl Http { /// Changes application emoji information. /// - /// See [`Context::edit_application_emoji`] for required fields. - /// - /// [`Context::edit_application_emoji`]: crate::gateway::client::Context::edit_application_emoji + /// See [`GuildId::edit_emoji`] for required fields. pub async fn edit_application_emoji( &self, emoji_id: EmojiId, diff --git a/src/http/error.rs b/serenity-core/src/http/error.rs similarity index 100% rename from src/http/error.rs rename to serenity-core/src/http/error.rs diff --git a/src/http/mod.rs b/serenity-core/src/http/mod.rs similarity index 94% rename from src/http/mod.rs rename to serenity-core/src/http/mod.rs index 62d74d5682a..ac88ced3fea 100644 --- a/src/http/mod.rs +++ b/serenity-core/src/http/mod.rs @@ -5,18 +5,14 @@ //! implements protection to pre-emptively ratelimit, to ensure that no wasted requests are made. //! //! The HTTP module comprises of two types of requests: -//! - REST API requests, which require an authorization token; +//! - REST API requests, which require authenticating to Discord's gateway using a token; //! - Other requests, which do not require an authorization token. //! -//! The former require a [`Client`] to have logged in, while the latter may be made regardless of -//! any other usage of the library. -//! //! If a request spuriously fails, it will be retried once. //! //! Note that you may want to perform requests through a [model]s' instance methods where possible, //! as they each offer different levels of a high-level interface to the HTTP module. //! -//! [`Client`]: crate::Client //! [model]: crate::model mod client; diff --git a/src/http/multipart.rs b/serenity-core/src/http/multipart.rs similarity index 100% rename from src/http/multipart.rs rename to serenity-core/src/http/multipart.rs diff --git a/src/http/ratelimiting.rs b/serenity-core/src/http/ratelimiting.rs similarity index 100% rename from src/http/ratelimiting.rs rename to serenity-core/src/http/ratelimiting.rs diff --git a/src/http/request.rs b/serenity-core/src/http/request.rs similarity index 100% rename from src/http/request.rs rename to serenity-core/src/http/request.rs diff --git a/src/http/routing.rs b/serenity-core/src/http/routing.rs similarity index 100% rename from src/http/routing.rs rename to serenity-core/src/http/routing.rs diff --git a/src/http/typing.rs b/serenity-core/src/http/typing.rs similarity index 100% rename from src/http/typing.rs rename to serenity-core/src/http/typing.rs diff --git a/src/internal/macros.rs b/serenity-core/src/internal/macros.rs similarity index 100% rename from src/internal/macros.rs rename to serenity-core/src/internal/macros.rs diff --git a/src/internal/mod.rs b/serenity-core/src/internal/mod.rs similarity index 100% rename from src/internal/mod.rs rename to serenity-core/src/internal/mod.rs diff --git a/src/internal/prelude.rs b/serenity-core/src/internal/prelude.rs similarity index 85% rename from src/internal/prelude.rs rename to serenity-core/src/internal/prelude.rs index 8196c10bb5d..88d48fb9e11 100644 --- a/src/internal/prelude.rs +++ b/serenity-core/src/internal/prelude.rs @@ -13,4 +13,6 @@ pub use super::utils::join_to_string; #[cfg(feature = "http")] pub use crate::error::Error; pub use crate::error::Result; -pub use crate::secrets::{SecretString, Token}; +pub use crate::secrets::SecretString; +#[cfg(feature = "http")] +pub use crate::secrets::Token; diff --git a/src/internal/tokio.rs b/serenity-core/src/internal/tokio.rs similarity index 100% rename from src/internal/tokio.rs rename to serenity-core/src/internal/tokio.rs diff --git a/src/internal/utils.rs b/serenity-core/src/internal/utils.rs similarity index 100% rename from src/internal/utils.rs rename to serenity-core/src/internal/utils.rs diff --git a/serenity-core/src/lib.rs b/serenity-core/src/lib.rs new file mode 100644 index 00000000000..ec056c83221 --- /dev/null +++ b/serenity-core/src/lib.rs @@ -0,0 +1,20 @@ +#[macro_use] +extern crate serde; + +#[macro_use] +mod internal; + +#[cfg(feature = "http")] +mod constants; +pub mod secrets; + +#[cfg(feature = "builder")] +pub mod builder; +#[cfg(feature = "cache")] +pub mod cache; +pub mod error; +#[cfg(feature = "http")] +pub mod http; +pub mod model; +#[cfg(feature = "utils")] +pub mod utils; diff --git a/src/model/application/command.rs b/serenity-core/src/model/application/command.rs similarity index 98% rename from src/model/application/command.rs rename to serenity-core/src/model/application/command.rs index 132e5d41356..4789a76e37b 100644 --- a/src/model/application/command.rs +++ b/serenity-core/src/model/application/command.rs @@ -91,7 +91,7 @@ pub struct Command { impl Command { /// Create a global [`Command`], overriding an existing one with the same name if it exists. /// - /// When a created [`Command`] is used, the [`InteractionCreate`] event will be emitted. + /// When a created [`Command`] is used, a [`InteractionCreateEvent`] will be emitted. /// /// **Note**: Global commands may take up to an hour to be updated in the user slash commands /// list. If an outdated command data is sent by a user, discord will consider it as an error @@ -142,8 +142,6 @@ impl Command { /// # Errors /// /// See [`CreateCommand::execute`] for a list of possible errors. - /// - /// [`InteractionCreate`]: crate::gateway::client::FullEvent::InteractionCreate pub async fn create_global_command(http: &Http, builder: CreateCommand<'_>) -> Result { builder.execute(http, None).await } diff --git a/src/model/application/command_interaction.rs b/serenity-core/src/model/application/command_interaction.rs similarity index 100% rename from src/model/application/command_interaction.rs rename to serenity-core/src/model/application/command_interaction.rs diff --git a/src/model/application/component.rs b/serenity-core/src/model/application/component.rs similarity index 100% rename from src/model/application/component.rs rename to serenity-core/src/model/application/component.rs diff --git a/src/model/application/component_interaction.rs b/serenity-core/src/model/application/component_interaction.rs similarity index 100% rename from src/model/application/component_interaction.rs rename to serenity-core/src/model/application/component_interaction.rs diff --git a/src/model/application/interaction.rs b/serenity-core/src/model/application/interaction.rs similarity index 100% rename from src/model/application/interaction.rs rename to serenity-core/src/model/application/interaction.rs diff --git a/src/model/application/mod.rs b/serenity-core/src/model/application/mod.rs similarity index 100% rename from src/model/application/mod.rs rename to serenity-core/src/model/application/mod.rs diff --git a/src/model/application/modal_interaction.rs b/serenity-core/src/model/application/modal_interaction.rs similarity index 100% rename from src/model/application/modal_interaction.rs rename to serenity-core/src/model/application/modal_interaction.rs diff --git a/src/model/application/oauth.rs b/serenity-core/src/model/application/oauth.rs similarity index 100% rename from src/model/application/oauth.rs rename to serenity-core/src/model/application/oauth.rs diff --git a/src/model/application/ping_interaction.rs b/serenity-core/src/model/application/ping_interaction.rs similarity index 100% rename from src/model/application/ping_interaction.rs rename to serenity-core/src/model/application/ping_interaction.rs diff --git a/src/model/channel/attachment.rs b/serenity-core/src/model/channel/attachment.rs similarity index 100% rename from src/model/channel/attachment.rs rename to serenity-core/src/model/channel/attachment.rs diff --git a/src/model/channel/channel_id.rs b/serenity-core/src/model/channel/channel_id.rs similarity index 99% rename from src/model/channel/channel_id.rs rename to serenity-core/src/model/channel/channel_id.rs index 19fc00753e3..bbc690b8bbb 100644 --- a/src/model/channel/channel_id.rs +++ b/serenity-core/src/model/channel/channel_id.rs @@ -641,6 +641,7 @@ impl GenericChannelId { /// # Errors /// /// Returns [`Error::Http`] if the channel retrieval request failed. + #[cfg_attr(not(feature = "cache"), allow(unused_variable))] pub async fn to_channel( self, cache_http: impl CacheHttp, diff --git a/src/model/channel/embed.rs b/serenity-core/src/model/channel/embed.rs similarity index 100% rename from src/model/channel/embed.rs rename to serenity-core/src/model/channel/embed.rs diff --git a/src/model/channel/followed_channel.rs b/serenity-core/src/model/channel/followed_channel.rs similarity index 100% rename from src/model/channel/followed_channel.rs rename to serenity-core/src/model/channel/followed_channel.rs diff --git a/src/model/channel/guild_channel.rs b/serenity-core/src/model/channel/guild_channel.rs similarity index 100% rename from src/model/channel/guild_channel.rs rename to serenity-core/src/model/channel/guild_channel.rs diff --git a/src/model/channel/interaction_channel.rs b/serenity-core/src/model/channel/interaction_channel.rs similarity index 100% rename from src/model/channel/interaction_channel.rs rename to serenity-core/src/model/channel/interaction_channel.rs diff --git a/src/model/channel/message.rs b/serenity-core/src/model/channel/message.rs similarity index 100% rename from src/model/channel/message.rs rename to serenity-core/src/model/channel/message.rs diff --git a/src/model/channel/mod.rs b/serenity-core/src/model/channel/mod.rs similarity index 100% rename from src/model/channel/mod.rs rename to serenity-core/src/model/channel/mod.rs diff --git a/src/model/channel/private_channel.rs b/serenity-core/src/model/channel/private_channel.rs similarity index 100% rename from src/model/channel/private_channel.rs rename to serenity-core/src/model/channel/private_channel.rs diff --git a/src/model/channel/reaction.rs b/serenity-core/src/model/channel/reaction.rs similarity index 100% rename from src/model/channel/reaction.rs rename to serenity-core/src/model/channel/reaction.rs diff --git a/src/model/channel/thread.rs b/serenity-core/src/model/channel/thread.rs similarity index 100% rename from src/model/channel/thread.rs rename to serenity-core/src/model/channel/thread.rs diff --git a/src/model/colour.rs b/serenity-core/src/model/colour.rs similarity index 100% rename from src/model/colour.rs rename to serenity-core/src/model/colour.rs diff --git a/src/model/connection.rs b/serenity-core/src/model/connection.rs similarity index 100% rename from src/model/connection.rs rename to serenity-core/src/model/connection.rs diff --git a/src/model/error.rs b/serenity-core/src/model/error.rs similarity index 99% rename from src/model/error.rs rename to serenity-core/src/model/error.rs index c3afa896e12..cffca1fd707 100644 --- a/src/model/error.rs +++ b/serenity-core/src/model/error.rs @@ -97,7 +97,7 @@ impl fmt::Display for Minimum { /// /// This is always wrapped within the library's [`Error::Model`] variant. /// -/// [`Error::Model`]: crate::Error::Model +/// [`Error::Model`]: crate::error::Error::Model /// [`model`]: crate::model #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[non_exhaustive] diff --git a/src/model/event.rs b/serenity-core/src/model/event.rs similarity index 91% rename from src/model/event.rs rename to serenity-core/src/model/event.rs index 9c709d7546b..83fe6cad3e1 100644 --- a/src/model/event.rs +++ b/serenity-core/src/model/event.rs @@ -4,11 +4,8 @@ //! Discord documentation for the event. use serde::Serialize; -use serde::de::Error as DeError; -use serde_json::value::RawValue; use strum::{EnumCount, IntoStaticStr, VariantNames}; -use crate::constants::Opcode; use crate::model::prelude::*; /// Requires no gateway intents. @@ -275,14 +272,13 @@ pub struct GuildMembersChunkEvent { pub chunk_index: u32, /// Total number of expected chunks for this response. pub chunk_count: u32, - /// When passing an invalid ID to [`crate::gateway::ShardRunnerMessage::ChunkGuild`], it will - /// be returned here. + /// Any invalid IDs passed when requesting guild chunking will be returned here. #[serde(default)] pub not_found: FixedArray, - /// When passing true to [`crate::gateway::ShardRunnerMessage::ChunkGuild`], presences of the - /// returned members will be here. + /// If the `presences` field is set when requesting guild chunking, this field will be filled + /// with member presences. pub presences: Option>, - /// Nonce used in the [`crate::gateway::ShardRunnerMessage::ChunkGuild`] request. + /// Nonce used in the guild chunking request. pub nonce: Option, } @@ -930,104 +926,6 @@ pub struct MessagePollVoteRemoveEvent { pub answer_id: AnswerId, } -/// [Discord docs](https://discord.com/developers/docs/topics/gateway-events#payload-structure). -#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] -#[derive(Debug, Clone, Serialize)] -#[non_exhaustive] -#[serde(untagged)] -pub enum GatewayEvent { - Dispatch { - seq: u64, - event: DeserializedEvent, - }, - Heartbeat, - Reconnect, - /// Whether the session can be resumed. - InvalidateSession(bool), - Hello(u64), - HeartbeatAck, -} - -#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] -#[derive(Clone, Debug, Serialize)] -#[non_exhaustive] -#[serde(untagged)] -pub enum DeserializedEvent { - Success(Box), - Unknown(UnknownEvent), -} - -#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] -#[derive(Clone, Debug, Deserialize, Serialize)] -#[non_exhaustive] -pub struct UnknownEvent { - #[serde(rename = "t")] - pub ty: String, - #[serde(rename = "d")] - #[cfg_attr(feature = "typesize", typesize(with = raw_value_len))] - pub data: Box, -} - -#[cfg(feature = "typesize")] -fn raw_value_len(val: &RawValue) -> usize { - val.get().len() -} - -// Manual impl needed to emulate integer enum tags -impl<'de> Deserialize<'de> for GatewayEvent { - fn deserialize>(deserializer: D) -> StdResult { - #[derive(Deserialize)] - struct GatewayEventRaw<'a> { - op: Opcode, - #[serde(rename = "s")] - seq: Option, - #[serde(rename = "d")] - data: &'a RawValue, - #[serde(rename = "t")] - ty: Option<&'a str>, - } - - let raw_data = <&RawValue>::deserialize(deserializer)?; - - let raw = GatewayEventRaw::deserialize(raw_data).map_err(DeError::custom)?; - - Ok(match raw.op { - Opcode::Dispatch => { - if raw.ty.is_none() { - return Err(DeError::missing_field("t")); - } - - Self::Dispatch { - seq: raw.seq.ok_or_else(|| DeError::missing_field("s"))?, - event: match Box::::deserialize(raw_data) { - Ok(event) => DeserializedEvent::Success(event), - Err(_) => DeserializedEvent::Unknown( - UnknownEvent::deserialize(raw_data).map_err(DeError::custom)?, - ), - }, - } - }, - Opcode::Heartbeat => Self::Heartbeat, - Opcode::InvalidSession => { - Self::InvalidateSession(bool::deserialize(raw.data).map_err(DeError::custom)?) - }, - Opcode::Hello => { - #[derive(Deserialize)] - struct HelloPayload { - heartbeat_interval: u64, - } - - let inner = HelloPayload::deserialize(raw.data).map_err(DeError::custom)?; - - Self::Hello(inner.heartbeat_interval) - }, - Opcode::Reconnect => Self::Reconnect, - Opcode::HeartbeatAck => Self::HeartbeatAck, - _ => return Err(DeError::custom("invalid opcode")), - }) - } -} - /// Event received over a websocket connection /// /// [Discord docs](https://discord.com/developers/docs/topics/gateway-events#receive-events). diff --git a/src/model/gateway.rs b/serenity-core/src/model/gateway.rs similarity index 100% rename from src/model/gateway.rs rename to serenity-core/src/model/gateway.rs diff --git a/src/model/guild/audit_log/change.rs b/serenity-core/src/model/guild/audit_log/change.rs similarity index 100% rename from src/model/guild/audit_log/change.rs rename to serenity-core/src/model/guild/audit_log/change.rs diff --git a/src/model/guild/audit_log/mod.rs b/serenity-core/src/model/guild/audit_log/mod.rs similarity index 100% rename from src/model/guild/audit_log/mod.rs rename to serenity-core/src/model/guild/audit_log/mod.rs diff --git a/src/model/guild/audit_log/utils.rs b/serenity-core/src/model/guild/audit_log/utils.rs similarity index 100% rename from src/model/guild/audit_log/utils.rs rename to serenity-core/src/model/guild/audit_log/utils.rs diff --git a/src/model/guild/automod.rs b/serenity-core/src/model/guild/automod.rs similarity index 100% rename from src/model/guild/automod.rs rename to serenity-core/src/model/guild/automod.rs diff --git a/src/model/guild/emoji.rs b/serenity-core/src/model/guild/emoji.rs similarity index 100% rename from src/model/guild/emoji.rs rename to serenity-core/src/model/guild/emoji.rs diff --git a/src/model/guild/guild_id.rs b/serenity-core/src/model/guild/guild_id.rs similarity index 100% rename from src/model/guild/guild_id.rs rename to serenity-core/src/model/guild/guild_id.rs diff --git a/src/model/guild/guild_preview.rs b/serenity-core/src/model/guild/guild_preview.rs similarity index 100% rename from src/model/guild/guild_preview.rs rename to serenity-core/src/model/guild/guild_preview.rs diff --git a/src/model/guild/integration.rs b/serenity-core/src/model/guild/integration.rs similarity index 100% rename from src/model/guild/integration.rs rename to serenity-core/src/model/guild/integration.rs diff --git a/src/model/guild/member.rs b/serenity-core/src/model/guild/member.rs similarity index 100% rename from src/model/guild/member.rs rename to serenity-core/src/model/guild/member.rs diff --git a/src/model/guild/mod.rs b/serenity-core/src/model/guild/mod.rs similarity index 99% rename from src/model/guild/mod.rs rename to serenity-core/src/model/guild/mod.rs index f109850a430..b77e86e9e1a 100644 --- a/src/model/guild/mod.rs +++ b/serenity-core/src/model/guild/mod.rs @@ -34,8 +34,6 @@ pub use self::system_channel::*; pub use self::welcome_screen::*; #[cfg(feature = "model")] use crate::builder::EditGuild; -#[cfg(doc)] -use crate::constants::LARGE_THRESHOLD; #[cfg(feature = "model")] use crate::http::{CacheHttp, Http}; use crate::model::prelude::*; @@ -210,7 +208,10 @@ pub struct Guild { /// Users who are members of the guild. /// /// Members might not all be available when the [`ReadyEvent`] is received if the - /// [`Self::member_count`] is greater than the [`LARGE_THRESHOLD`] set by the library. + /// [`Self::member_count`] is greater than the [Large Threshold] set by the library when + /// identifying. + /// + /// [Large Threshold]: https://discord.com/developers/docs/events/gateway-events#identify-identify-structure pub members: ExtractMap, /// All voice and text channels contained within a guild. /// @@ -285,8 +286,8 @@ impl Guild { /// Creates a guild with the data provided. /// - /// Only a [`PartialGuild`] will be immediately returned, and a full [`Guild`] will be received - /// over a [`Shard`]. + /// Only a [`PartialGuild`] will be immediately returned, and a full [`Guild`] will later be + /// sent over the gateway via a [`GuildCreateEvent`], if at least one shard is running. /// /// **Note**: This endpoint is usually only available for user accounts. Refer to Discord's /// information for the endpoint [here][whitelist] for more information. If you require this as @@ -310,7 +311,6 @@ impl Guild { /// /// Returns [`Error::Http`] if the current user cannot create a Guild. /// - /// [`Shard`]: crate::gateway::Shard /// [whitelist]: https://discord.com/developers/docs/resources/guild#create-guild #[deprecated = "This endpoint has been deprecated by Discord and will stop functioning after July 15, 2025. For more information, see: https://discord.com/developers/docs/change-log#deprecating-guild-creation-by-apps"] pub async fn create(http: &Http, name: &str, icon: Option) -> Result { diff --git a/src/model/guild/partial_guild.rs b/serenity-core/src/model/guild/partial_guild.rs similarity index 100% rename from src/model/guild/partial_guild.rs rename to serenity-core/src/model/guild/partial_guild.rs diff --git a/src/model/guild/premium_tier.rs b/serenity-core/src/model/guild/premium_tier.rs similarity index 100% rename from src/model/guild/premium_tier.rs rename to serenity-core/src/model/guild/premium_tier.rs diff --git a/src/model/guild/role.rs b/serenity-core/src/model/guild/role.rs similarity index 100% rename from src/model/guild/role.rs rename to serenity-core/src/model/guild/role.rs diff --git a/src/model/guild/scheduled_event.rs b/serenity-core/src/model/guild/scheduled_event.rs similarity index 100% rename from src/model/guild/scheduled_event.rs rename to serenity-core/src/model/guild/scheduled_event.rs diff --git a/src/model/guild/system_channel.rs b/serenity-core/src/model/guild/system_channel.rs similarity index 100% rename from src/model/guild/system_channel.rs rename to serenity-core/src/model/guild/system_channel.rs diff --git a/src/model/guild/welcome_screen.rs b/serenity-core/src/model/guild/welcome_screen.rs similarity index 100% rename from src/model/guild/welcome_screen.rs rename to serenity-core/src/model/guild/welcome_screen.rs diff --git a/src/model/id.rs b/serenity-core/src/model/id.rs similarity index 100% rename from src/model/id.rs rename to serenity-core/src/model/id.rs diff --git a/src/model/invite.rs b/serenity-core/src/model/invite.rs similarity index 100% rename from src/model/invite.rs rename to serenity-core/src/model/invite.rs diff --git a/src/model/mention.rs b/serenity-core/src/model/mention.rs similarity index 100% rename from src/model/mention.rs rename to serenity-core/src/model/mention.rs diff --git a/src/model/misc.rs b/serenity-core/src/model/misc.rs similarity index 100% rename from src/model/misc.rs rename to serenity-core/src/model/misc.rs diff --git a/src/model/mod.rs b/serenity-core/src/model/mod.rs similarity index 100% rename from src/model/mod.rs rename to serenity-core/src/model/mod.rs diff --git a/src/model/monetization.rs b/serenity-core/src/model/monetization.rs similarity index 100% rename from src/model/monetization.rs rename to serenity-core/src/model/monetization.rs diff --git a/src/model/permissions.rs b/serenity-core/src/model/permissions.rs similarity index 100% rename from src/model/permissions.rs rename to serenity-core/src/model/permissions.rs diff --git a/src/model/soundboard.rs b/serenity-core/src/model/soundboard.rs similarity index 100% rename from src/model/soundboard.rs rename to serenity-core/src/model/soundboard.rs diff --git a/src/model/sticker.rs b/serenity-core/src/model/sticker.rs similarity index 100% rename from src/model/sticker.rs rename to serenity-core/src/model/sticker.rs diff --git a/src/model/timestamp.rs b/serenity-core/src/model/timestamp.rs similarity index 100% rename from src/model/timestamp.rs rename to serenity-core/src/model/timestamp.rs diff --git a/src/model/user.rs b/serenity-core/src/model/user.rs similarity index 100% rename from src/model/user.rs rename to serenity-core/src/model/user.rs diff --git a/src/model/utils.rs b/serenity-core/src/model/utils.rs similarity index 100% rename from src/model/utils.rs rename to serenity-core/src/model/utils.rs diff --git a/src/model/voice.rs b/serenity-core/src/model/voice.rs similarity index 100% rename from src/model/voice.rs rename to serenity-core/src/model/voice.rs diff --git a/src/model/webhook.rs b/serenity-core/src/model/webhook.rs similarity index 100% rename from src/model/webhook.rs rename to serenity-core/src/model/webhook.rs diff --git a/src/secrets.rs b/serenity-core/src/secrets.rs similarity index 100% rename from src/secrets.rs rename to serenity-core/src/secrets.rs diff --git a/src/utils/content_safe.rs b/serenity-core/src/utils/content_safe.rs similarity index 100% rename from src/utils/content_safe.rs rename to serenity-core/src/utils/content_safe.rs diff --git a/src/utils/custom_message.rs b/serenity-core/src/utils/custom_message.rs similarity index 97% rename from src/utils/custom_message.rs rename to serenity-core/src/utils/custom_message.rs index 961a9e48bed..ee097c750f5 100644 --- a/src/utils/custom_message.rs +++ b/serenity-core/src/utils/custom_message.rs @@ -2,10 +2,8 @@ use crate::model::prelude::*; /// A builder for constructing a personal [`Message`] instance. /// -/// This can be useful for emitting a manual [`dispatch`] to the framework, but you don't have a +/// This can be useful for emitting a manual dispatch to the framework, but you don't have a /// message in hand, or just have a fragment of its data. -/// -/// [`dispatch`]: crate::framework::Framework::dispatch #[derive(Clone, Default, Debug)] pub struct CustomMessage { msg: Message, diff --git a/src/utils/formatted_timestamp.rs b/serenity-core/src/utils/formatted_timestamp.rs similarity index 100% rename from src/utils/formatted_timestamp.rs rename to serenity-core/src/utils/formatted_timestamp.rs diff --git a/src/utils/message_builder.rs b/serenity-core/src/utils/message_builder.rs similarity index 100% rename from src/utils/message_builder.rs rename to serenity-core/src/utils/message_builder.rs diff --git a/src/utils/mod.rs b/serenity-core/src/utils/mod.rs similarity index 100% rename from src/utils/mod.rs rename to serenity-core/src/utils/mod.rs diff --git a/voice-model/Cargo.toml b/serenity-voice-model/Cargo.toml similarity index 84% rename from voice-model/Cargo.toml rename to serenity-voice-model/Cargo.toml index 77d70c8f994..9ee0b8cfc5b 100644 --- a/voice-model/Cargo.toml +++ b/serenity-voice-model/Cargo.toml @@ -17,16 +17,10 @@ rust-version.workspace = true [dependencies] bitflags = "2.4" num-traits = "0.2" +serde = { workspace = true } +serde_json = { workspace = true } serde_repr = "0.1.5" -[dependencies.serde] -version = "1" -features = ["derive"] - -[dependencies.serde_json] -features = ["raw_value"] -version = "1" - [dev-dependencies] criterion = "0.5" serde_test = "1" diff --git a/voice-model/benches/de.rs b/serenity-voice-model/benches/de.rs similarity index 100% rename from voice-model/benches/de.rs rename to serenity-voice-model/benches/de.rs diff --git a/voice-model/rustfmt.toml b/serenity-voice-model/rustfmt.toml similarity index 100% rename from voice-model/rustfmt.toml rename to serenity-voice-model/rustfmt.toml diff --git a/voice-model/src/close_code.rs b/serenity-voice-model/src/close_code.rs similarity index 100% rename from voice-model/src/close_code.rs rename to serenity-voice-model/src/close_code.rs diff --git a/voice-model/src/constants.rs b/serenity-voice-model/src/constants.rs similarity index 100% rename from voice-model/src/constants.rs rename to serenity-voice-model/src/constants.rs diff --git a/voice-model/src/event/from.rs b/serenity-voice-model/src/event/from.rs similarity index 100% rename from voice-model/src/event/from.rs rename to serenity-voice-model/src/event/from.rs diff --git a/voice-model/src/event/mod.rs b/serenity-voice-model/src/event/mod.rs similarity index 100% rename from voice-model/src/event/mod.rs rename to serenity-voice-model/src/event/mod.rs diff --git a/voice-model/src/event/tests.rs b/serenity-voice-model/src/event/tests.rs similarity index 100% rename from voice-model/src/event/tests.rs rename to serenity-voice-model/src/event/tests.rs diff --git a/voice-model/src/id.rs b/serenity-voice-model/src/id.rs similarity index 100% rename from voice-model/src/id.rs rename to serenity-voice-model/src/id.rs diff --git a/voice-model/src/lib.rs b/serenity-voice-model/src/lib.rs similarity index 100% rename from voice-model/src/lib.rs rename to serenity-voice-model/src/lib.rs diff --git a/voice-model/src/opcode.rs b/serenity-voice-model/src/opcode.rs similarity index 100% rename from voice-model/src/opcode.rs rename to serenity-voice-model/src/opcode.rs diff --git a/voice-model/src/payload.rs b/serenity-voice-model/src/payload.rs similarity index 100% rename from voice-model/src/payload.rs rename to serenity-voice-model/src/payload.rs diff --git a/voice-model/src/protocol_data.rs b/serenity-voice-model/src/protocol_data.rs similarity index 100% rename from voice-model/src/protocol_data.rs rename to serenity-voice-model/src/protocol_data.rs diff --git a/voice-model/src/speaking_state.rs b/serenity-voice-model/src/speaking_state.rs similarity index 100% rename from voice-model/src/speaking_state.rs rename to serenity-voice-model/src/speaking_state.rs diff --git a/voice-model/src/util.rs b/serenity-voice-model/src/util.rs similarity index 100% rename from voice-model/src/util.rs rename to serenity-voice-model/src/util.rs diff --git a/src/collector/mod.rs b/src/collector/mod.rs index 52bc1cd1980..5acfdd868cc 100644 --- a/src/collector/mod.rs +++ b/src/collector/mod.rs @@ -172,7 +172,7 @@ make_specific_collector!( CollectComponentInteractions, collect_component_interactions, // This defines the extractor pattern, which extracts the data we want to collect from an Event. Event::InteractionCreate(InteractionCreateEvent { - interaction: Interaction::Component(interaction), + interaction: Interaction::Component(interaction), .. }) => interaction, // All following lines define built-in filters of the collector. // Each line consists of: @@ -189,7 +189,7 @@ make_specific_collector!( ModalInteractionCollector, ModalInteraction, CollectModalInteractions, collect_modal_interactions, Event::InteractionCreate(InteractionCreateEvent { - interaction: Interaction::Modal(interaction), + interaction: Interaction::Modal(interaction), .. }) => interaction, author_id: UserId => interaction.user.id == *author_id, channel_id: GenericChannelId => interaction.channel_id == *channel_id, @@ -200,7 +200,7 @@ make_specific_collector!( make_specific_collector!( ReactionCollector, Reaction, CollectReactions, collect_reactions, - Event::ReactionAdd(ReactionAddEvent { reaction }) => reaction, + Event::ReactionAdd(ReactionAddEvent { reaction, .. }) => reaction, author_id: UserId => reaction.user_id.is_none_or(|a| a == *author_id), channel_id: GenericChannelId => reaction.channel_id == *channel_id, guild_id: GuildId => reaction.guild_id.is_none_or(|g| g == *guild_id), @@ -209,7 +209,7 @@ make_specific_collector!( make_specific_collector!( MessageCollector, Message, CollectMessages, collect_messages, - Event::MessageCreate(MessageCreateEvent { message }) => message, + Event::MessageCreate(MessageCreateEvent { message, .. }) => message, author_id: UserId => message.author.id == *author_id, channel_id: GenericChannelId => message.channel_id == *channel_id, guild_id: GuildId => message.guild_id.is_none_or(|g| g == *guild_id), diff --git a/src/collector/quick_modal.rs b/src/collector/quick_modal.rs index d4d0c40d74f..3b7460a4f9e 100644 --- a/src/collector/quick_modal.rs +++ b/src/collector/quick_modal.rs @@ -1,5 +1,7 @@ use std::borrow::Cow; +use to_arraystring::ToArrayString; + use crate::builder::{CreateActionRow, CreateInputText, CreateInteractionResponse, CreateModal}; use crate::collector::ModalInteractionCollector; use crate::gateway::client::Context; diff --git a/src/error.rs b/src/error.rs index 605413f53ca..cae46dc8761 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,9 +1,7 @@ use std::error::Error as StdError; use std::fmt; -use std::io::Error as IoError; -#[cfg(feature = "http")] -use reqwest::{Error as ReqwestError, header::InvalidHeaderValue}; +pub use serenity_core::error::Error as CoreError; #[cfg(feature = "gateway")] use tokio_tungstenite::tungstenite::error::Error as TungsteniteError; #[cfg(feature = "tracing_instrument")] @@ -11,75 +9,22 @@ use tracing::instrument; #[cfg(feature = "gateway")] use crate::gateway::GatewayError; -#[cfg(feature = "http")] -use crate::http::HttpError; -use crate::internal::prelude::*; -use crate::model::ModelError; -use crate::secrets::TokenError; -/// The common result type between most library functions. -/// -/// The library exposes functions which, for a result type, exposes only one type, rather than the -/// usual 2 (`Result`). This is because all functions that return a result return -/// serenity's [`Error`], so this is implied, and a "simpler" result is used. -pub type Result = StdResult; +pub type Result = std::result::Result; -/// A common error enum returned by most of the library's functionality within a custom [`Result`]. #[derive(Debug)] #[non_exhaustive] pub enum Error { - /// An [`std::io`] error. - Io(IoError), - /// An error from the [`serde_json`] crate. - Json(serde_json::Error), - /// An error from the [`model`] module. - /// - /// [`model`]: crate::model - Model(ModelError), /// An error from the [`gateway`] module. /// /// [`gateway`]: crate::gateway #[cfg(feature = "gateway")] Gateway(GatewayError), - /// An error from the [`http`] module. - /// - /// [`http`]: crate::http - #[cfg(feature = "http")] - Http(HttpError), /// An error from the `tungstenite` crate. #[cfg(feature = "gateway")] Tungstenite(Box), - /// An error from the [`secrets`] module. - /// - /// [`secrets`]: crate::secrets - Token(TokenError), - /// When parsing a URL failed due to invalid input. - Url(UrlError), -} - -#[derive(Debug)] -#[non_exhaustive] -pub enum UrlError { - Parsing(url::ParseError), - InvalidDataURI, -} - -impl fmt::Display for UrlError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Parsing(inner) => fmt::Display::fmt(&inner, f), - Self::InvalidDataURI => f.write_str("Provided string is not a valid data URI"), - } - } -} - -impl StdError for UrlError { - fn source(&self) -> Option<&(dyn StdError + 'static)> { - match self { - Self::Parsing(inner) => Some(inner), - _ => None, - } - } + /// An error from serenity's core. + Core(CoreError), } #[cfg(feature = "gateway")] @@ -89,24 +34,6 @@ impl From for Error { } } -impl From for Error { - fn from(e: IoError) -> Error { - Error::Io(e) - } -} - -impl From for Error { - fn from(e: serde_json::Error) -> Error { - Error::Json(e) - } -} - -impl From for Error { - fn from(e: ModelError) -> Error { - Error::Model(e) - } -} - #[cfg(feature = "gateway")] impl From for Error { fn from(e: TungsteniteError) -> Error { @@ -114,59 +41,20 @@ impl From for Error { } } -#[cfg(feature = "http")] -impl From for Error { - fn from(e: HttpError) -> Error { - Error::Http(e) - } -} - -impl From for Error { - fn from(e: TokenError) -> Error { - Error::Token(e) - } -} - -impl From for Error { - fn from(e: UrlError) -> Error { - Error::Url(e) - } -} - -impl From for Error { - fn from(e: url::ParseError) -> Error { - UrlError::Parsing(e).into() - } -} - -#[cfg(feature = "http")] -impl From for Error { - fn from(e: InvalidHeaderValue) -> Error { - HttpError::InvalidHeader(e).into() - } -} - -#[cfg(feature = "http")] -impl From for Error { - fn from(e: ReqwestError) -> Error { - HttpError::Request(e).into() +impl From for Error { + fn from(e: CoreError) -> Error { + Error::Core(e) } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Io(inner) => fmt::Display::fmt(&inner, f), - Self::Json(inner) => fmt::Display::fmt(&inner, f), - Self::Model(inner) => fmt::Display::fmt(&inner, f), #[cfg(feature = "gateway")] Self::Gateway(inner) => fmt::Display::fmt(&inner, f), - #[cfg(feature = "http")] - Self::Http(inner) => fmt::Display::fmt(&inner, f), #[cfg(feature = "gateway")] Self::Tungstenite(inner) => fmt::Display::fmt(&inner, f), - Self::Token(inner) => fmt::Display::fmt(&inner, f), - Self::Url(inner) => fmt::Display::fmt(&inner, f), + Self::Core(inner) => fmt::Display::fmt(&inner, f), } } } @@ -175,17 +63,11 @@ impl StdError for Error { #[cfg_attr(feature = "tracing_instrument", instrument)] fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { - Self::Io(inner) => Some(inner), - Self::Json(inner) => Some(inner), - Self::Model(inner) => Some(inner), #[cfg(feature = "gateway")] Self::Gateway(inner) => Some(inner), - #[cfg(feature = "http")] - Self::Http(inner) => Some(inner), #[cfg(feature = "gateway")] Self::Tungstenite(inner) => Some(inner), - Self::Token(inner) => Some(inner), - Self::Url(inner) => Some(inner), + Self::Core(inner) => Some(inner), } } } diff --git a/src/gateway/client/context.rs b/src/gateway/client/context.rs index cc61bf50e8a..dba77379e3a 100644 --- a/src/gateway/client/context.rs +++ b/src/gateway/client/context.rs @@ -15,6 +15,7 @@ use crate::gateway::{ ShardRunnerMessage, }; use crate::http::{CacheHttp, Http}; +use crate::internal::prelude::*; use crate::model::prelude::*; /// A general utility struct provided on event dispatches. @@ -306,7 +307,7 @@ impl Context { /// /// Returns an error if the Application ID is not known. pub async fn get_application_emojis(&self) -> Result> { - self.http.get_application_emojis().await + Ok(self.http.get_application_emojis().await?) } /// Gets information about an application emoji. @@ -315,7 +316,7 @@ impl Context { /// /// Returns an error if the emoji does not exist. pub async fn get_application_emoji(&self, emoji_id: EmojiId) -> Result { - self.http.get_application_emoji(emoji_id).await + Ok(self.http.get_application_emoji(emoji_id).await?) } /// Creates an application emoji with a name and base64-encoded image. @@ -336,7 +337,7 @@ impl Context { image, }; - self.http.create_application_emoji(&body).await + Ok(self.http.create_application_emoji(&body).await?) } /// Changes the name of an application emoji. @@ -354,7 +355,7 @@ impl Context { name, }; - self.http.edit_application_emoji(emoji_id, &body).await + Ok(self.http.edit_application_emoji(emoji_id, &body).await?) } /// Deletes an application emoji. @@ -363,6 +364,6 @@ impl Context { /// /// Returns an error if the emoji does not exist. pub async fn delete_application_emoji(&self, emoji_id: EmojiId) -> Result<()> { - self.http.delete_application_emoji(emoji_id).await + Ok(self.http.delete_application_emoji(emoji_id).await?) } } diff --git a/src/gateway/client/dispatch.rs b/src/gateway/client/dispatch.rs index 1fa3150d011..356d457ac64 100644 --- a/src/gateway/client/dispatch.rs +++ b/src/gateway/client/dispatch.rs @@ -495,6 +495,7 @@ fn update_cache_with_event( Event::MessagePollVoteRemove(event) => FullEvent::MessagePollVoteRemove { event, }, + _ => todo!(), }; (event, extra_event) diff --git a/src/gateway/client/event_handler.rs b/src/gateway/client/event_handler.rs index feadbb7d558..b6f10c005bf 100644 --- a/src/gateway/client/event_handler.rs +++ b/src/gateway/client/event_handler.rs @@ -3,6 +3,7 @@ use std::collections::VecDeque; use std::num::NonZeroU16; use async_trait::async_trait; +use extract_map::ExtractMap; use strum::{EnumCount, IntoStaticStr, VariantNames}; use super::context::Context; diff --git a/src/gateway/client/mod.rs b/src/gateway/client/mod.rs index 27f84384ef3..8eabfaed87c 100644 --- a/src/gateway/client/mod.rs +++ b/src/gateway/client/mod.rs @@ -472,6 +472,7 @@ impl Client { /// manager. /// /// [gateway docs]: crate::gateway#sharding + /// [`Error::Http`]: serenity_core::error::Error::Http #[cfg_attr(feature = "tracing_instrument", instrument(skip(self)))] pub async fn start(&mut self) -> Result<()> { self.start_connection(0, 0, NonZeroU16::MIN).await @@ -515,6 +516,7 @@ impl Client { /// manager. /// /// [gateway docs]: crate::gateway#sharding + /// [`Error::Http`]: serenity_core::error::Error::Http #[cfg_attr(feature = "tracing_instrument", instrument(skip(self)))] pub async fn start_autosharded(&mut self) -> Result<()> { let (end, total) = { @@ -581,6 +583,7 @@ impl Client { /// manager. /// /// [gateway docs]: crate::gateway#sharding + /// [`Error::Http`]: serenity_core::error::Error::Http #[cfg_attr(feature = "tracing_instrument", instrument(skip(self)))] pub async fn start_shard(&mut self, shard: u16, shards: u16) -> Result<()> { self.start_connection(shard, shard, check_shard_total(shards)).await @@ -624,6 +627,7 @@ impl Client { /// manager. /// /// [Gateway docs]: crate::gateway#sharding + /// [`Error::Http`]: serenity_core::error::Error::Http #[cfg_attr(feature = "tracing_instrument", instrument(skip(self)))] pub async fn start_shards(&mut self, total_shards: u16) -> Result<()> { self.start_connection(0, total_shards - 1, check_shard_total(total_shards)).await @@ -667,6 +671,7 @@ impl Client { /// manager. /// /// [Gateway docs]: crate::gateway#sharding + /// [`Error::Http`]: serenity_core::error::Error::Http #[cfg_attr(feature = "tracing_instrument", instrument(skip(self)))] pub async fn start_shard_range(&mut self, range: Range, total_shards: u16) -> Result<()> { self.start_connection(range.start, range.end, check_shard_total(total_shards)).await diff --git a/src/constants.rs b/src/gateway/constants.rs similarity index 78% rename from src/constants.rs rename to src/gateway/constants.rs index a88dbb8baed..d9b4914af5a 100644 --- a/src/constants.rs +++ b/src/gateway/constants.rs @@ -1,40 +1,9 @@ -//! A set of constants used by the library. - -use nonmax::NonMaxU16; - -/// The maximum length of the textual size of an embed. -pub const EMBED_MAX_LENGTH: usize = 6000; - -/// The maximum number of embeds in a message. -pub const EMBED_MAX_COUNT: usize = 10; - -/// The maximum number of stickers in a message. -pub const STICKER_MAX_COUNT: usize = 3; - /// The gateway version used by the library. The gateway URL is retrieved via the REST API. pub const GATEWAY_VERSION: u8 = 10; /// The large threshold to send on identify. pub const LARGE_THRESHOLD: u8 = 250; -/// The maximum unicode code points allowed within a message by Discord. -pub const MESSAGE_CODE_LIMIT: usize = 2000; - -/// The maximum number of members the bot can fetch at once -pub const MEMBER_FETCH_LIMIT: NonMaxU16 = match NonMaxU16::new(1000) { - Some(m) => m, - None => unreachable!(), -}; - -/// The [UserAgent] sent along with every request. -/// -/// [UserAgent]: ::reqwest::header::USER_AGENT -pub const USER_AGENT: &str = concat!( - "DiscordBot (https://github.com/serenity-rs/serenity, ", - env!("CARGO_PKG_VERSION"), - ")" -); - enum_number! { /// An enum representing the gateway opcodes. /// diff --git a/src/gateway/mod.rs b/src/gateway/mod.rs index ca507d49590..222e8f4a02b 100644 --- a/src/gateway/mod.rs +++ b/src/gateway/mod.rs @@ -12,6 +12,7 @@ //! [`Client`]: client::Client pub mod client; +pub mod constants; mod error; pub mod sharding; #[cfg(feature = "voice")] @@ -21,13 +22,18 @@ mod ws; #[cfg(feature = "http")] use reqwest::IntoUrl; use reqwest::Url; +use serde::de::{Deserialize, Deserializer, Error as DeError}; +use serde_json::value::RawValue; +use self::constants::Opcode; pub use self::error::Error as GatewayError; pub use self::sharding::*; #[cfg(feature = "voice")] pub use self::voice::VoiceGatewayManager; pub use self::ws::WsClient; +use crate::error::CoreError; use crate::internal::prelude::*; +use crate::model::event::Event; use crate::model::gateway::{Activity, ActivityType}; use crate::model::id::UserId; use crate::model::user::OnlineStatus; @@ -78,7 +84,7 @@ impl ActivityData { name: name.into().trunc_into(), kind: ActivityType::Streaming, state: None, - url: Some(url.into_url()?), + url: Some(url.into_url().map_err(CoreError::from)?), }) } @@ -154,3 +160,103 @@ pub enum ChunkGuildFilter { /// Will return a maximum of 100 members. UserIds(Vec), } + +/// [Discord docs](https://discord.com/developers/docs/topics/gateway-events#payload-structure). +#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] +#[derive(Debug, Clone, Serialize)] +#[non_exhaustive] +#[serde(untagged)] +pub enum GatewayEvent { + Dispatch { + seq: u64, + event: DeserializedEvent, + }, + Heartbeat, + Reconnect, + /// Whether the session can be resumed. + InvalidateSession(bool), + Hello(u64), + HeartbeatAck, +} + +#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] +#[derive(Clone, Debug, Serialize)] +#[non_exhaustive] +#[serde(untagged)] +pub enum DeserializedEvent { + Success(Box), + Unknown(UnknownEvent), +} + +#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] +#[derive(Clone, Debug, Deserialize, Serialize)] +#[non_exhaustive] +pub struct UnknownEvent { + #[serde(rename = "t")] + pub ty: String, + #[serde(rename = "d")] + #[cfg_attr(feature = "typesize", typesize(with = raw_value_len))] + pub data: Box, +} + +#[cfg(feature = "typesize")] +fn raw_value_len(val: &RawValue) -> usize { + val.get().len() +} + +// Manual impl needed to emulate integer enum tags +impl<'de> Deserialize<'de> for GatewayEvent { + fn deserialize>(deserializer: D) -> Result { + #[derive(Debug, Clone, Deserialize)] + struct GatewayEventRaw<'a> { + op: Opcode, + #[serde(rename = "s")] + seq: Option, + #[serde(rename = "d")] + data: &'a RawValue, + #[serde(rename = "t")] + ty: Option<&'a str>, + } + + let raw_data = <&RawValue>::deserialize(deserializer)?; + + let raw = GatewayEventRaw::deserialize(raw_data).map_err(DeError::custom)?; + + Ok(match raw.op { + Opcode::Dispatch => { + if raw.ty.is_none() { + return Err(DeError::missing_field("t")); + } + + Self::Dispatch { + seq: raw.seq.ok_or_else(|| DeError::missing_field("s"))?, + event: { + match Box::::deserialize(raw_data) { + Ok(event) => DeserializedEvent::Success(event), + Err(_) => DeserializedEvent::Unknown( + UnknownEvent::deserialize(raw_data).map_err(DeError::custom)?, + ), + } + }, + } + }, + Opcode::Heartbeat => Self::Heartbeat, + Opcode::InvalidSession => { + Self::InvalidateSession(bool::deserialize(raw.data).map_err(DeError::custom)?) + }, + Opcode::Hello => { + #[derive(Deserialize)] + struct HelloPayload { + heartbeat_interval: u64, + } + + let inner = HelloPayload::deserialize(raw.data).map_err(DeError::custom)?; + + Self::Hello(inner.heartbeat_interval) + }, + Opcode::Reconnect => Self::Reconnect, + Opcode::HeartbeatAck => Self::HeartbeatAck, + _ => return Err(DeError::custom("invalid opcode")), + }) + } +} diff --git a/src/gateway/sharding/mod.rs b/src/gateway/sharding/mod.rs index 823fb8ec5b7..4b79ccdefca 100644 --- a/src/gateway/sharding/mod.rs +++ b/src/gateway/sharding/mod.rs @@ -54,10 +54,19 @@ pub use self::shard_manager::{ }; pub use self::shard_queue::ShardQueue; pub use self::shard_runner::{ShardRunner, ShardRunnerMessage, ShardRunnerOptions}; -use super::{ActivityData, ChunkGuildFilter, GatewayError, PresenceData, WsClient}; -use crate::constants::{self, CloseCode}; +use super::constants::{self, CloseCode}; +use super::{ + ActivityData, + ChunkGuildFilter, + DeserializedEvent, + GatewayError, + GatewayEvent, + PresenceData, + UnknownEvent, + WsClient, +}; use crate::internal::prelude::*; -use crate::model::event::{DeserializedEvent, Event, GatewayEvent, UnknownEvent}; +use crate::model::event::Event; use crate::model::gateway::{GatewayIntents, ShardInfo}; #[cfg(feature = "voice")] use crate::model::id::ChannelId; diff --git a/src/gateway/sharding/shard_runner.rs b/src/gateway/sharding/shard_runner.rs index 36dc6d00ebb..a4a1901fc7c 100644 --- a/src/gateway/sharding/shard_runner.rs +++ b/src/gateway/sharding/shard_runner.rs @@ -20,13 +20,12 @@ use crate::framework::Framework; use crate::gateway::VoiceGatewayManager; use crate::gateway::client::dispatch::dispatch_model; use crate::gateway::client::{Context, EventHandler, RawEventHandler}; -use crate::gateway::{ActivityData, ChunkGuildFilter, GatewayError}; +use crate::gateway::{ActivityData, ChunkGuildFilter, GatewayError, GatewayEvent}; use crate::http::Http; use crate::internal::prelude::*; use crate::internal::tokio::spawn_named; #[cfg(feature = "voice")] use crate::model::event::Event; -use crate::model::event::GatewayEvent; #[cfg(feature = "voice")] use crate::model::id::ChannelId; use crate::model::id::{GuildId, ShardId}; diff --git a/src/gateway/ws.rs b/src/gateway/ws.rs index 313e8abbfef..98b48f73ff5 100644 --- a/src/gateway/ws.rs +++ b/src/gateway/ws.rs @@ -18,9 +18,16 @@ use url::Url; #[cfg(feature = "transport_compression_zstd")] use zstd_safe::{DStream as ZstdInflater, InBuffer, OutBuffer}; -use super::{ActivityData, ChunkGuildFilter, GatewayError, PresenceData, TransportCompression}; -use crate::constants::{self, Opcode}; -use crate::model::event::GatewayEvent; +use super::constants::{self, Opcode}; +use super::{ + ActivityData, + ChunkGuildFilter, + GatewayError, + GatewayEvent, + PresenceData, + TransportCompression, +}; +use crate::error::CoreError; use crate::model::gateway::{GatewayIntents, ShardInfo}; #[cfg(feature = "voice")] use crate::model::id::ChannelId; @@ -126,7 +133,7 @@ impl Compression { warn!("Err decompressing bytes: {why:?}"); debug!("Failing bytes: {slice:?}"); - why + CoreError::Io(why) })?; Ok(Some(decompressed.as_slice())) @@ -276,13 +283,13 @@ impl WsClient { Ok(event) => Ok(Some(event)), Err(err) => { debug!("Failing text: {}", String::from_utf8_lossy(json_bytes)); - Err(Error::Json(err)) + Err(Error::Core(CoreError::Json(err))) }, } } pub(crate) async fn send_json(&mut self, value: &impl serde::Serialize) -> Result<()> { - let message = Message::Text(serde_json::to_string(value)?.into()); + let message = Message::Text(serde_json::to_string(value).map_err(CoreError::Json)?.into()); self.stream.send(message).await?; Ok(()) diff --git a/src/internal.rs b/src/internal.rs new file mode 100644 index 00000000000..039f1817d81 --- /dev/null +++ b/src/internal.rs @@ -0,0 +1,73 @@ +#[cfg(feature = "gateway")] +pub mod prelude { + pub use serenity_core::secrets::Token; + pub use small_fixed_array::{FixedArray, FixedString, TruncatingInto}; + + pub use crate::error::{Error, Result}; +} + +#[cfg(feature = "gateway")] +pub mod tokio { + use std::future::Future; + + pub fn spawn_named(_name: &str, future: F) -> tokio::task::JoinHandle + where + F: Future + Send + 'static, + T: Send + 'static, + { + #[cfg(all(tokio_unstable, feature = "tokio_task_builder"))] + let handle = tokio::task::Builder::new() + .name(&*format!("serenity::{}", _name)) + .spawn(future) + .expect("called outside tokio runtime"); + #[cfg(not(all(tokio_unstable, feature = "tokio_task_builder")))] + let handle = tokio::spawn(future); + handle + } +} + +#[cfg(feature = "gateway")] +#[macro_use] +pub mod macros { + macro_rules! enum_number { + ( + $(#[$outer:meta])* + $(#[ = $default:literal])? + $vis:vis enum $Enum:ident { + $( + $(#[doc = $doc:literal])* + $(#[cfg $($cfg:tt)*])? + $Variant:ident = $value:literal, + )* + _ => Unknown($T:ty), + } + ) => { + $(#[$outer])* + $vis struct $Enum (pub $T); + + $( + impl Default for $Enum { + fn default() -> Self { + Self($default) + } + } + )? + + #[allow(non_snake_case, non_upper_case_globals)] + #[allow(clippy::allow_attributes, reason = "Does not always trigger due to macro")] + impl $Enum { + $( + $(#[doc = $doc])* + $(#[cfg $($cfg)*])? + $vis const $Variant: Self = Self($value); + )* + + /// Variant value is unknown. + #[must_use] + $vis const fn Unknown(val: $T) -> Self { + Self(val) + } + } + }; + } +} diff --git a/src/lib.rs b/src/lib.rs index 3ed332d5a88..7196a6a030b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,30 +84,29 @@ extern crate serde; #[macro_use] mod internal; -pub mod constants; -pub mod model; pub mod prelude; -#[cfg(feature = "builder")] -pub mod builder; -#[cfg(feature = "cache")] -pub mod cache; #[cfg(feature = "collector")] pub mod collector; #[cfg(feature = "framework")] pub mod framework; #[cfg(feature = "gateway")] pub mod gateway; -#[cfg(feature = "http")] -pub mod http; #[cfg(feature = "interactions_endpoint")] pub mod interactions_endpoint; -pub mod secrets; -#[cfg(feature = "utils")] -pub mod utils; mod error; +#[cfg(feature = "builder")] +pub use serenity_core::builder; +#[cfg(feature = "cache")] +pub use serenity_core::cache; +#[cfg(feature = "http")] +pub use serenity_core::http; +#[cfg(feature = "utils")] +pub use serenity_core::utils; +pub use serenity_core::{model, secrets}; + pub use crate::error::{Error, Result}; #[cfg(feature = "gateway")] pub use crate::gateway::client::Client; @@ -125,14 +124,12 @@ pub mod all { #[cfg(feature = "collector")] #[doc(no_inline)] pub use crate::collector::*; - #[doc(no_inline)] - pub use crate::constants::*; #[cfg(feature = "framework")] #[doc(no_inline)] pub use crate::framework::*; #[cfg(feature = "gateway")] #[doc(no_inline)] - pub use crate::gateway::{client::*, *}; + pub use crate::gateway::{client::*, constants::*, *}; #[cfg(feature = "http")] #[doc(no_inline)] pub use crate::http::*; @@ -157,4 +154,4 @@ pub mod all { // Re-exports of crates used internally which are already publically exposed. pub use async_trait::async_trait; -pub use {futures, nonmax, small_fixed_array}; +pub use futures;