aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMishio595 <[email protected]>2018-08-12 13:02:49 -0600
committerMishio595 <[email protected]>2018-08-12 13:02:49 -0600
commit517a56317a86d327a7ed990d555fbe871e39f0f1 (patch)
treee867c2d13b11530d3e62655779c3442370bebd79
parentcorrect a typo (diff)
parentResolve conflicts (diff)
downloadserenity-517a56317a86d327a7ed990d555fbe871e39f0f1.tar.xz
serenity-517a56317a86d327a7ed990d555fbe871e39f0f1.zip
Merge branch 'master' into old_message_on_update
-rw-r--r--.travis.yml1
-rw-r--r--CHANGELOG.md273
-rw-r--r--Cargo.toml4
-rw-r--r--LICENSE.md2
-rw-r--r--README.md36
-rw-r--r--examples/05_command_framework/src/main.rs32
-rw-r--r--src/builder/create_embed.rs89
-rw-r--r--src/builder/create_invite.rs4
-rw-r--r--src/builder/edit_guild.rs2
-rw-r--r--src/builder/edit_member.rs10
-rw-r--r--src/builder/edit_profile.rs2
-rw-r--r--src/cache/mod.rs182
-rw-r--r--src/client/bridge/gateway/mod.rs9
-rw-r--r--src/client/bridge/gateway/shard_manager_monitor.rs2
-rw-r--r--src/client/bridge/gateway/shard_messenger.rs39
-rw-r--r--src/client/bridge/gateway/shard_runner_message.rs2
-rw-r--r--src/client/bridge/mod.rs2
-rw-r--r--src/client/context.rs21
-rw-r--r--src/client/dispatch.rs71
-rw-r--r--src/client/mod.rs23
-rw-r--r--src/constants.rs2
-rw-r--r--src/framework/standard/args.rs717
-rw-r--r--src/framework/standard/command.rs19
-rw-r--r--src/framework/standard/configuration.rs120
-rw-r--r--src/framework/standard/help_commands.rs4
-rw-r--r--src/framework/standard/mod.rs91
-rw-r--r--src/gateway/mod.rs21
-rw-r--r--src/gateway/shard.rs13
-rw-r--r--src/http/mod.rs1559
-rw-r--r--src/http/ratelimiting.rs286
-rw-r--r--src/http/request.rs116
-rw-r--r--src/http/routing.rs1433
-rw-r--r--src/internal/macros.rs41
-rw-r--r--src/lib.rs25
-rw-r--r--src/model/application.rs2
-rw-r--r--src/model/channel/attachment.rs4
-rw-r--r--src/model/channel/channel_category.rs2
-rw-r--r--src/model/channel/channel_id.rs98
-rw-r--r--src/model/channel/embed.rs2
-rw-r--r--src/model/channel/group.rs52
-rw-r--r--src/model/channel/guild_channel.rs87
-rw-r--r--src/model/channel/message.rs57
-rw-r--r--src/model/channel/mod.rs204
-rw-r--r--src/model/channel/private_channel.rs46
-rw-r--r--src/model/channel/reaction.rs30
-rw-r--r--src/model/error.rs22
-rw-r--r--src/model/event.rs14
-rw-r--r--src/model/gateway.rs66
-rw-r--r--src/model/guild/emoji.rs5
-rw-r--r--src/model/guild/guild_id.rs115
-rw-r--r--src/model/guild/member.rs35
-rw-r--r--src/model/guild/mod.rs213
-rw-r--r--src/model/guild/partial_guild.rs64
-rw-r--r--src/model/guild/role.rs8
-rw-r--r--src/model/id.rs2
-rw-r--r--src/model/invite.rs38
-rw-r--r--src/model/misc.rs84
-rw-r--r--src/model/permissions.rs182
-rw-r--r--src/model/user.rs99
-rw-r--r--src/model/webhook.rs14
-rw-r--r--src/utils/colour.rs68
-rw-r--r--src/utils/message_builder.rs78
-rw-r--r--src/utils/mod.rs53
-rw-r--r--src/voice/audio.rs1
-rw-r--r--src/voice/connection.rs7
-rw-r--r--tests/resources/guild_-1_role_position.json271
-rw-r--r--tests/resources/role_-1_position.json10
-rw-r--r--tests/test_args.rs432
-rw-r--r--tests/test_cache.rs174
-rw-r--r--tests/test_channels.rs91
-rw-r--r--tests/test_colour.rs53
-rw-r--r--tests/test_create_embed.rs92
-rw-r--r--tests/test_decode_role.rs302
-rw-r--r--tests/test_deser.rs29
-rw-r--r--tests/test_formatters.rs80
-rw-r--r--tests/test_guild.rs103
-rw-r--r--tests/test_http.rs18
-rw-r--r--tests/test_message.rs29
-rw-r--r--tests/test_msg_builder.rs77
-rw-r--r--tests/test_parsers.rs41
-rw-r--r--tests/test_user.rs66
-rw-r--r--tests/test_utils.rs15
82 files changed, 5290 insertions, 3598 deletions
diff --git a/.travis.yml b/.travis.yml
index bd426a7..c3ea58c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,7 @@ rust:
- stable
- beta
- nightly
+ - 1.25.0
os:
- linux
- osx
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 223f832..45e2037 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,180 @@
All notable changes to this project will be documented in this file.
This project mostly adheres to [Semantic Versioning][semver].
+## [0.5.7] - 2018-08-09
+
+This is a hotfix release for an incorrect warning about cache deadlocking during
+event dispatches in the client and fixing some routing method typos due to the
+HTTP rewrite.
+
+Thanks to the following for their contributions:
+
+- [@acdenisSK]
+- [@Lymia]
+- [@zeyla]
+
+### Fixed
+
+- [client] Fix erroneous deadlock detection messages ([@Lymia]) [c:d1266fc]
+- [http] Fix some routing issues ([@zeyla]) [c:04b410e]
+
+### Misc.
+
+- Slightly reword a cache update comment ([@acdenisSK]) [c:3a58090]
+
+## [0.5.6] - 2018-08-07
+
+This is a bugfix release that fixes a long-standing bug causing shards to
+randomly die under certain rare conditions when dispatching the Ready event,
+and compilation of the `cache` and `client` features without the `framework`
+feature. This also contains an internal rewrite of the HTTP module.
+
+The minimum required rustc version is now pinned at 1.25.0.
+
+Thanks to the following for their contributions:
+
+- [@acdenisSK]
+- [@Erk-]
+- [@Lakelezz]
+- [@Mishio595]
+- [@Roughsketch]
+- [@zeyla]
+
+### Upgrade Path
+
+Per [c:01e3c33], `Context::edit_profile` has been deprecated. Call
+`serenity::http::edit_profile` instead.
+
+### Added
+
+- [model] `impl AsRef<MessageId> for Message` ([@Mishio595]) [c:1de3937]
+- [model] Add `From` impls for `Game`, genericify `Game` params ([@zeyla])
+ [c:e1332a5], [c:a4c3fec]
+- [http] Make `http::fire`, `http::request` public ([@zeyla]) [c:0d55363]
+- [framework] Add no-parse getters and advancer to `Args` ([@acdenisSK])
+ [c:73ab20f]
+- [model] Add support for new `PRIORITY_SPEAKER` permission ([@Erk-])
+ [c:2179623]
+
+### Fixed
+
+- [client] Don't delay Ready event with cache enabled ([@zeyla]) [c:12d5321]
+- [framework] Handle no delimiters in `Args` ([@acdenisSK]) [c:e5ea6c1],
+ [c:9568e3b]
+- [client] Add missing `mut`, fixing no-framework compilation ([@acdenisSK])
+ [c:90c7ec4]
+- [framework] Check if message is empty in `Args` ([@acdenisSK]) [c:0501020]
+- [client] Fix potential cache deadlocking when dispatching ([@zeyla])
+ [c:f064d65]
+- [framework] Pass failed sub-command to default command ([@Lakelezz])
+ [c:db21036]
+- [framework] Fix default command upon shortcut prefix ([@Lakelezz]) [c:8f128b2]
+
+### Changed
+
+- [client] Deprecate `Context::edit_profile` ([@zeyla]) [c:01e3c33]
+
+### Misc.
+
+- [model] Fix `ChannelId::send_message`'s dead links ([@acdenisSK]) [c:7a93557]
+- [model] Add note about cache in `UserId::get` docs ([@zeyla]) [c:e2873c8]
+- [general] Reduce required rustc to 1.25.0 ([@zeyla]) [c:f3f22d7], [c:b324774]
+- [model] Make `GuildId::member` use cache when possible ([@Roughsketch])
+ [c:21eb42f]
+- [framework] Reword some `StandardFramework::complex_bucket` docs
+ ([@acdenisSK]) [c:02de778]
+- [framework] Internally refactor `positions` ([@acdenisSK]) [c:2a6c3b1]
+- [framework] Update `Configuration` default value listings ([@zeyla])
+ [c:602c5a7]
+- [http] Maintain a single, re-used HTTP client ([@zeyla]) [c:8c0e5a3]
+- [http] Redo the HTTP module internally ([@zeyla]) [c:a0b0dd2], [c:4648f58],
+ [c:8918201], [c:8301333], [c:bbbf638], [c:9a863bd], [c:c458099], [c:aa437d4]
+- [docs] Don't return Result from tests ([@acdenisSK]) [c:e290b03]
+- [docs] Fix all dead links in permissions ([@Erk-]) [c:869fff5]
+
+## [0.5.5] - 2018-07-25
+
+This release is mostly a bugfix release. Thanks to the following for their
+contributions:
+
+- [@acdenisSK]
+- [@drklee3]
+- [@foxbot]
+- [@Lakelezz]
+- [@Mishio595]
+- [@perryprog]
+- [@TheUnitedStatesOfAmerica]
+- [@zeyla]
+
+### Added
+
+- [framework] Add `Args::rest` ([@acdenisSK]) [c:9b2cd75]
+- [model] Add `Message::guild_id` structfield ([@foxbot], [@zeyla]) [c:a9e8626],
+ [c:3121f90]
+- [framework] Improve logic for displaying help ([@Lakelezz]) [c:7937025]
+- [http] Add `http::ratelimiting::offset` ([@zeyla]) [c:55555b8]
+- [cache] Make the Cache Update API public ([@zeyla]) [c:9e56062]
+- [utils] Add associated consts in `utils::Colour` ([@zeyla]) [c:bbfc8e2]
+- [model] `impl From<&ID> for ID` for all Id types ([@zelya]) [c:9e45642],
+ [c:530ea76]
+- [cache] Add a Message cache API ([@zeyla]) [c:e602630]
+- [voice] Add `streamer::ffmpeg_optioned` ([@zeyla]) [c:5dab87b], [c:1f3a57e]
+- [model] Implement Mentionable for `GuildChannel` ([@Mishio595]) [c:ce8da79]
+- [framework] Allow nil prefixes in DMs ([@acdenisSK]) [c:10bbffe]
+- [model] Implement `Mentionable` for `ChannelCategory`, `Group`,
+ `PrivateChannel` ([@zeyla]) [c:dd3744b], [c:8ce8234], [c:d11d916], [c:5abc7d1]
+- [framework] Add checks for groups ([@Lakelezz]) [c:29480e5]
+- [framework] Support multiple prefixes for command groups ([@Lakelezz])
+ [c:305d200]
+- [framework] Add default commands for command groups ([@Lakelezz]) [c:40c8248],
+ [c:8aefde0]
+
+### Fixed
+
+- [framework] Handle debug impls better ([@acdenisSK]) [c:caeab28], [c:7eac4d5]
+- [framework] Reorder some dispatch checks to fix an owner override bug
+ ([@acdenisSK]) [c:8114a7a], [c:93f453b]
+- [framework] Force `Args::find{,_n}` to be quote-aware ([@acdenisSK])
+ [c:f0f06b7]
+- [framework] Fix an `Args` test ([@zeyla]) [c:2ef660e]
+- [framework] Fix command visibility on no help ([@Lakelezz]) [c:aeb89af]
+- [framework] Add missing `Send + Sync` bounds on `Check` ([@acdenisSK])
+ [c:f09b661]
+- [utils] Fix `utils::is_nsfw` slicing ([@acdenisSK], [@zeyla]) [c:0067c33],
+ [c:ccd2506]
+- [utils] Fix `nsfw-` case in `utils::is_nsfw` ([@zeyla]) [c:bd4aa0a]
+- [framework] Don't assume all characters at end are 1-length ([@acdenisSK])
+ [c:4e4dcb1]
+- [framework] Don't suggest command if no command is related to input
+ ([@Lakelezz]) [c:614402f]
+
+### Changed
+
+- [model] Make `Invite::guild` and `RichInvite::guild` optional ([@zeyla])
+ [c:3a647e3]
+
+### Misc.
+
+- [framework] Fix example typo ([@perryprog]) [c:d0d363f]
+- [framework] Add more docs to `Args` ([@acdenisSK]) [c:04b0be1]
+- [general] Fix extraneous spaces at the end of lines ([@zeyla]) [c:6ddfef8]
+- [http] Add (late) april fool's functions ([@TheUnitedStatesOfAmerica])
+ [c:5ffdcea]
+- Rename https://github.com/serenity-rs/serenity/commit/6e1edde4a3fe27d0d90db7ea906ca5f115a2d5fb
+- [framework] Remove some repitition repition ([@acdenisSK]) [c:10f7548],
+ [c:1ec1086]
+- [docs] Add more docs to `CreateEmbed::fields` ([@acdenisSK]) [c:703d135]
+- [docs] Remove some dead links ([@acdenisSK], [@Lakelezz]) [c:eae624e],
+ [c:4cf83d0]
+- [docs] Remove old notice about `CreateEmbed::field` ([@acdenisSK]) [c:5b66ace]
+- [examples] Add `CreateEmbed::field` and `CreateEmbed::fields` usage to example
+ 11 ([@drklee3]) [c:a9a2c27]
+- [general] Monomorphize all functions ([@zeyla]) [c:7b9764c]
+- [general] Update README logo URI ([@zeyla]) [c:2ff765b]
+- [docs] Fix doc links with no anchor ([@zeyla]) [c:0d6e019]
+- [docs] Add docs for `Args::new` ([@acdenisSK]) [c:b520ec7]
+- [general] Fix some clippy lints ([@zeyla]) [c:9da7669]
+
## [0.5.4] - 2018-06-07
Thanks to the following for their contributions:
@@ -2076,6 +2250,9 @@ rest::get_guilds(GuildPagination::After(GuildId(777)), 50);
Initial commit.
+[0.5.7]: https://github.com/serenity-rs/serenity/compare/v0.5.6...v0.5.7
+[0.5.6]: https://github.com/serenity-rs/serenity/compare/v0.5.5...v0.5.6
+[0.5.5]: https://github.com/serenity-rs/serenity/compare/v0.5.4...v0.5.5
[0.5.4]: https://github.com/serenity-rs/serenity/compare/v0.5.3...v0.5.4
[0.5.3]: https://github.com/serenity-rs/serenity/compare/v0.5.2...v0.5.3
[0.5.2]: https://github.com/serenity-rs/serenity/compare/v0.5.1...v0.5.2
@@ -2113,6 +2290,7 @@ Initial commit.
[@ConcurrentMarxistGC]: https://github.com/ConcurrentMarxistGC
[@DeltaEvo]: https://github.com/DeltaEvo
[@drklee3]: https://github.com/drklee3
+[@Erk-]: https://github.com/Erk-
[@eLunate]: https://github.com/eLunate
[@emoticon]: https://github.com/emoticon
[@efyang]: https://github.com/efyang
@@ -2133,8 +2311,10 @@ Initial commit.
[@joek13]: https://github.com/joek13
[@Lakelezz]: https://github.com/Lakelezz
[@lolzballs]: https://github.com/lolzballs
+[@Lymia]: https://github.com/Lymia
[@khazhyk]: https://github.com/khazhyk
[@megumisonoda]: https://github.com/megumisonoda
+[@Mishio595]: https://github.com/Mishio595
[@MOZGIII]: https://github.com/MOZGIII
[@nabijaczleweli]: https://github.com/nabijaczleweli
[@perryprog]: https://github.com/perryprog
@@ -2144,12 +2324,105 @@ Initial commit.
[@SunDwarf]: https://github.com/SunDwarf
[@tahahawa]: https://github.com/tahahawa
[@ThatsNoMoon]: https://github.com/ThatsNoMoon
+[@TheUnitedStatesOfAmerica]: https://github.com/TheUnitedStatesOfAmerica
[@thelearnerofcode]: https://github.com/thelearnerofcode
[@timotree3]: https://github.com/timotree3
[@xentec]: https://github.com/xentec
[@vityafx]: https://github.com/vityafx
[@zeyla]: https://github.com/zeyla
+[c:04b410e]: https://github.com/serenity-rs/serenity/commit/04b410ee75b2eb29f32e66fc137d3992a4972f1d
+[c:3a58090]: https://github.com/serenity-rs/serenity/commit/3a580909c489c328f3faa10741debd4b063e7fbd
+[c:d1266fc]: https://github.com/serenity-rs/serenity/commit/d1266fc3051a436f87a4778c5081c2228eb50b1c
+
+[c:01e3c33]: https://github.com/serenity-rs/serenity/commit/01e3c331ed188e2b95bafa2fa0fc63d5c0c03905
+[c:02de778]: https://github.com/serenity-rs/serenity/commit/02de7789d72141434264e8bd7cee7e1fc65a043f
+[c:0501020]: https://github.com/serenity-rs/serenity/commit/05010204eaded91b29aef0561fc8fb668b522760
+[c:0d55363]: https://github.com/serenity-rs/serenity/commit/0d553630c1a9da216e42e7c0a9bedaccfedf678d
+[c:12d5321]: https://github.com/serenity-rs/serenity/commit/12d53214f39211a4c02026d9389b9aa2bfa8a5ee
+[c:1de3937]: https://github.com/serenity-rs/serenity/commit/1de39377a2e428f9652d887627f420349337c5b1
+[c:2179623]: https://github.com/serenity-rs/serenity/commit/2179623ebf12f7d8e16cc87e193ecd4de0f7b1fe
+[c:21eb42f]: https://github.com/serenity-rs/serenity/commit/21eb42f96f9721d4e004dbc70aedf60e6d1ae7c4
+[c:2a6c3b1]: https://github.com/serenity-rs/serenity/commit/2a6c3b1d1e24ec7dc3b1f19baf87594e362ded27
+[c:4648f58]: https://github.com/serenity-rs/serenity/commit/4648f58e8ddc878d06a5a4a1d2840180c359ddf0
+[c:602c5a7]: https://github.com/serenity-rs/serenity/commit/602c5a7b78dda42b9c3d5426c39099d48e74bca5
+[c:73ab20f]: https://github.com/serenity-rs/serenity/commit/73ab20f271c9cc6dadb7bb76938ae64d19cee71e
+[c:7a93557]: https://github.com/serenity-rs/serenity/commit/7a935574ffe0b7d19c1ed5c5befe1b7e7e4f0e0d
+[c:8301333]: https://github.com/serenity-rs/serenity/commit/830133377a5832784c311302e543f86f85194e3b
+[c:869fff5]: https://github.com/serenity-rs/serenity/commit/869fff566ca7a3669f7f08461a6bd481af3649d3
+[c:8918201]: https://github.com/serenity-rs/serenity/commit/891820102ff7b9025c67e03ac59f5ecd75959aac
+[c:8c0e5a3]: https://github.com/serenity-rs/serenity/commit/8c0e5a377ad7db3c40e37740123c0ebf3d7e36ae
+[c:8f128b2]: https://github.com/serenity-rs/serenity/commit/8f128b2c041d5f708378082af3653ff1ee2df919
+[c:90c7ec4]: https://github.com/serenity-rs/serenity/commit/90c7ec45d6cc01b25296de9619b7d3a6288244fe
+[c:9568e3b]: https://github.com/serenity-rs/serenity/commit/9568e3b24816bb180740789d1e30c29f3658dc8b
+[c:9a863bd]: https://github.com/serenity-rs/serenity/commit/9a863bd78e8edc5849e56e979888f1191b1d5845
+[c:a0b0dd2]: https://github.com/serenity-rs/serenity/commit/a0b0dd226f9ad2476729fa79dbc680bd08aa44b3
+[c:a4c3fec]: https://github.com/serenity-rs/serenity/commit/a4c3fec493d3b85ad1b43f3a5c4927d0d5cdc717
+[c:aa437d4]: https://github.com/serenity-rs/serenity/commit/aa437d4dbc4a59ffa65f80c7eafa6efc37eedc86
+[c:b324774]: https://github.com/serenity-rs/serenity/commit/b3247749f745c524b1eb0f44118c8358868e722a
+[c:bbbf638]: https://github.com/serenity-rs/serenity/commit/bbbf63868a8ef3c0f21c1896f7afb96f4d8fbcc1
+[c:c458099]: https://github.com/serenity-rs/serenity/commit/c45809973f9ed333d9c13905a376af14a73d920b
+[c:db21036]: https://github.com/serenity-rs/serenity/commit/db210367f3752d8e8ad018742ea0b590ddc54009
+[c:e1332a5]: https://github.com/serenity-rs/serenity/commit/e1332a54af46eff6051097ff4989c8d0fde4ca37
+[c:e2873c8]: https://github.com/serenity-rs/serenity/commit/e2873c820c1134ea7cc4cfbe99467aac350fa892
+[c:e290b03]: https://github.com/serenity-rs/serenity/commit/e290b038242cec6d4465f96c22cff24578f1a068
+[c:e5ea6c1]: https://github.com/serenity-rs/serenity/commit/e5ea6c176ba96988efc612a8e14eea90f9c293e1
+[c:f064d65]: https://github.com/serenity-rs/serenity/commit/f064d65486d0c8a3c510ee398e7d0bbf6b283bdb
+[c:f3f22d7]: https://github.com/serenity-rs/serenity/commit/f3f22d7e072477028c9853d467dd18cf50e1589f
+
+[c:0067c33]: https://github.com/serenity-rs/serenity/commit/0067c3335929325f54a3a0fe3693703e16de219c
+[c:04b0be1]: https://github.com/serenity-rs/serenity/commit/04b0be18b101186d618f9593fc8d2569ee845487
+[c:0d6e019]: https://github.com/serenity-rs/serenity/commit/0d6e019c258a8f2e743bcab196acab50b01e3958
+[c:10bbffe]: https://github.com/serenity-rs/serenity/commit/10bbffe9332edf8b8835d98cfffb8ec411162145
+[c:10f7548]: https://github.com/serenity-rs/serenity/commit/10f7548d4d57864b599dd7a760d2609144a2ec63
+[c:1ec1086]: https://github.com/serenity-rs/serenity/commit/1ec1086026971c903858128a8d38c5143f3f0f6f
+[c:1f3a57e]: https://github.com/serenity-rs/serenity/commit/1f3a57eb6c0a1419614927d52bd3e798db36b043
+[c:29480e5]: https://github.com/serenity-rs/serenity/commit/29480e5eeccc12afc0e9020373647786736aabc7
+[c:2ef660e]: https://github.com/serenity-rs/serenity/commit/2ef660e34c4cca96ec30049e42c79e899c573be0
+[c:2ff765b]: https://github.com/serenity-rs/serenity/commit/2ff765bbe74e2dc36a6c0c221c7ab06aac74462a
+[c:305d200]: https://github.com/serenity-rs/serenity/commit/305d2008216b5351d9fdd357381027ea42f4740b
+[c:3121f90]: https://github.com/serenity-rs/serenity/commit/3121f90a9f98e82fab48d62cf95cd316ae9f0496
+[c:3a647e3]: https://github.com/serenity-rs/serenity/commit/3a647e3b7f6762fa6a078bc539e5b3e8012b37d4
+[c:40c8248]: https://github.com/serenity-rs/serenity/commit/40c8248d107b3c6cad785502e6d619669aba6431
+[c:4cf83d0]: https://github.com/serenity-rs/serenity/commit/4cf83d0d6b2a4fe156d3c54c06db4ce32293efb0
+[c:4e4dcb1]: https://github.com/serenity-rs/serenity/commit/4e4dcb11586520f798c831956dc42778c0205386
+[c:530ea76]: https://github.com/serenity-rs/serenity/commit/530ea76cfd05ffa64a826e6afa342860c730fd00
+[c:55555b8]: https://github.com/serenity-rs/serenity/commit/55555b88dd44366e27d2c7cc02166995a3835a69
+[c:5abc7d1]: https://github.com/serenity-rs/serenity/commit/5abc7d1d7fe7130e73e4848c6333627d9881cb9e
+[c:5dab87b]: https://github.com/serenity-rs/serenity/commit/5dab87b0ff0097eb78abc1089c6a51ea05aa2273
+[c:5b66ace]: https://github.com/serenity-rs/serenity/commit/5b66ace77b55c3d7272aab9b49db919c180ec33f
+[c:5ffdcea]: https://github.com/serenity-rs/serenity/commit/5ffdceafcbc75947365004107e640783ec033335
+[c:614402f]: https://github.com/serenity-rs/serenity/commit/614402f7b963a713bfa98bc5b1cfa968e8d6c103
+[c:6ddfef8]: https://github.com/serenity-rs/serenity/commit/6ddfef8359a619be9a49be7b33b466724eed0ecb
+[c:703d135]: https://github.com/serenity-rs/serenity/commit/703d13564f9081839eb77e4e4699d711b1de895a
+[c:7937025]: https://github.com/serenity-rs/serenity/commit/7937025a484955cc8d74fb10004ba8b49dcc2bb0
+[c:7b9764c]: https://github.com/serenity-rs/serenity/commit/7b9764cf1097b0620d871fabe67b5593f0cd4a4a
+[c:7eac4d5]: https://github.com/serenity-rs/serenity/commit/7eac4d5fcf6c16db64e118de3d69825909979d5b
+[c:8114a7a]: https://github.com/serenity-rs/serenity/commit/8114a7ace3ad51b9903a6017993aa526742bd72d
+[c:8aefde0]: https://github.com/serenity-rs/serenity/commit/8aefde08465a050ad7bae12e6003fe514f43af5f
+[c:8ce8234]: https://github.com/serenity-rs/serenity/commit/8ce82346846f235357b8dc53cb3ff399e70fcb4a
+[c:93f453b]: https://github.com/serenity-rs/serenity/commit/93f453b07b9e8f813e6bfb0ddd2648a8e626d136
+[c:9b2cd75]: https://github.com/serenity-rs/serenity/commit/9b2cd75baf1fa7ee063f47e966ee3f6566a6d45c
+[c:9da7669]: https://github.com/serenity-rs/serenity/commit/9da766976929417c4b8f487f8ec05b6f8b3f43ef
+[c:9e45642]: https://github.com/serenity-rs/serenity/commit/9e456427ccd496c4128bde841df0c0af7a262047
+[c:9e56062]: https://github.com/serenity-rs/serenity/commit/9e560628deb1cf66e0c5029f41a79404fadffb40
+[c:a9a2c27]: https://github.com/serenity-rs/serenity/commit/a9a2c27d7aefa6061dd9ca58a96c5ba617a78a6a
+[c:a9e8626]: https://github.com/serenity-rs/serenity/commit/a9e8626c4cd642087f828c5b32481bee9e4d368b
+[c:aeb89af]: https://github.com/serenity-rs/serenity/commit/aeb89af4eff59bb3ea9eb7623685bf7ad7520496
+[c:b520ec7]: https://github.com/serenity-rs/serenity/commit/b520ec708c375e09838b9f25fd285790b856bb97
+[c:bbfc8e2]: https://github.com/serenity-rs/serenity/commit/bbfc8e2d0250f41d5bf4230b6efb428419133de8
+[c:bd4aa0a]: https://github.com/serenity-rs/serenity/commit/bd4aa0aabda4a2986e6145e3a793e8b2a391f8dd
+[c:caeab28]: https://github.com/serenity-rs/serenity/commit/caeab28059d029a92b784f3b5ae1f79c412c8404
+[c:ccd2506]: https://github.com/serenity-rs/serenity/commit/ccd250649665b1726b0ca852b2375c113da6ed57
+[c:ce8da79]: https://github.com/serenity-rs/serenity/commit/ce8da793d3142cb001d9b155ff4224c15fe833ce
+[c:d0d363f]: https://github.com/serenity-rs/serenity/commit/d0d363fb2a3475c68d40b02ec22ab728059fd55e
+[c:d11d916]: https://github.com/serenity-rs/serenity/commit/d11d916a94b8a96fde218db4550d6c2428b4bc2a
+[c:dd3744b]: https://github.com/serenity-rs/serenity/commit/dd3744b08887debba0d44fd0bceddef5f8ed1356
+[c:e602630]: https://github.com/serenity-rs/serenity/commit/e6026308b33c80aa33f0001c89cd271cc5cb6687
+[c:eae624e]: https://github.com/serenity-rs/serenity/commit/eae624e3f18681971a654c95624d917afe00695a
+[c:f09b661]: https://github.com/serenity-rs/serenity/commit/f09b661be9085c7525a6c9f6929b50deebffae9b
+[c:f0f06b7]: https://github.com/serenity-rs/serenity/commit/f0f06b7d3b890d2ddcb84e00b3f62e195da80090
+
[c:0324e01]: https://github.com/serenity-rs/serenity/commit/0324e011f1ea0eed0709c92fe86319c812a42206
[c:08a7110]: https://github.com/serenity-rs/serenity/commit/08a71106748e356d2618e48d8797e6da60d7eb54
[c:0e1e8fb]: https://github.com/serenity-rs/serenity/commit/0e1e8fbbe564c23530a709a7ec407b08f63944e2
diff --git a/Cargo.toml b/Cargo.toml
index c47ebbd..777701f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-authors = ["alex <[email protected]>", "Zeyla Hellyer <[email protected]>"]
+authors = ["alex <[email protected]>", "Zeyla Hellyer <[email protected]>"]
description = "A Rust library for the Discord API."
documentation = "https://docs.rs/serenity"
homepage = "https://github.com/serenity-rs/serenity"
@@ -8,7 +8,7 @@ license = "ISC"
name = "serenity"
readme = "README.md"
repository = "https://github.com/serenity-rs/serenity.git"
-version = "0.5.4"
+version = "0.5.7"
[dependencies]
bitflags = "^1.0"
diff --git a/LICENSE.md b/LICENSE.md
index f6f664c..276b01b 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,6 +1,6 @@
ISC License (ISC)
-Copyright (c) 2016, Zeyla Hellyer <[email protected]>
+Copyright (c) 2016, Zeyla Hellyer <[email protected]>
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
diff --git a/README.md b/README.md
index d1417a9..ee63c4b 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[![ci-badge][]][ci] [![docs-badge][]][docs] [![guild-badge][]][guild]
+[![ci-badge][]][ci] [![docs-badge][]][docs] [![guild-badge][]][guild] [![crates.io version]][crates.io link] [![rust 1.25+ badge]][rust 1.25+ link]
# serenity
@@ -89,7 +89,7 @@ and to the top of your `main.rs`:
#[macro_use] extern crate serenity;
```
-Serenity only supports the _latest_ Stable, Beta, and Nightly.
+Serenity supports a minimum of Rust 1.25.
# Features
@@ -103,6 +103,9 @@ features = ["pick", "your", "feature", "names", "here"]
version = "0.5"
```
+The default features are: `builder`, `cache`, `client`, `framework`, `gateway`,
+`http`, `model`, `standard_framework`, and `utils`.
+
The following is a full list of features:
- **builder**: The builders used in conjunction with models' methods.
@@ -124,6 +127,25 @@ the HTTP functions.
- **voice**: Enables compilation of voice support, so that voice channels can be
connected to and audio can be sent/received.
+If you want all of the default features except for `cache` for example, you can
+list all but that:
+
+```toml
+[dependencies.serenity]
+default-features = false
+features = [
+ "builder",
+ "client",
+ "framework",
+ "gateway",
+ "http",
+ "model",
+ "standard_framework",
+ "utils",
+]
+version = "0.5"
+```
+
# Dependencies
Serenity requires the following dependencies:
@@ -167,15 +189,17 @@ Voice+youtube-dl:
[`validate_token`]: https://docs.rs/serenity/*/serenity/client/fn.validate_token.html
[cache docs]: https://docs.rs/serenity/*/serenity/cache/index.html
[ci]: https://travis-ci.org/serenity-rs/serenity
-[ci-badge]: https://travis-ci.org/serenity-rs/serenity.svg?branch=master
+[ci-badge]: https://img.shields.io/travis/serenity-rs/serenity.svg?style=flat-square
[client's module-level documentation]: https://docs.rs/serenity/*/serenity/client/index.html
+[crates.io link]: https://crates.io/crates/serenity
+[crates.io version]: https://img.shields.io/crates/v/serenity.svg?style=flat-square
[discord docs]: https://discordapp.com/developers/docs/intro
[docs]: https://docs.rs/serenity
-[docs-badge]: https://img.shields.io/badge/docs-online-5023dd.svg
+[docs-badge]: https://img.shields.io/badge/docs-online-5023dd.svg?style=flat-square
[examples]: https://github.com/serenity-rs/serenity/tree/master/examples
[gateway docs]: https://docs.rs/serenity/*/serenity/gateway/index.html
[guild]: https://discord.gg/WBdGJCc
-[guild-badge]: https://discordapp.com/api/guilds/381880193251409931/widget.png
+[guild-badge]: https://img.shields.io/discord/381880193251409931.svg?style=flat-square&colorB=7289DA
[library:Discord.net]: https://github.com/RogueException/Discord.Net
[library:JDA]: https://github.com/DV8FromTheWorld/JDA
[library:disco]: https://github.com/b1naryth1ef/disco
@@ -183,3 +207,5 @@ Voice+youtube-dl:
[library:discord.js]: https://github.com/hydrabolt/discord.js
[library:discord.py]: https://github.com/Rapptz/discord.py
[logo]: https://raw.githubusercontent.com/serenity-rs/serenity/master/logo.png
+[rust 1.25+ badge]: https://img.shields.io/badge/rust-1.25+-93450a.svg?style=flat-square
+[rust 1.25+ link]: https://blog.rust-lang.org/2018/03/29/Rust-1.25.html
diff --git a/examples/05_command_framework/src/main.rs b/examples/05_command_framework/src/main.rs
index be8ff41..a5494f0 100644
--- a/examples/05_command_framework/src/main.rs
+++ b/examples/05_command_framework/src/main.rs
@@ -168,10 +168,12 @@ fn main() {
.bucket("complicated")
.cmd(commands))
.group("Emoji", |g| g
- // Sets a single prefix for a group:
- .prefix("emoji")
+ // Sets multiple prefixes for a group.
+ // This requires us to call commands in this group
+ // via `~emoji` (or `~e`) instead of just `~`.
+ .prefixes(vec!["emoji", "em"])
// Sets a command that will be executed if only a group-prefix was passed.
- .default_cmd(dog)
+ .default_cmd(bird)
.command("cat", |c| c
.desc("Sends an emoji with a cat.")
.batch_known_as(vec!["kitty", "neko"]) // Adds multiple aliases
@@ -184,10 +186,10 @@ fn main() {
.bucket("emoji")
.cmd(dog)))
.group("Math", |g| g
- // Sets multiple prefixes for a group.
- // This requires us to call commands in this group
- // via `~math` (or `~m`) instead of just `~`.
- .prefixes(vec!["m", "math"])
+ // Sets a single prefix for this group.
+ // So one has to call commands in this group
+ // via `~math` instead of just `~`.
+ .prefix("math")
.command("multiply", |c| c
.known_as("*") // Lets us also call `~math *` instead of just `~math multiply`.
.cmd(multiply)))
@@ -204,7 +206,7 @@ fn main() {
.group("Owner", |g| g
// This check applies to every command on this group.
// User needs to pass the test for the command to execute.
- .check(admin_check)
+ .check(admin_check)
.command("am i admin", |c| c
.cmd(am_i_admin))
.guild_only(true)
@@ -249,7 +251,7 @@ fn owner_check(_: &mut Context, msg: &Message, _: &mut Args, _: &CommandOptions)
// A function which acts as a "check", to determine whether to call a command.
//
-// This check analyses whether a guild member permissions has
+// This check analyses whether a guild member permissions has
// administrator-permissions.
fn admin_check(_: &mut Context, msg: &Message, _: &mut Args, _: &CommandOptions) -> bool {
if let Some(member) = msg.member() {
@@ -381,3 +383,15 @@ command!(cat(_ctx, msg, _args) {
println!("Error sending message: {:?}", why);
}
});
+
+command!(bird(_ctx, msg, args) {
+ let say_content = if args.is_empty() {
+ ":bird: can find animals for you.".to_string()
+ } else {
+ format!(":bird: could not find animal named: `{}`.", args.full())
+ };
+
+ if let Err(why) = msg.channel_id.say(say_content) {
+ println!("Error sending message: {:?}", why);
+ }
+});
diff --git a/src/builder/create_embed.rs b/src/builder/create_embed.rs
index 7604386..35172e1 100644
--- a/src/builder/create_embed.rs
+++ b/src/builder/create_embed.rs
@@ -515,3 +515,92 @@ impl<'a, Tz: TimeZone> From<&'a DateTime<Tz>> for Timestamp
}
}
}
+
+#[cfg(test)]
+mod test {
+ use model::channel::{Embed, EmbedField, EmbedFooter, EmbedImage, EmbedVideo};
+ use serde_json::Value;
+ use super::CreateEmbed;
+ use utils::{self, Colour};
+
+ #[test]
+ fn test_from_embed() {
+ let embed = Embed {
+ author: None,
+ colour: Colour::new(0xFF0011),
+ description: Some("This is a test description".to_string()),
+ fields: vec![
+ EmbedField {
+ inline: false,
+ name: "a".to_string(),
+ value: "b".to_string(),
+ },
+ EmbedField {
+ inline: true,
+ name: "c".to_string(),
+ value: "z".to_string(),
+ },
+ ],
+ footer: Some(EmbedFooter {
+ icon_url: Some("https://i.imgur.com/XfWpfCV.gif".to_string()),
+ proxy_icon_url: None,
+ text: "This is a hakase footer".to_string(),
+ }),
+ image: Some(EmbedImage {
+ height: 213,
+ proxy_url: "a".to_string(),
+ url: "https://i.imgur.com/XfWpfCV.gif".to_string(),
+ width: 224,
+ }),
+ kind: "rich".to_string(),
+ provider: None,
+ thumbnail: None,
+ timestamp: None,
+ title: Some("hakase".to_string()),
+ url: Some("https://i.imgur.com/XfWpfCV.gif".to_string()),
+ video: Some(EmbedVideo {
+ height: 213,
+ url: "https://i.imgur.com/XfWpfCV.mp4".to_string(),
+ width: 224,
+ }),
+ };
+
+ let builder = CreateEmbed::from(embed)
+ .colour(0xFF0011)
+ .description("This is a hakase description")
+ .image("https://i.imgur.com/XfWpfCV.gif")
+ .title("still a hakase")
+ .url("https://i.imgur.com/XfWpfCV.gif");
+
+ let built = Value::Object(utils::vecmap_to_json_map(builder.0));
+
+ let obj = json!({
+ "color": 0xFF0011,
+ "description": "This is a hakase description",
+ "title": "still a hakase",
+ "type": "rich",
+ "url": "https://i.imgur.com/XfWpfCV.gif",
+ "fields": [
+ {
+ "inline": false,
+ "name": "a",
+ "value": "b",
+ },
+ {
+ "inline": true,
+ "name": "c",
+ "value": "z",
+ },
+ ],
+ "image": {
+ "url": "https://i.imgur.com/XfWpfCV.gif",
+ },
+ "footer": {
+ "text": "This is a hakase footer",
+ "icon_url": "https://i.imgur.com/XfWpfCV.gif",
+ }
+ });
+
+ assert_eq!(built, obj);
+ }
+}
diff --git a/src/builder/create_invite.rs b/src/builder/create_invite.rs
index c9b00ff..c38924b 100644
--- a/src/builder/create_invite.rs
+++ b/src/builder/create_invite.rs
@@ -59,8 +59,8 @@ use utils::VecMap;
/// client.start().unwrap();
/// ```
///
-/// [`GuildChannel::create_invite`]: ../model/guild/struct.GuildChannel.html#method.create_invite
-/// [`RichInvite`]: ../model/guild/struct.Invite.html
+/// [`GuildChannel::create_invite`]: ../model/channel/struct.GuildChannel.html#method.create_invite
+/// [`RichInvite`]: ../model/invite/struct.RichInvite.html
#[derive(Clone, Debug)]
pub struct CreateInvite(pub VecMap<&'static str, Value>);
diff --git a/src/builder/edit_guild.rs b/src/builder/edit_guild.rs
index 28abda7..5db2051 100644
--- a/src/builder/edit_guild.rs
+++ b/src/builder/edit_guild.rs
@@ -10,7 +10,7 @@ use utils::VecMap;
///
/// [`Guild::edit`]: ../model/guild/struct.Guild.html#method.edit
/// [`Guild`]: ../model/guild/struct.Guild.html
-/// [Manage Guild]: ../model/permissions/constant.MANAGE_GUILD.html
+/// [Manage Guild]: ../model/permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[derive(Clone, Debug, Default)]
pub struct EditGuild(pub VecMap<&'static str, Value>);
diff --git a/src/builder/edit_member.rs b/src/builder/edit_member.rs
index c0791d6..0fba010 100644
--- a/src/builder/edit_member.rs
+++ b/src/builder/edit_member.rs
@@ -15,7 +15,7 @@ impl EditMember {
///
/// Requires the [Deafen Members] permission.
///
- /// [Deafen Members]: ../model/permissions/constant.DEAFEN_MEMBERS.html
+ /// [Deafen Members]: ../model/permissions/struct.Permissions.html#associatedconstant.DEAFEN_MEMBERS
pub fn deafen(mut self, deafen: bool) -> Self {
self.0.insert("deaf", Value::Bool(deafen));
@@ -26,7 +26,7 @@ impl EditMember {
///
/// Requires the [Mute Members] permission.
///
- /// [Mute Members]: ../model/permissions/constant.MUTE_MEMBERS.html
+ /// [Mute Members]: ../model/permissions/struct.Permissions.html#associatedconstant.MUTE_MEMBERS
pub fn mute(mut self, mute: bool) -> Self {
self.0.insert("mute", Value::Bool(mute));
@@ -38,7 +38,7 @@ impl EditMember {
///
/// Requires the [Manage Nicknames] permission.
///
- /// [Manage Nicknames]: ../model/permissions/constant.MANAGE_NICKNAMES.html
+ /// [Manage Nicknames]: ../model/permissions/struct.Permissions.html#associatedconstant.MANAGE_NICKNAMES
pub fn nickname(mut self, nickname: &str) -> Self {
self.0.insert("nick", Value::String(nickname.to_string()));
@@ -49,7 +49,7 @@ impl EditMember {
///
/// Requires the [Manage Roles] permission to modify.
///
- /// [Manage Roles]: ../model/permissions/constant.MANAGE_ROLES.html
+ /// [Manage Roles]: ../model/permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
pub fn roles<T: AsRef<RoleId>, It: IntoIterator<Item=T>>(self, roles: It) -> Self {
let roles = roles
.into_iter()
@@ -69,7 +69,7 @@ impl EditMember {
///
/// Requires the [Move Members] permission.
///
- /// [Move Members]: ../model/permissions/constant.MOVE_MEMBERS.html
+ /// [Move Members]: ../model/permissions/struct.Permissions.html#associatedconstant.MOVE_MEMBERS
#[inline]
pub fn voice_channel<C: Into<ChannelId>>(self, channel_id: C) -> Self {
self._voice_channel(channel_id.into())
diff --git a/src/builder/edit_profile.rs b/src/builder/edit_profile.rs
index df428b4..5bc0b92 100644
--- a/src/builder/edit_profile.rs
+++ b/src/builder/edit_profile.rs
@@ -45,7 +45,7 @@ impl EditProfile {
/// # client.start().unwrap();
/// ```
///
- /// [`utils::read_image`]: ../fn.read_image.html
+ /// [`utils::read_image`]: ../utils/fn.read_image.html
pub fn avatar(mut self, avatar: Option<&str>) -> Self {
let avatar = avatar.map_or(Value::Null, |x| Value::String(x.to_string()));
self.0.insert("avatar", avatar);
diff --git a/src/cache/mod.rs b/src/cache/mod.rs
index cf54d1a..0d4d069 100644
--- a/src/cache/mod.rs
+++ b/src/cache/mod.rs
@@ -13,7 +13,7 @@
//!
//! # Use by Models
//!
-//! Most models of Discord objects, such as the [`Message`], [`PublicChannel`],
+//! Most models of Discord objects, such as the [`Message`], [`GuildChannel`],
//! or [`Emoji`], have methods for interacting with that single instance. This
//! feature is only compiled if the `methods` feature is enabled. An example of
//! this is [`Guild::edit`], which performs a check to ensure that the current
@@ -37,7 +37,7 @@
//! [`Guild`]: ../model/guild/struct.Guild.html
//! [`Guild::edit`]: ../model/guild/struct.Guild.html#method.edit
//! [`Message`]: ../model/channel/struct.Message.html
-//! [`PublicChannel`]: ../model/channel/struct.PublicChannel.html
+//! [`GuildChannel`]: ../model/channel/struct.GuildChannel.html
//! [`Role`]: ../model/guild/struct.Role.html
//! [`CACHE`]: ../struct.CACHE.html
//! [`http`]: ../http/index.html
@@ -154,7 +154,6 @@ pub struct Cache {
/// - [`GuildMemberAdd`][`GuildMemberAddEvent`]
/// - [`GuildMemberRemove`][`GuildMemberRemoveEvent`]
/// - [`GuildMembersChunk`][`GuildMembersChunkEvent`]
- /// - [`GuildSync`][`GuildSyncEvent`]
/// - [`PresenceUpdate`][`PresenceUpdateEvent`]
/// - [`Ready`][`ReadyEvent`]
///
@@ -167,7 +166,6 @@ pub struct Cache {
/// [`GuildMemberRemoveEvent`]: ../model/event/struct.GuildMemberRemoveEvent.html
/// [`GuildMemberUpdateEvent`]: ../model/event/struct.GuildMemberUpdateEvent.html
/// [`GuildMembersChunkEvent`]: ../model/event/struct.GuildMembersChunkEvent.html
- /// [`GuildSyncEvent`]: ../model/event/struct.GuildSyncEvent.html
/// [`PresenceUpdateEvent`]: ../model/event/struct.PresenceUpdateEvent.html
/// [`ReadyEvent`]: ../model/event/struct.ReadyEvent.html
pub users: HashMap<UserId, Arc<RwLock<User>>>,
@@ -391,7 +389,7 @@ impl Cache {
/// The only advantage of this method is that you can pass in anything that
/// is indirectly a [`GuildId`].
///
- /// [`GuildId`]: ../model/guild/struct.GuildId.html
+ /// [`GuildId`]: ../model/id/struct.GuildId.html
///
/// # Examples
///
@@ -711,7 +709,7 @@ impl Cache {
/// The only advantage of this method is that you can pass in anything that
/// is indirectly a [`UserId`].
///
- /// [`UserId`]: ../model/user/struct.UserId.html
+ /// [`UserId`]: ../model/id/struct.UserId.html
/// [`users`]: #structfield.users
///
/// # Examples
@@ -802,3 +800,175 @@ impl Default for Cache {
}
}
}
+
+#[cfg(test)]
+mod test {
+ use chrono::DateTime;
+ use serde_json::{Number, Value};
+ use std::{
+ collections::HashMap,
+ sync::Arc,
+ };
+ use {
+ cache::{Cache, CacheUpdate, Settings},
+ model::prelude::*,
+ prelude::RwLock,
+ };
+
+ #[test]
+ fn test_cache_messages() {
+ let mut settings = Settings::new();
+ settings.max_messages(2);
+ let mut cache = Cache::new_with_settings(settings);
+
+ // Test inserting one message into a channel's message cache.
+ let datetime = DateTime::parse_from_str(
+ "1983 Apr 13 12:09:14.274 +0000",
+ "%Y %b %d %H:%M:%S%.3f %z",
+ ).unwrap();
+ let mut event = MessageCreateEvent {
+ message: Message {
+ id: MessageId(3),
+ attachments: vec![],
+ author: User {
+ id: UserId(2),
+ avatar: None,
+ bot: false,
+ discriminator: 1,
+ name: "user 1".to_owned(),
+ },
+ channel_id: ChannelId(2),
+ guild_id: Some(GuildId(1)),
+ content: String::new(),
+ edited_timestamp: None,
+ embeds: vec![],
+ kind: MessageType::Regular,
+ member: None,
+ mention_everyone: false,
+ mention_roles: vec![],
+ mentions: vec![],
+ nonce: Value::Number(Number::from(1)),
+ pinned: false,
+ reactions: vec![],
+ timestamp: datetime.clone(),
+ tts: false,
+ webhook_id: None,
+ },
+ };
+ // Check that the channel cache doesn't exist.
+ assert!(!cache.messages.contains_key(&event.message.channel_id));
+ // Add first message, none because message ID 2 doesn't already exist.
+ assert!(event.update(&mut cache).is_none());
+ // None, it only returns the oldest message if the cache was already full.
+ assert!(event.update(&mut cache).is_none());
+ // Assert there's only 1 message in the channel's message cache.
+ assert_eq!(cache.messages.get(&event.message.channel_id).unwrap().len(), 1);
+
+ // Add a second message, assert that channel message cache length is 2.
+ event.message.id = MessageId(4);
+ assert!(event.update(&mut cache).is_none());
+ assert_eq!(cache.messages.get(&event.message.channel_id).unwrap().len(), 2);
+
+ // Add a third message, the first should now be removed.
+ event.message.id = MessageId(5);
+ assert!(event.update(&mut cache).is_some());
+
+ {
+ let channel = cache.messages.get(&event.message.channel_id).unwrap();
+
+ assert_eq!(channel.len(), 2);
+ // Check that the first message is now removed.
+ assert!(!channel.contains_key(&MessageId(3)));
+ }
+
+ let guild_channel = GuildChannel {
+ id: event.message.channel_id,
+ bitrate: None,
+ category_id: None,
+ guild_id: event.message.guild_id.unwrap(),
+ kind: ChannelType::Text,
+ last_message_id: None,
+ last_pin_timestamp: None,
+ name: String::new(),
+ permission_overwrites: vec![],
+ position: 0,
+ topic: None,
+ user_limit: None,
+ nsfw: false,
+ };
+
+ // Add a channel delete event to the cache, the cached messages for that
+ // channel should now be gone.
+ let mut delete = ChannelDeleteEvent {
+ channel: Channel::Guild(Arc::new(RwLock::new(guild_channel.clone()))),
+ };
+ assert!(cache.update(&mut delete).is_none());
+ assert!(!cache.messages.contains_key(&delete.channel.id()));
+
+ // Test deletion of a guild channel's message cache when a GuildDeleteEvent
+ // is received.
+ let mut guild_create = {
+ let mut channels = HashMap::new();
+ channels.insert(ChannelId(2), Arc::new(RwLock::new(guild_channel.clone())));
+
+ GuildCreateEvent {
+ guild: Guild {
+ id: GuildId(1),
+ afk_channel_id: None,
+ afk_timeout: 0,
+ application_id: None,
+ default_message_notifications: DefaultMessageNotificationLevel::All,
+ emojis: HashMap::new(),
+ explicit_content_filter: ExplicitContentFilter::None,
+ features: vec![],
+ icon: None,
+ joined_at: datetime,
+ large: false,
+ member_count: 0,
+ members: HashMap::new(),
+ mfa_level: MfaLevel::None,
+ name: String::new(),
+ owner_id: UserId(3),
+ presences: HashMap::new(),
+ region: String::new(),
+ roles: HashMap::new(),
+ splash: None,
+ system_channel_id: None,
+ verification_level: VerificationLevel::Low,
+ voice_states: HashMap::new(),
+ channels,
+ },
+ }
+ };
+ assert!(cache.update(&mut guild_create).is_none());
+ assert!(cache.update(&mut event).is_none());
+
+ let mut guild_delete = GuildDeleteEvent {
+ guild: PartialGuild {
+ id: GuildId(1),
+ afk_channel_id: None,
+ afk_timeout: 0,
+ default_message_notifications: DefaultMessageNotificationLevel::All,
+ embed_channel_id: None,
+ embed_enabled: false,
+ emojis: HashMap::new(),
+ features: vec![],
+ icon: None,
+ mfa_level: MfaLevel::None,
+ name: String::new(),
+ owner_id: UserId(3),
+ region: String::new(),
+ roles: HashMap::new(),
+ splash: None,
+ verification_level: VerificationLevel::Low,
+ },
+ };
+
+ // The guild existed in the cache, so the cache's guild is returned by the
+ // update.
+ assert!(cache.update(&mut guild_delete).is_some());
+
+ // Assert that the channel's message cache no longer exists.
+ assert!(!cache.messages.contains_key(&ChannelId(2)));
+ }
+}
diff --git a/src/client/bridge/gateway/mod.rs b/src/client/bridge/gateway/mod.rs
index 27b5c10..a542dbc 100644
--- a/src/client/bridge/gateway/mod.rs
+++ b/src/client/bridge/gateway/mod.rs
@@ -1,4 +1,4 @@
-//! The client gateway bridge is support essential for the [`client`] module.
+//! The client gateway bridge is support essential for the [`client`][client] module.
//!
//! This is made available for user use if one wishes to be lower-level or avoid
//! the higher functionality of the [`Client`].
@@ -38,13 +38,14 @@
//! For almost every - if not every - use case, you only need to _possibly_ be
//! concerned about the [`ShardManager`] in this module.
//!
+//! [client]: ../../index.html
//! [`Client`]: ../../struct.Client.html
-//! [`client`]: ../..
//! [`Shard`]: ../../../gateway/struct.Shard.html
//! [`ShardManager`]: struct.ShardManager.html
//! [`ShardManager::restart`]: struct.ShardManager.html#method.restart
//! [`ShardManager::shutdown`]: struct.ShardManager.html#method.shutdown
-//! [`ShardQueuer`]: struct.ShardQueuer.html
+//! [`ShardManagerMessage`]: enum.ShardManagerMessage.html
+//! [`ShardQueue`]: struct.ShardQueuer.html
//! [`ShardRunner`]: struct.ShardRunner.html
pub mod event;
@@ -127,7 +128,7 @@ pub enum ShardManagerMessage {
/// This should usually be wrapped in a [`ShardClientMessage`].
///
/// [`ShardClientMessage`]: enum.ShardClientMessage.html
-/// [`ShardQueuer`]: enum.ShardQueuer.html
+/// [`ShardQueuer`]: struct.ShardQueuer.html
#[derive(Clone, Debug)]
pub enum ShardQueuerMessage {
/// Message to start a shard, where the 0-index element is the ID of the
diff --git a/src/client/bridge/gateway/shard_manager_monitor.rs b/src/client/bridge/gateway/shard_manager_monitor.rs
index e9a07cd..4989ef2 100644
--- a/src/client/bridge/gateway/shard_manager_monitor.rs
+++ b/src/client/bridge/gateway/shard_manager_monitor.rs
@@ -12,7 +12,7 @@ use super::{ShardManager, ShardManagerMessage};
/// receiving [`ShardManagerMessage`]s, such as whether to shutdown a shard or
/// shutdown everything entirely.
///
-/// [`ShardManagerMessage`]: struct.ShardManagerMessage.html
+/// [`ShardManagerMessage`]: enum.ShardManagerMessage.html
#[derive(Debug)]
pub struct ShardManagerMonitor {
/// An clone of the Arc to the manager itself.
diff --git a/src/client/bridge/gateway/shard_messenger.rs b/src/client/bridge/gateway/shard_messenger.rs
index 2331d4a..11e75ce 100644
--- a/src/client/bridge/gateway/shard_messenger.rs
+++ b/src/client/bridge/gateway/shard_messenger.rs
@@ -106,10 +106,9 @@ impl ShardMessenger {
/// # }
/// ```
///
- /// [`Event::GuildMembersChunk`]:
- /// ../../model/event/enum.Event.html#variant.GuildMembersChunk
- /// [`Guild`]: ../../model/guild/struct.Guild.html
- /// [`Member`]: ../../model/guild/struct.Member.html
+ /// [`Event::GuildMembersChunk`]: ../../../model/event/enum.Event.html#variant.GuildMembersChunk
+ /// [`Guild`]: ../../../model/guild/struct.Guild.html
+ /// [`Member`]: ../../../model/guild/struct.Member.html
pub fn chunk_guilds<It>(
&self,
guild_ids: It,
@@ -147,9 +146,19 @@ impl ShardMessenger {
/// #
/// # let mut shard = Shard::new(mutex.clone(), mutex, [0, 1]).unwrap();
/// #
+ /// # #[cfg(feature = "model")]
/// use serenity::model::gateway::Game;
+ /// # #[cfg(not(feature = "model"))]
+ /// use serenity::model::gateway::{Game, GameType};
///
+ /// # #[cfg(feature = "model")]
/// shard.set_game(Some(Game::playing("Heroes of the Storm")));
+ /// # #[cfg(not(feature = "model"))]
+ /// shard.set_game(Some(Game {
+ /// kind: GameType::Playing,
+ /// name: "Heroes of the Storm".to_owned(),
+ /// url: None,
+ /// }));
/// # Ok(())
/// # }
/// #
@@ -157,7 +166,11 @@ impl ShardMessenger {
/// # try_main().unwrap();
/// # }
/// ```
- pub fn set_game(&self, game: Option<Game>) {
+ pub fn set_game<T: Into<Game>>(&self, game: Option<T>) {
+ self._set_game(game.map(Into::into))
+ }
+
+ fn _set_game(&self, game: Option<Game>) {
let _ = self.send(ShardRunnerMessage::SetGame(game));
}
@@ -195,7 +208,15 @@ impl ShardMessenger {
/// # try_main().unwrap();
/// # }
/// ```
- pub fn set_presence(&self, game: Option<Game>, mut status: OnlineStatus) {
+ pub fn set_presence<T: Into<Game>>(
+ &self,
+ game: Option<T>,
+ status: OnlineStatus,
+ ) {
+ self._set_presence(game.map(Into::into), status)
+ }
+
+ fn _set_presence(&self, game: Option<Game>, mut status: OnlineStatus) {
if status == OnlineStatus::Offline {
status = OnlineStatus::Invisible;
}
@@ -239,9 +260,9 @@ impl ShardMessenger {
/// # }
/// ```
///
- /// [`DoNotDisturb`]: ../../model/user/enum.OnlineStatus.html#variant.DoNotDisturb
- /// [`Invisible`]: ../../model/user/enum.OnlineStatus.html#variant.Invisible
- /// [`Offline`]: ../../model/user/enum.OnlineStatus.html#variant.Offline
+ /// [`DoNotDisturb`]: ../../../model/user/enum.OnlineStatus.html#variant.DoNotDisturb
+ /// [`Invisible`]: ../../../model/user/enum.OnlineStatus.html#variant.Invisible
+ /// [`Offline`]: ../../../model/user/enum.OnlineStatus.html#variant.Offline
pub fn set_status(&self, mut online_status: OnlineStatus) {
if online_status == OnlineStatus::Offline {
online_status = OnlineStatus::Invisible;
diff --git a/src/client/bridge/gateway/shard_runner_message.rs b/src/client/bridge/gateway/shard_runner_message.rs
index 29024c9..f281d21 100644
--- a/src/client/bridge/gateway/shard_runner_message.rs
+++ b/src/client/bridge/gateway/shard_runner_message.rs
@@ -17,7 +17,7 @@ pub enum ShardRunnerMessage {
/// The maximum number of members to receive [`GuildMembersChunkEvent`]s
/// for.
///
- /// [`GuildMembersChunkEvent`]: ../../../model/event/GuildMembersChunkEvent.html
+ /// [`GuildMembersChunkEvent`]: ../../../model/event/struct.GuildMembersChunkEvent.html
limit: Option<u16>,
/// Text to filter members by.
///
diff --git a/src/client/bridge/mod.rs b/src/client/bridge/mod.rs
index 41fcdec..a3a431d 100644
--- a/src/client/bridge/mod.rs
+++ b/src/client/bridge/mod.rs
@@ -1,4 +1,4 @@
-//! A collection of bridged support between the [`client`] module and other
+//! A collection of bridged support between the [`client`](../index.html) module and other
//! modules.
//!
//! **Warning**: You likely _do not_ need to mess with anything in here. Beware.
diff --git a/src/client/context.rs b/src/client/context.rs
index d581ffb..61a1925 100644
--- a/src/client/context.rs
+++ b/src/client/context.rs
@@ -87,6 +87,7 @@ impl Context {
/// client.start().unwrap();
/// ```
#[cfg(feature = "builder")]
+ #[deprecated(since = "0.5.6", note = "Use the http module instead.")]
pub fn edit_profile<F: FnOnce(EditProfile) -> EditProfile>(&self, f: F) -> Result<CurrentUser> {
let mut map = VecMap::with_capacity(2);
@@ -273,7 +274,7 @@ impl Context {
/// [`set_presence`]: #method.set_presence
#[inline]
pub fn reset_presence(&self) {
- self.shard.set_presence(None, OnlineStatus::Online);
+ self.shard.set_presence(None::<Game>, OnlineStatus::Online);
}
/// Sets the current game, defaulting to an online status of [`Online`].
@@ -316,7 +317,11 @@ impl Context {
///
/// [`Online`]: ../model/user/enum.OnlineStatus.html#variant.Online
#[inline]
- pub fn set_game(&self, game: Game) {
+ pub fn set_game<T: Into<Game>>(&self, game: T) {
+ self._set_game(game.into())
+ }
+
+ fn _set_game(&self, game: Game) {
self.shard.set_presence(Some(game), OnlineStatus::Online);
}
@@ -356,14 +361,10 @@ impl Context {
/// [`Playing`]: ../model/gateway/enum.GameType.html#variant.Playing
/// [`reset_presence`]: #method.reset_presence
/// [`set_presence`]: #method.set_presence
- pub fn set_game_name(&self, game_name: &str) {
- let game = Game {
- kind: GameType::Playing,
- name: game_name.to_string(),
- url: None,
- };
-
- self.shard.set_presence(Some(game), OnlineStatus::Online);
+ #[deprecated(since = "0.5.5", note = "Use Context::set_game")]
+ #[inline]
+ pub fn set_game_name<T: Into<String>>(&self, game_name: T) {
+ self.set_game(game_name.into())
}
/// Sets the current user's presence, providing all fields to be passed.
diff --git a/src/client/dispatch.rs b/src/client/dispatch.rs
index dd5b4fe..7dc9024 100644
--- a/src/client/dispatch.rs
+++ b/src/client/dispatch.rs
@@ -15,14 +15,12 @@ use std::sync::mpsc::Sender;
use threadpool::ThreadPool;
use typemap::ShareMap;
-#[cfg(feature = "cache")]
-use chrono::{Timelike, Utc};
#[cfg(feature = "framework")]
use framework::Framework;
#[cfg(feature = "cache")]
use model::id::GuildId;
#[cfg(feature = "cache")]
-use std::{thread, time};
+use std::time::Duration;
#[cfg(feature = "cache")]
use super::CACHE;
@@ -32,17 +30,21 @@ macro_rules! update {
{
#[cfg(feature = "cache")]
{
- CACHE.write().update(&mut $event)
+ if let Some(mut lock) = CACHE.try_write_for(Duration::from_millis(10)) {
+ lock.update(&mut $event)
+ } else {
+ warn!(
+ "[dispatch] Possible deadlock: couldn't unlock cache to update with event: {:?}",
+ $event,
+ );
+
+ None
+ }
}
}
};
}
-#[cfg(feature = "cache")]
-macro_rules! now {
- () => (Utc::now().time().second() * 1000)
-}
-
fn context(
data: &Arc<Mutex<ShareMap>>,
runner_tx: &Sender<InterMessage>,
@@ -95,6 +97,7 @@ pub(crate) fn dispatch<H: EventHandler + Send + Sync + 'static>(
}
#[cfg(not(feature = "framework"))]
+#[allow(unused_mut)]
pub(crate) fn dispatch<H: EventHandler + Send + Sync + 'static>(
event: DispatchEvent,
data: &Arc<Mutex<ShareMap>>,
@@ -104,7 +107,7 @@ pub(crate) fn dispatch<H: EventHandler + Send + Sync + 'static>(
shard_id: u64,
) {
match event {
- DispatchEvent::Model(Event::MessageCreate(event)) => {
+ DispatchEvent::Model(Event::MessageCreate(mut event)) => {
update!(event);
let context = context(data, runner_tx, shard_id);
@@ -149,20 +152,6 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
threadpool: &ThreadPool,
shard_id: u64,
) {
- #[cfg(feature = "cache")]
- let mut last_guild_create_time = now!();
-
- #[cfg(feature = "cache")]
- let wait_for_guilds = move || -> ::Result<()> {
- let unavailable_guilds = CACHE.read().unavailable_guilds.len();
-
- while unavailable_guilds != 0 && (now!() < last_guild_create_time + 2000) {
- thread::sleep(time::Duration::from_millis(500));
- }
-
- Ok(())
- };
-
match event {
DispatchEvent::Client(ClientEvent::ShardStageUpdate(event)) => {
let context = context(data, runner_tx, shard_id);
@@ -177,9 +166,9 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
let context = context(data, runner_tx, shard_id);
- // This different channel_create dispatching is only due to the fact that
- // each time the bot receives a dm, this event is also fired.
- // So in short, only exists to reduce unnecessary clutter.
+ // Discord sends both a MessageCreate and a ChannelCreate upon a new message in a private channel.
+ // This could potentionally be annoying to handle when otherwise wanting to normally take care of a new channel.
+ // So therefore, private channels are dispatched to their own handler code.
match event.channel {
Channel::Private(channel) => {
let event_handler = Arc::clone(event_handler);
@@ -309,8 +298,6 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
#[cfg(feature = "cache")]
{
- last_guild_create_time = now!();
-
let cache = CACHE.read();
if cache.unavailable_guilds.is_empty() {
@@ -578,28 +565,12 @@ fn handle_event<H: EventHandler + Send + Sync + 'static>(
DispatchEvent::Model(Event::Ready(mut event)) => {
update!(event);
- let event_handler = Arc::clone(event_handler);
-
- feature_cache! {{
- last_guild_create_time = now!();
-
- let _ = wait_for_guilds()
- .map(move |_| {
- let context = context(data, runner_tx, shard_id);
- let event_handler = Arc::clone(&event_handler);
-
- threadpool.execute(move || {
- event_handler.ready(context, event.ready);
- });
- });
- } else {
- let context = context(data, runner_tx, shard_id);
- let event_handler = Arc::clone(&event_handler);
+ let context = context(data, runner_tx, shard_id);
+ let event_handler = Arc::clone(&event_handler);
- threadpool.execute(move || {
- event_handler.ready(context, event.ready);
- });
- }}
+ threadpool.execute(move || {
+ event_handler.ready(context, event.ready);
+ });
},
DispatchEvent::Model(Event::Resumed(mut event)) => {
let context = context(data, runner_tx, shard_id);
diff --git a/src/client/mod.rs b/src/client/mod.rs
index d5f8711..6fcb84c 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -92,10 +92,10 @@ use self::bridge::voice::ClientVoiceManager;
/// client.start();
/// ```
///
-/// [`Shard`]: gateway/struct.Shard.html
+/// [`Shard`]: ../gateway/struct.Shard.html
/// [`EventHandler::message`]: trait.EventHandler.html#tymethod.message
/// [`Event::MessageCreate`]: ../model/event/enum.Event.html#variant.MessageCreate
-/// [sharding docs]: gateway/index.html#sharding
+/// [sharding docs]: ../index.html#sharding
pub struct Client {
/// A ShareMap which requires types to be Send + Sync. This is a map that
/// can be safely shared across contexts.
@@ -169,13 +169,12 @@ pub struct Client {
///
/// Refer to [example 05] for an example on using the `data` field.
///
- /// [`Context::data`]: struct.Context.html#method.data
+ /// [`Context::data`]: struct.Context.html#structfield.data
/// [`Event::MessageCreate`]: ../model/event/enum.Event.html#variant.MessageCreate
/// [`Event::MessageDelete`]: ../model/event/enum.Event.html#variant.MessageDelete
/// [`Event::MessageDeleteBulk`]: ../model/event/enum.Event.html#variant.MessageDeleteBulk
/// [`Event::MessageUpdate`]: ../model/event/enum.Event.html#variant.MessageUpdate
- /// [example 05]:
- /// https://github.com/serenity-rs/serenity/tree/master/examples/05_command_framework
+ /// [example 05]: https://github.com/serenity-rs/serenity/tree/master/examples/05_command_framework
pub data: Arc<Mutex<ShareMap>>,
/// A vector of all active shards that have received their [`Event::Ready`]
/// payload, and have dispatched to [`on_ready`] if an event handler was
@@ -387,7 +386,7 @@ impl Client {
}
/// Sets a framework to be used with the client. All message events will be
- /// passed through the framework _after_ being passed to the [`on_message`]
+ /// passed through the framework _after_ being passed to the [`message`]
/// event handler.
///
/// See the [framework module-level documentation][framework docs] for more
@@ -486,7 +485,7 @@ impl Client {
/// Refer to the documentation for the `framework` module for more in-depth
/// information.
///
- /// [`on_message`]: #method.on_message
+ /// [`message`]: trait.EventHandler.html#method.message
/// [framework docs]: ../framework/index.html
#[cfg(feature = "framework")]
pub fn with_framework<F: Framework + Send + 'static>(&mut self, f: F) {
@@ -534,7 +533,7 @@ impl Client {
/// # }
/// ```
///
- /// [gateway docs]: gateway/index.html#sharding
+ /// [gateway docs]: ../gateway/index.html#sharding
pub fn start(&mut self) -> Result<()> {
self.start_connection([0, 0, 1])
}
@@ -586,7 +585,7 @@ impl Client {
/// an error.
///
/// [`ClientError::Shutdown`]: enum.ClientError.html#variant.Shutdown
- /// [gateway docs]: gateway/index.html#sharding
+ /// [gateway docs]: ../gateway/index.html#sharding
pub fn start_autosharded(&mut self) -> Result<()> {
let (x, y) = {
let res = http::get_bot_gateway()?;
@@ -673,7 +672,7 @@ impl Client {
/// [`ClientError::Shutdown`]: enum.ClientError.html#variant.Shutdown
/// [`start`]: #method.start
/// [`start_autosharded`]: #method.start_autosharded
- /// [gateway docs]: gateway/index.html#sharding
+ /// [gateway docs]: ../gateway/index.html#sharding
pub fn start_shard(&mut self, shard: u64, shards: u64) -> Result<()> {
self.start_connection([shard, shard, shards])
}
@@ -727,7 +726,7 @@ impl Client {
/// [`ClientError::Shutdown`]: enum.ClientError.html#variant.Shutdown
/// [`start_shard`]: #method.start_shard
/// [`start_shard_range`]: #method.start_shard_range
- /// [Gateway docs]: gateway/index.html#sharding
+ /// [Gateway docs]: ../gateway/index.html#sharding
pub fn start_shards(&mut self, total_shards: u64) -> Result<()> {
self.start_connection([0, total_shards - 1, total_shards])
}
@@ -797,7 +796,7 @@ impl Client {
/// [`ClientError::Shutdown`]: enum.ClientError.html#variant.Shutdown
/// [`start_shard`]: #method.start_shard
/// [`start_shards`]: #method.start_shards
- /// [Gateway docs]: gateway/index.html#sharding
+ /// [Gateway docs]: ../gateway/index.html#sharding
pub fn start_shard_range(&mut self, range: [u64; 2], total_shards: u64) -> Result<()> {
self.start_connection([range[0], range[1], total_shards])
}
diff --git a/src/constants.rs b/src/constants.rs
index 89a2085..5485d7e 100644
--- a/src/constants.rs
+++ b/src/constants.rs
@@ -13,7 +13,7 @@ pub const LARGE_THRESHOLD: u8 = 250;
pub const MESSAGE_CODE_LIMIT: u16 = 2000;
/// The [UserAgent] sent along with every request.
///
-/// [UserAgent]: ../hyper/header/struct.UserAgent.html
+/// [UserAgent]: ../../hyper/header/struct.UserAgent.html
pub const USER_AGENT: &str = concat!(
"DiscordBot (https://github.com/serenity-rs/serenity, ",
env!("CARGO_PKG_VERSION"),
diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs
index bf3056b..2700bfb 100644
--- a/src/framework/standard/args.rs
+++ b/src/framework/standard/args.rs
@@ -7,10 +7,9 @@ use std::{
/// Defines how an operation on an `Args` method failed.
#[derive(Debug)]
pub enum Error<E: StdError> {
- /// "END-OF-STRING", more precisely, there isn't anything to parse anymore.
+ /// "END-OF-STRING". There's nothing to parse anymore.
Eos,
- /// A parsing operation failed; the error in it can be of any returned from the `FromStr`
- /// trait.
+ /// The parsing operation failed; the error can be anything returned from the `FromStr` trait.
Parse(E),
}
@@ -200,34 +199,34 @@ impl<'a> Lexer<'a> {
/// // You have a `photo` command that grabs the avatar url of a user. This command accepts names only.
/// // Now, one of your users wants the avatar of a user named Princess Zelda.
/// // Problem is, her name contains a space; our delimiter. This would result in two arguments, "Princess" and "Zelda".
-/// // So how should we get around this? Through quotes! By surrounding her name in them we can perceive it as one single argument.
+/// // So how shall we get around this? Through quotes! By surrounding her name in them we can perceive it as one single argument.
/// let mut args = Args::new(r#""Princess Zelda""#, &[" ".to_string()]);
///
/// // Hooray!
/// assert_eq!(args.single_quoted::<String>().unwrap(), "Princess Zelda");
/// ```
///
-/// In case of a mistake, we can go back in time... er i mean, one step (or entirely):
+/// In case of a mistake, we can go back in time... er I mean, one step (or entirely):
///
/// ```rust
/// use serenity::framework::standard::Args;
///
-/// let mut args = Args::new("4 20", &[" ".to_string()]);
+/// let mut args = Args::new("4 2", &[" ".to_string()]);
///
/// assert_eq!(args.single::<u32>().unwrap(), 4);
///
/// // Oh wait, oops, meant to double the 4.
-/// // But i won't able to access it now...
-/// // oh wait, i can `rewind`.
+/// // But I won't able to access it now...
+/// // oh wait, I can `rewind`.
/// args.rewind();
///
/// assert_eq!(args.single::<u32>().unwrap() * 2, 8);
///
-/// // And the same for the 20
-/// assert_eq!(args.single::<u32>().unwrap() * 2, 40);
+/// // And the same for the 2
+/// assert_eq!(args.single::<u32>().unwrap() * 2, 4);
///
-/// // WAIT, NO. I wanted to concatenate them into a "420" string...
-/// // Argh, what should i do now????
+/// // WAIT, NO. I wanted to concatenate them into a "42" string...
+/// // Argh, what should I do now????
/// // ....
/// // oh, `restore`
/// args.restore();
@@ -235,7 +234,7 @@ impl<'a> Lexer<'a> {
/// let res = format!("{}{}", args.single::<String>().unwrap(), args.single::<String>().unwrap());
///
/// // Yay.
-/// assert_eq!(res, "420");
+/// assert_eq!(res, "42");
/// ```
///
/// Hmm, taking a glance at the prior example, it seems we have an issue with reading the same argument over and over.
@@ -244,19 +243,21 @@ impl<'a> Lexer<'a> {
/// ```rust
/// use serenity::framework::standard::Args;
///
-/// let mut args = Args::new("four five six three", &[" ".to_string()]);
+/// let mut args = Args::new("trois cinq quatre six", &[" ".to_string()]);
///
-/// assert_eq!(args.single_n::<String>().unwrap(), "four");
+/// assert_eq!(args.single_n::<String>().unwrap(), "trois");
///
-/// // It might suggest we've lost the `four`, but in fact, we didn't! And not only that, we can do it an infinite amount of times!
-/// assert_eq!(args.single_n::<String>().unwrap(), "four");
-/// assert_eq!(args.single_n::<String>().unwrap(), "four");
-/// assert_eq!(args.single_n::<String>().unwrap(), "four");
-/// assert_eq!(args.single_n::<String>().unwrap(), "four");
+/// // It might suggest we've lost the `trois`. But in fact, we didn't! And not only that, we can do it an infinite amount of times!
+/// assert_eq!(args.single_n::<String>().unwrap(), "trois");
+/// assert_eq!(args.single_n::<String>().unwrap(), "trois");
+/// assert_eq!(args.single_n::<String>().unwrap(), "trois");
+/// assert_eq!(args.single_n::<String>().unwrap(), "trois");
///
-/// // Only if we use its parent method will we then lose it.
-/// assert_eq!(args.single::<String>().unwrap(), "four");
-/// assert_eq!(args.single_n::<String>().unwrap(), "five");
+/// // Only if we use its brother method we'll then lose it.
+/// assert_eq!(args.single::<String>().unwrap(), "trois");
+/// assert_eq!(args.single::<String>().unwrap(), "cinq");
+/// assert_eq!(args.single::<String>().unwrap(), "quatre");
+/// assert_eq!(args.single::<String>().unwrap(), "six");
/// ```
#[derive(Clone, Debug)]
pub struct Args {
@@ -276,11 +277,11 @@ impl Args {
/// use serenity::framework::standard::Args;
///
/// let mut args = Args::new(
- /// // Our source from where we'll parse over.
+ /// // Our message from which we'll parse over.
/// "the quick brown fox jumps over the lazy",
///
/// // The "delimiters", or aka the separators. They denote how we distinguish arguments as their own.
- /// // For this instance, we'll use one delimiter. The space (`0x20`), which will separate the arguments.
+ /// // For this example, we'll use one delimiter, the space (`0x20`), which will separate the message.
/// &[" ".to_string()],
/// );
///
@@ -288,7 +289,7 @@ impl Args {
/// assert_eq!(args.single::<String>().unwrap(), "quick");
/// assert_eq!(args.single::<String>().unwrap(), "brown");
///
- /// // We should not see `the quick brown` again.
+ /// // We shall not see `the quick brown` again.
/// assert_eq!(args.rest(), "fox jumps over the lazy");
/// ```
///
@@ -300,16 +301,21 @@ impl Args {
.flat_map(|s| s.chars())
.collect::<Vec<_>>();
- let mut lex = Lexer::new(message, &delims);
-
let mut args = Vec::new();
- while let Some(token) = lex.commit() {
- if token.kind == TokenKind::Delimiter {
- continue;
- }
+ // If there are no delimiters, then the only possible argument is the whole message.
+ if delims.is_empty() && !message.is_empty() {
+ args.push(Token::new(TokenKind::Argument, &message[..], 0));
+ } else {
+ let mut lex = Lexer::new(message, &delims);
+
+ while let Some(token) = lex.commit() {
+ if token.kind == TokenKind::Delimiter {
+ continue;
+ }
- args.push(token);
+ args.push(token);
+ }
}
Args {
@@ -319,6 +325,29 @@ impl Args {
}
}
+ /// Retrieves the current argument. Does not parse.
+ ///
+ /// # Note
+ ///
+ /// This borrows `Args` for the entire lifetime of the returned argument.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use serenity::framework::standard::Args;
+ ///
+ /// let mut args = Args::new("4 2", &[" ".to_string()]);
+ ///
+ /// assert_eq!(args.current(), Some("4"));
+ /// args.next();
+ /// assert_eq!(args.current(), Some("2"));
+ /// args.next();
+ /// assert_eq!(args.current(), None);
+ /// ```
+ pub fn current(&self) -> Option<&str> {
+ self.args.get(self.offset).map(|t| t.lit.as_str())
+ }
+
/// Parses the current argument and advances.
///
/// # Examples
@@ -326,12 +355,12 @@ impl Args {
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let mut args = Args::new("42 69", &[" ".to_string()]);
+ /// let mut args = Args::new("4 2", &[" ".to_string()]);
///
- /// assert_eq!(args.single::<u32>().unwrap(), 42);
+ /// assert_eq!(args.single::<u32>().unwrap(), 4);
///
- /// // `42` is now out of the way, next we have `69`
- /// assert_eq!(args.single::<u32>().unwrap(), 69);
+ /// // `4` is now out of the way. Next we have `2`
+ /// assert_eq!(args.single::<u32>().unwrap(), 2);
/// ```
pub fn single<T: FromStr>(&mut self) -> Result<T, T::Err>
where T::Err: StdError {
@@ -353,10 +382,10 @@ impl Args {
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let args = Args::new("42 69", &[" ".to_string()]);
+ /// let args = Args::new("4 2", &[" ".to_string()]);
///
- /// assert_eq!(args.single_n::<u32>().unwrap(), 42);
- /// assert_eq!(args.rest(), "42 69");
+ /// assert_eq!(args.single_n::<u32>().unwrap(), 4);
+ /// assert_eq!(args.rest(), "4 2");
/// ```
///
/// [`single`]: #method.single
@@ -371,17 +400,17 @@ impl Args {
Ok(T::from_str(&cur.lit)?)
}
- /// "Skip" the argument (Sugar for `args.single::<String>().ok()`)
+ /// "Skip" the argument. Equivalent to `args.single::<String>().ok()`.
///
/// # Examples
///
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let mut args = Args::new("42 69", &[" ".to_string()]);
+ /// let mut args = Args::new("4 2", &[" ".to_string()]);
///
/// args.skip();
- /// assert_eq!(args.single::<u32>().unwrap(), 69);
+ /// assert_eq!(args.single::<u32>().unwrap(), 2);
/// ```
pub fn skip(&mut self) -> Option<String> {
if self.is_empty() {
@@ -398,11 +427,11 @@ impl Args {
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let mut args = Args::new("42 69 88 99", &[" ".to_string()]);
+ /// let mut args = Args::new("man of culture topknot", &[" ".to_string()]);
///
/// args.skip_for(3);
/// assert_eq!(args.remaining(), 1);
- /// assert_eq!(args.single::<u32>().unwrap(), 99);
+ /// assert_eq!(args.single::<String>().unwrap(), "topknot");
/// ```
///
/// [`skip`]: #method.skip
@@ -420,17 +449,23 @@ impl Args {
Some(vec)
}
-
- /// Provides an iterator that will spew arguments until the end of the message.
+ /// Iterate until end of message.
///
/// # Examples
///
+ /// Assert that all of the numbers in the message are even.
+ ///
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let mut args = Args::new("3 4", &[" ".to_string()]);
+ /// let mut args = Args::new("4 2", &[" ".to_string()]);
+ ///
+ /// for arg in args.iter::<u32>() {
+ /// // Default to zero in case some linguist turns our numbers into words and can't parse those.
+ /// let arg = arg.unwrap_or(0);
+ /// assert!(arg % 2 == 0);
+ /// }
///
- /// assert_eq!(*args.iter::<u32>().map(|num| num.unwrap().pow(2)).collect::<Vec<_>>(), [9, 16]);
/// assert!(args.is_empty());
/// ```
pub fn iter<T: FromStr>(&mut self) -> Iter<T>
@@ -438,16 +473,17 @@ impl Args {
Iter::new(self)
}
- /// Parses all of the remaining arguments and returns them in a `Vec` (Sugar for `args.iter().collect::<Vec<_>>()`).
+ /// Parses all of the remaining arguments and returns them in a `Vec`.
+ /// Equivalent to `args.iter().collect::<Vec<_>>()`.
///
/// # Examples
///
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let args = Args::new("42 69", &[" ".to_string()]);
+ /// let args = Args::new("4 2", &[" ".to_string()]);
///
- /// assert_eq!(*args.multiple::<u32>().unwrap(), [42, 69]);
+ /// assert_eq!(*args.multiple::<u32>().unwrap(), [4, 2]);
/// ```
pub fn multiple<T: FromStr>(mut self) -> Result<Vec<T>, T::Err>
where T::Err: StdError {
@@ -458,6 +494,30 @@ impl Args {
self.iter::<T>().collect()
}
+ /// Retrieves the current argument and also removes quotes around it if they're present.
+ /// Does not parse.
+ ///
+ /// # Note
+ ///
+ /// This borrows `Args` for the entire lifetime of the returned argument.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use serenity::framework::standard::Args;
+ ///
+ /// let mut args = Args::new("4 \"2\"", &[" ".to_string()]);
+ ///
+ /// assert_eq!(args.current_quoted(), Some("4"));
+ /// args.next();
+ /// assert_eq!(args.current_quoted(), Some("2"));
+ /// args.next();
+ /// assert_eq!(args.current_quoted(), None);
+ /// ```
+ pub fn current_quoted(&self) -> Option<&str> {
+ self.args.get(self.offset).map(|t| quotes_extract(t))
+ }
+
/// Like [`single`], but accounts quotes.
///
/// # Examples
@@ -465,9 +525,9 @@ impl Args {
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let mut args = Args::new(r#""42 69""#, &[" ".to_string()]);
+ /// let mut args = Args::new(r#""4 2""#, &[" ".to_string()]);
///
- /// assert_eq!(args.single_quoted::<String>().unwrap(), "42 69");
+ /// assert_eq!(args.single_quoted::<String>().unwrap(), "4 2");
/// assert!(args.is_empty());
/// ```
///
@@ -495,10 +555,10 @@ impl Args {
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let mut args = Args::new(r#""42 69""#, &[" ".to_string()]);
+ /// let mut args = Args::new(r#""4 2""#, &[" ".to_string()]);
///
- /// assert_eq!(args.single_quoted_n::<String>().unwrap(), "42 69");
- /// assert_eq!(args.rest(), r#""42 69""#);
+ /// assert_eq!(args.single_quoted_n::<String>().unwrap(), "4 2");
+ /// assert_eq!(args.rest(), r#""4 2""#);
/// ```
///
/// [`single_quoted`]: #method.single_quoted
@@ -519,12 +579,19 @@ impl Args {
///
/// # Examples
///
+ /// Assert that all of the numbers in quotations in the message are odd.
+ ///
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let mut args = Args::new(r#""2" "5""#, &[" ".to_string()]);
+ /// let mut args = Args::new(r#""5" "3""#, &[" ".to_string()]);
+ ///
+ /// for arg in args.iter_quoted::<u32>() {
+ /// // Default to zero in case some linguist turns our numbers into words and can't parse those.
+ /// let arg = arg.unwrap_or(0);
+ /// assert!(arg % 2 != 0);
+ /// }
///
- /// assert_eq!(*args.iter_quoted::<u32>().map(|n| n.unwrap().pow(2)).collect::<Vec<_>>(), [4, 25]);
/// assert!(args.is_empty());
/// ```
///
@@ -541,9 +608,9 @@ impl Args {
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let mut args = Args::new(r#""42" "69""#, &[" ".to_string()]);
+ /// let mut args = Args::new(r#""4" "2""#, &[" ".to_string()]);
///
- /// assert_eq!(*args.multiple_quoted::<u32>().unwrap(), [42, 69]);
+ /// assert_eq!(*args.multiple_quoted::<u32>().unwrap(), [4, 2]);
/// ```
///
/// [`multiple`]: #method.multiple
@@ -559,20 +626,20 @@ impl Args {
/// Returns the first argument that can be parsed and removes it from the message. The suitable argument
/// can be in an arbitrary position in the message. Likewise, takes quotes into account.
///
- /// **Note**:
- /// Unlike how other methods on this struct work,
- /// this function permantently removes the argument if it was **found** and was **succesfully** parsed.
- /// Hence, use this with caution.
+ /// # Note
+ ///
+ /// Unlike the rest, this function permantently removes the argument if it was **found** and was **succesfully** parsed.
+ /// Hence, use with caution.
///
/// # Examples
///
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let mut args = Args::new("c42 69", &[" ".to_string()]);
+ /// let mut args = Args::new("c4 2", &[" ".to_string()]);
///
- /// assert_eq!(args.find::<u32>().unwrap(), 69);
- /// assert_eq!(args.single::<String>().unwrap(), "c42");
+ /// assert_eq!(args.find::<u32>().unwrap(), 2);
+ /// assert_eq!(args.single::<String>().unwrap(), "c4");
/// assert!(args.is_empty());
/// ```
pub fn find<T: FromStr>(&mut self) -> Result<T, T::Err>
@@ -600,13 +667,13 @@ impl Args {
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let mut args = Args::new("c42 69", &[" ".to_string()]);
+ /// let mut args = Args::new("c4 2", &[" ".to_string()]);
///
- /// assert_eq!(args.find_n::<u32>().unwrap(), 69);
+ /// assert_eq!(args.find_n::<u32>().unwrap(), 2);
///
/// // The `69` is still here, so let's parse it again.
- /// assert_eq!(args.single::<String>().unwrap(), "c42");
- /// assert_eq!(args.single::<u32>().unwrap(), 69);
+ /// assert_eq!(args.single::<String>().unwrap(), "c4");
+ /// assert_eq!(args.single::<u32>().unwrap(), 2);
/// assert!(args.is_empty());
/// ```
///
@@ -675,12 +742,7 @@ impl Args {
return s;
}
- let end = s.rfind('"');
- if end.is_none() {
- return s;
- }
-
- let end = end.unwrap();
+ let end = s.rfind('"').unwrap();
// If it got the quote at the start, then there's no closing quote.
if end == 0 {
@@ -697,17 +759,17 @@ impl Args {
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let mut args = Args::new("42 69 91", &[" ".to_string()]);
+ /// let mut args = Args::new("to tre fire", &[" ".to_string()]);
///
- /// assert_eq!(args.rest(), "42 69 91");
+ /// assert_eq!(args.rest(), "to tre fire");
///
/// args.skip();
///
- /// assert_eq!(args.rest(), "69 91");
+ /// assert_eq!(args.rest(), "tre fire");
///
/// args.skip();
///
- /// assert_eq!(args.rest(), "91");
+ /// assert_eq!(args.rest(), "fire");
///
/// args.skip();
///
@@ -729,7 +791,8 @@ impl Args {
/// The full amount of recognised arguments.
///
- /// **Note**:
+ /// # Note
+ ///
/// This never changes. Except for [`find`], which upon success, subtracts the length by 1. (e.g len of `3` becomes `2`)
///
/// # Examples
@@ -737,9 +800,9 @@ impl Args {
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let mut args = Args::new("42 69", &[" ".to_string()]);
+ /// let mut args = Args::new("4 2", &[" ".to_string()]);
///
- /// assert_eq!(args.len(), 2); // `2` because `["42", "69"]`
+ /// assert_eq!(args.len(), 2); // `2` because `["4", "2"]`
/// ```
///
/// [`find`]: #method.find
@@ -756,7 +819,8 @@ impl Args {
///
/// let mut args = Args::new("", &[" ".to_string()]);
///
- /// assert!(args.is_empty()); // `true` because passed message is empty thus no arguments.
+ /// // will be `true` because passed message is empty thus no arguments.
+ /// assert!(args.is_empty());
/// ```
pub fn is_empty(&self) -> bool {
self.offset >= self.args.len()
@@ -769,7 +833,7 @@ impl Args {
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let mut args = Args::new("42 69", &[" ".to_string()]);
+ /// let mut args = Args::new("2 4", &[" ".to_string()]);
///
/// assert_eq!(args.remaining(), 2);
///
@@ -785,22 +849,43 @@ impl Args {
self.len() - self.offset
}
+ /// Move to the next argument.
+ /// This increments the offset pointer.
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use serenity::framework::standard::Args;
+ ///
+ /// let mut args = Args::new("4 2", &[" ".to_string()]);
+ ///
+ /// args.next();
+ ///
+ /// assert_eq!(args.single::<u32>().unwrap(), 2);
+ /// assert!(args.is_empty());
+ /// ```
+ #[inline]
+ pub fn next(&mut self) {
+ self.offset += 1;
+ }
+
/// Go one step behind.
+ /// This decrements the offset pointer.
///
/// # Examples
///
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let mut args = Args::new("42 69", &[" ".to_string()]);
+ /// let mut args = Args::new("4 2", &[" ".to_string()]);
///
- /// assert_eq!(args.single::<u32>().unwrap(), 42);
+ /// assert_eq!(args.single::<u32>().unwrap(), 4);
///
- /// // By this point, we can only parse 69 now.
- /// // However, with the help of `rewind`, we can mess with 42 again.
+ /// // By this point, we can only parse 2 now.
+ /// // However, with the help of `rewind`, we can mess with 4 again.
/// args.rewind();
///
- /// assert_eq!(args.single::<u32>().unwrap() * 2, 84);
+ /// assert_eq!(args.single::<u32>().unwrap() * 2, 8);
/// ```
#[inline]
pub fn rewind(&mut self) {
@@ -818,12 +903,12 @@ impl Args {
/// ```rust
/// use serenity::framework::standard::Args;
///
- /// let mut args = Args::new("42 69 95", &[" ".to_string()]);
+ /// let mut args = Args::new("42 420 69", &[" ".to_string()]);
///
/// // Let's parse 'em numbers!
/// assert_eq!(args.single::<u32>().unwrap(), 42);
+ /// assert_eq!(args.single::<u32>().unwrap(), 420);
/// assert_eq!(args.single::<u32>().unwrap(), 69);
- /// assert_eq!(args.single::<u32>().unwrap(), 95);
///
/// // Oh, no! I actually wanted to multiply all of them by 2!
/// // I don't want to call `rewind` 3 times manually....
@@ -831,8 +916,8 @@ impl Args {
/// args.restore();
///
/// assert_eq!(args.single::<u32>().unwrap() * 2, 84);
+ /// assert_eq!(args.single::<u32>().unwrap() * 2, 840);
/// assert_eq!(args.single::<u32>().unwrap() * 2, 138);
- /// assert_eq!(args.single::<u32>().unwrap() * 2, 190);
/// ```
///
#[inline]
@@ -944,3 +1029,445 @@ fn quotes_extract(token: &Token) -> &str {
&token.lit
}
}
+
+#[cfg(test)]
+mod test {
+ use super::{Args, Error as ArgError};
+
+ #[test]
+ fn single_with_empty_message() {
+ let mut args = Args::new("", &["".to_string()]);
+ assert_matches!(args.single::<String>().unwrap_err(), ArgError::Eos);
+
+ let mut args = Args::new("", &[",".to_string()]);
+ assert_matches!(args.single::<String>().unwrap_err(), ArgError::Eos);
+ }
+
+ #[test]
+ fn single_n_with_empty_message() {
+ let args = Args::new("", &["".to_string()]);
+ assert_matches!(args.single_n::<String>().unwrap_err(), ArgError::Eos);
+
+ let args = Args::new("", &[",".to_string()]);
+ assert_matches!(args.single_n::<String>().unwrap_err(), ArgError::Eos);
+ }
+
+ #[test]
+ fn single_quoted_with_empty_message() {
+ let mut args = Args::new("", &["".to_string()]);
+ assert_matches!(args.single_quoted::<String>().unwrap_err(), ArgError::Eos);
+
+ let mut args = Args::new("", &[",".to_string()]);
+ assert_matches!(args.single_quoted::<String>().unwrap_err(), ArgError::Eos);
+ }
+
+ #[test]
+ fn multiple_with_empty_message() {
+ let args = Args::new("", &["".to_string()]);
+ assert_matches!(args.multiple::<String>().unwrap_err(), ArgError::Eos);
+
+ let args = Args::new("", &[",".to_string()]);
+ assert_matches!(args.multiple::<String>().unwrap_err(), ArgError::Eos);
+ }
+
+ #[test]
+ fn multiple_quoted_with_empty_message() {
+ let args = Args::new("", &["".to_string()]);
+ assert_matches!(args.multiple_quoted::<String>().unwrap_err(), ArgError::Eos);
+
+ let args = Args::new("", &[",".to_string()]);
+ assert_matches!(args.multiple_quoted::<String>().unwrap_err(), ArgError::Eos);
+ }
+
+ #[test]
+ fn skip_with_empty_message() {
+ let mut args = Args::new("", &["".to_string()]);
+ assert_matches!(args.skip(), None);
+
+ let mut args = Args::new("", &[",".to_string()]);
+ assert_matches!(args.skip(), None);
+ }
+
+ #[test]
+ fn skip_for_with_empty_message() {
+ let mut args = Args::new("", &["".to_string()]);
+ assert_matches!(args.skip_for(0), None);
+
+ let mut args = Args::new("", &["".to_string()]);
+ assert_matches!(args.skip_for(5), None);
+
+ let mut args = Args::new("", &[",".to_string()]);
+ assert_matches!(args.skip_for(0), None);
+
+ let mut args = Args::new("", &[",".to_string()]);
+ assert_matches!(args.skip_for(5), None);
+ }
+
+ #[test]
+ fn single_i32_with_2_bytes_long_delimiter() {
+ let mut args = Args::new("1, 2", &[", ".to_string()]);
+
+ assert_eq!(args.single::<i32>().unwrap(), 1);
+ assert_eq!(args.single::<i32>().unwrap(), 2);
+ }
+
+ #[test]
+ fn single_i32_with_1_byte_long_delimiter_i32() {
+ let mut args = Args::new("1,2", &[",".to_string()]);
+
+ assert_eq!(args.single::<i32>().unwrap(), 1);
+ assert_eq!(args.single::<i32>().unwrap(), 2);
+ }
+
+ #[test]
+ fn single_i32_with_wrong_char_after_first_arg() {
+ let mut args = Args::new("1, 2", &[",".to_string()]);
+
+ assert_eq!(args.single::<i32>().unwrap(), 1);
+ assert!(args.single::<i32>().is_err());
+ }
+
+ #[test]
+ fn single_i32_with_one_character_being_3_bytes_long() {
+ let mut args = Args::new("1★2", &["★".to_string()]);
+
+ assert_eq!(args.single::<i32>().unwrap(), 1);
+ assert_eq!(args.single::<i32>().unwrap(), 2);
+ }
+
+ #[test]
+ fn single_i32_with_untrimmed_whitespaces() {
+ let mut args = Args::new(" 1, 2 ", &[",".to_string()]);
+
+ assert!(args.single::<i32>().is_err());
+ }
+
+ #[test]
+ fn single_i32_n() {
+ let args = Args::new("1,2", &[",".to_string()]);
+
+ assert_eq!(args.single_n::<i32>().unwrap(), 1);
+ assert_eq!(args.single_n::<i32>().unwrap(), 1);
+ }
+
+ #[test]
+ fn single_quoted_chaining() {
+ let mut args = Args::new(r#""1, 2" "2" """#, &[" ".to_string()]);
+
+ assert_eq!(args.single_quoted::<String>().unwrap(), "1, 2");
+ assert_eq!(args.single_quoted::<String>().unwrap(), "2");
+ assert_eq!(args.single_quoted::<String>().unwrap(), "");
+ }
+
+ #[test]
+ fn single_quoted_and_single_chaining() {
+ let mut args = Args::new(r#""1, 2" "2" "3" 4"#, &[" ".to_string()]);
+
+ assert_eq!(args.single_quoted::<String>().unwrap(), "1, 2");
+ assert!(args.single_n::<i32>().is_err());
+ assert_eq!(args.single::<String>().unwrap(), "\"2\"");
+ assert_eq!(args.single_quoted::<i32>().unwrap(), 3);
+ assert_eq!(args.single::<i32>().unwrap(), 4);
+ }
+
+ #[test]
+ fn full_on_args() {
+ let test_text = "Some text to ensure `full()` works.";
+ let args = Args::new(test_text, &[" ".to_string()]);
+
+ assert_eq!(args.full(), test_text);
+ }
+
+ #[test]
+ fn multiple_quoted_strings_one_delimiter() {
+ let args = Args::new(r#""1, 2" "a" "3" 4 "5"#, &[" ".to_string()]);
+
+ assert_eq!(args.multiple_quoted::<String>().unwrap(), ["1, 2", "a", "3", "4", "\"5"]);
+ }
+
+ #[test]
+ fn multiple_quoted_strings_with_multiple_delimiter() {
+ let args = Args::new(r#""1, 2" "a","3"4 "5"#, &[" ".to_string(), ",".to_string()]);
+
+ assert_eq!(args.multiple_quoted::<String>().unwrap(), ["1, 2", "a", "3", "4", "\"5"]);
+ }
+
+ #[test]
+ fn multiple_quoted_strings_with_multiple_delimiters() {
+ let args = Args::new(r#""1, 2" "a","3" """#, &[" ".to_string(), ",".to_string()]);
+
+ assert_eq!(args.multiple_quoted::<String>().unwrap(), ["1, 2", "a", "3", ""]);
+ }
+
+ #[test]
+ fn multiple_quoted_i32() {
+ let args = Args::new(r#""1" "2" 3"#, &[" ".to_string()]);
+
+ assert_eq!(args.multiple_quoted::<i32>().unwrap(), [1, 2, 3]);
+ }
+
+ #[test]
+ fn multiple_quoted_quote_appears_without_delimiter_in_front() {
+ let args = Args::new(r#"hello, my name is cake" 2"#, &[",".to_string(), " ".to_string()]);
+
+ assert_eq!(args.multiple_quoted::<String>().unwrap(), ["hello", "my", "name", "is", "cake\"", "2"]);
+ }
+
+ #[test]
+ fn multiple_quoted_single_quote() {
+ let args = Args::new(r#"hello "2 b"#, &[",".to_string(), " ".to_string()]);
+
+ assert_eq!(args.multiple_quoted::<String>().unwrap(), ["hello", "\"2 b"]);
+ }
+
+ #[test]
+ fn multiple_quoted_one_quote_pair() {
+ let args = Args::new(r#"hello "2 b""#, &[",".to_string(), " ".to_string()]);
+
+ assert_eq!(args.multiple_quoted::<String>().unwrap(), ["hello", "2 b"]);
+ }
+
+
+ #[test]
+ fn delimiter_before_multiple_quoted() {
+ let args = Args::new(r#","hello, my name is cake" "2""#, &[",".to_string(), " ".to_string()]);
+
+ assert_eq!(args.multiple_quoted::<String>().unwrap(), ["hello, my name is cake", "2"]);
+ }
+
+ #[test]
+ fn no_quote() {
+ let args = Args::new("hello, my name is cake", &[",".to_string(), " ".to_string()]);
+
+ assert_eq!(args.single_quoted_n::<String>().unwrap(), "hello");
+ }
+
+ #[test]
+ fn single_quoted_n() {
+ let args = Args::new(r#""hello, my name is cake","test"#, &[",".to_string()]);
+
+ assert_eq!(args.single_quoted_n::<String>().unwrap(), "hello, my name is cake");
+ assert_eq!(args.single_quoted_n::<String>().unwrap(), "hello, my name is cake");
+ }
+
+ #[test]
+ fn multiple_quoted_starting_with_wrong_delimiter_in_first_quote() {
+ let args = Args::new(r#""hello, my name is cake" "2""#, &[",".to_string(), " ".to_string()]);
+
+ assert_eq!(args.multiple_quoted::<String>().unwrap(), ["hello, my name is cake", "2"]);
+ }
+
+ #[test]
+ fn multiple_quoted_with_one_correct_and_one_invalid_quote() {
+ let args = Args::new(r#""hello, my name is cake" "2""#, &[",".to_string(), " ".to_string()]);
+
+ assert_eq!(args.multiple_quoted::<String>().unwrap(), ["hello, my name is cake", "2"]);
+ }
+
+ #[test]
+ fn find_i32_one_one_byte_delimiter() {
+ let mut args = Args::new("hello,my name is cake 2", &[" ".to_string()]);
+
+ assert_eq!(args.find::<i32>().unwrap(), 2);
+ }
+
+ #[test]
+ fn find_i32_one_three_byte_delimiter() {
+ let mut args = Args::new("hello,my name is cakeé2", &["é".to_string()]);
+
+ assert_eq!(args.find::<i32>().unwrap(), 2);
+ }
+
+ #[test]
+ fn find_i32_multiple_delimiter_but_i32_not_last() {
+ let mut args = Args::new("hello,my name is 2 cake", &[" ".to_string(), ",".to_string()]);
+
+ assert_eq!(args.find::<i32>().unwrap(), 2);
+ }
+
+ #[test]
+ fn find_i32_multiple_delimiter() {
+ let mut args = Args::new("hello,my name is cake 2", &[" ".to_string(), ",".to_string()]);
+
+ assert_eq!(args.find::<i32>().unwrap(), 2);
+ }
+
+ #[test]
+ fn find_n_i32() {
+ let mut args = Args::new("a 2", &[" ".to_string()]);
+
+ assert_eq!(args.find_n::<i32>().unwrap(), 2);
+ assert_eq!(args.find_n::<i32>().unwrap(), 2);
+ }
+
+ #[test]
+ fn skip() {
+ let mut args = Args::new("1 2", &[" ".to_string()]);
+
+ assert_eq!(args.skip().unwrap(), "1");
+ assert_eq!(args.remaining(), 1);
+ assert_eq!(args.single::<String>().unwrap(), "2");
+ }
+
+ #[test]
+ fn skip_for() {
+ let mut args = Args::new("1 2 neko 100", &[" ".to_string()]);
+
+ assert_eq!(args.skip_for(2).unwrap(), ["1", "2"]);
+ assert_eq!(args.remaining(), 2);
+ assert_eq!(args.single::<String>().unwrap(), "neko");
+ assert_eq!(args.single::<String>().unwrap(), "100");
+ }
+
+ #[test]
+ fn len_with_one_delimiter() {
+ let args = Args::new("1 2 neko 100", &[" ".to_string()]);
+
+ assert_eq!(args.len(), 4);
+ assert_eq!(args.remaining(), 4);
+ }
+
+ #[test]
+ fn len_multiple_quoted() {
+ let args = Args::new(r#""hello, my name is cake" "2""#, &[" ".to_string()]);
+
+ assert_eq!(args.len(), 2);
+ }
+
+ #[test]
+ fn remaining_len_before_and_after_single() {
+ let mut args = Args::new("1 2", &[" ".to_string()]);
+
+ assert_eq!(args.remaining(), 2);
+ assert_eq!(args.single::<i32>().unwrap(), 1);
+ assert_eq!(args.remaining(), 1);
+ assert_eq!(args.single::<i32>().unwrap(), 2);
+ assert_eq!(args.remaining(), 0);
+ }
+
+ #[test]
+ fn remaining_len_before_and_after_single_quoted() {
+ let mut args = Args::new(r#""1" "2" "3""#, &[" ".to_string()]);
+
+ assert_eq!(args.remaining(), 3);
+ assert_eq!(args.single_quoted::<i32>().unwrap(), 1);
+ assert_eq!(args.remaining(), 2);
+ assert_eq!(args.single_quoted::<i32>().unwrap(), 2);
+ assert_eq!(args.remaining(), 1);
+ assert_eq!(args.single_quoted::<i32>().unwrap(), 3);
+ assert_eq!(args.remaining(), 0);
+ }
+
+ #[test]
+ fn remaining_len_before_and_after_skip() {
+ let mut args = Args::new("1 2", &[" ".to_string()]);
+
+ assert_eq!(args.remaining(), 2);
+ assert_eq!(args.skip().unwrap(), "1");
+ assert_eq!(args.remaining(), 1);
+ assert_eq!(args.skip().unwrap(), "2");
+ assert_eq!(args.remaining(), 0);
+ }
+
+ #[test]
+ fn remaining_len_before_and_after_skip_empty_string() {
+ let mut args = Args::new("", &[" ".to_string()]);
+
+ assert_eq!(args.remaining(), 0);
+ assert_eq!(args.skip(), None);
+ assert_eq!(args.remaining(), 0);
+ }
+
+ #[test]
+ fn remaining_len_before_and_after_skip_for() {
+ let mut args = Args::new("1 2", &[" ".to_string()]);
+
+ assert_eq!(args.remaining(), 2);
+ assert_eq!(args.skip_for(2), Some(vec!["1".to_string(), "2".to_string()]));
+ assert_eq!(args.skip_for(2), None);
+ assert_eq!(args.remaining(), 0);
+ }
+
+ #[test]
+ fn remaining_len_before_and_after_find() {
+ let mut args = Args::new("a 2 6", &[" ".to_string()]);
+
+ assert_eq!(args.remaining(), 3);
+ assert_eq!(args.find::<i32>().unwrap(), 2);
+ assert_eq!(args.remaining(), 2);
+ assert_eq!(args.find::<i32>().unwrap(), 6);
+ assert_eq!(args.remaining(), 1);
+ assert_eq!(args.find::<String>().unwrap(), "a");
+ assert_eq!(args.remaining(), 0);
+ assert_matches!(args.find::<String>().unwrap_err(), ArgError::Eos);
+ assert_eq!(args.remaining(), 0);
+ }
+
+ #[test]
+ fn remaining_len_before_and_after_find_n() {
+ let mut args = Args::new("a 2 6", &[" ".to_string()]);
+
+ assert_eq!(args.remaining(), 3);
+ assert_eq!(args.find_n::<i32>().unwrap(), 2);
+ assert_eq!(args.remaining(), 3);
+ }
+
+
+ #[test]
+ fn multiple_strings_with_one_delimiter() {
+ let args = Args::new("hello, my name is cake 2", &[" ".to_string()]);
+
+ assert_eq!(args.multiple::<String>().unwrap(), ["hello,", "my", "name", "is", "cake", "2"]);
+ }
+
+ #[test]
+ fn multiple_i32_with_one_delimiter() {
+ let args = Args::new("1 2 3", &[" ".to_string()]);
+
+ assert_eq!(args.multiple::<i32>().unwrap(), [1, 2, 3]);
+ }
+
+ #[test]
+ fn multiple_i32_with_one_delimiter_and_parse_error() {
+ let args = Args::new("1 2 3 abc", &[" ".to_string()]);
+
+ assert_matches!(args.multiple::<i32>().unwrap_err(), ArgError::Parse(_));
+ }
+
+ #[test]
+ fn multiple_i32_with_three_delimiters() {
+ let args = Args::new("1 2 3", &[" ".to_string(), ",".to_string()]);
+
+ assert_eq!(args.multiple::<i32>().unwrap(), [1, 2, 3]);
+ }
+
+ #[test]
+ fn single_after_failed_single() {
+ let mut args = Args::new("b 2", &[" ".to_string()]);
+
+ assert_matches!(args.single::<i32>().unwrap_err(), ArgError::Parse(_));
+ // Test that `single` short-circuts on an error and leaves the source as is.
+ assert_eq!(args.remaining(), 2);
+ assert_eq!(args.single::<String>().unwrap(), "b");
+ assert_eq!(args.single::<String>().unwrap(), "2");
+ }
+
+ #[test]
+ fn remaining_len_after_failed_single_quoted() {
+ let mut args = Args::new("b a", &[" ".to_string()]);
+
+ assert_eq!(args.remaining(), 2);
+ // Same goes for `single_quoted` and the alike.
+ assert_matches!(args.single_quoted::<i32>().unwrap_err(), ArgError::Parse(_));
+ assert_eq!(args.remaining(), 2);
+ }
+
+ #[test]
+ fn no_delims_entire_message() {
+ let mut args = Args::new("abc", &[]);
+
+ assert_eq!(args.remaining(), 1);
+ assert_eq!(args.single::<String>().unwrap(), "abc");
+ assert_eq!(args.remaining(), 0);
+ }
+}
diff --git a/src/framework/standard/command.rs b/src/framework/standard/command.rs
index a6a6074..42264f2 100644
--- a/src/framework/standard/command.rs
+++ b/src/framework/standard/command.rs
@@ -338,15 +338,14 @@ impl Default for CommandOptions {
}
pub fn positions(ctx: &mut Context, msg: &Message, conf: &Configuration) -> Option<Vec<usize>> {
- if !conf.prefixes.is_empty() || conf.dynamic_prefix.is_some() {
- // Find out if they were mentioned. If not, determine if the prefix
- // was used. If not, return None.
- let mut positions: Vec<usize> = vec![];
+ // Mentions have the highest precedence.
+ if let Some(mention_end) = find_mention_end(&msg.content, conf) {
+ return Some(vec![mention_end]); // This can simply be returned without trying to find the end whitespaces as trim will remove it later
+ }
- if let Some(mention_end) = find_mention_end(&msg.content, conf) {
- positions.push(mention_end);
- return Some(positions);
- }
+ if !conf.prefixes.is_empty() || conf.dynamic_prefix.is_some() {
+ // Determine if a prefix was used. Otherwise return None.
+ let mut positions = Vec::new();
// Dynamic prefixes, if present and suitable, always have a higher priority.
if let Some(x) = conf.dynamic_prefix.as_ref().and_then(|f| f(ctx, msg)) {
@@ -390,10 +389,6 @@ pub fn positions(ctx: &mut Context, msg: &Message, conf: &Configuration) -> Opti
}
Some(positions)
- } else if conf.on_mention.is_some() {
- find_mention_end(&msg.content, conf).map(|mention_end| {
- vec![mention_end] // This can simply be returned without trying to find the end whitespaces as trim will remove it later
- })
} else {
None
}
diff --git a/src/framework/standard/configuration.rs b/src/framework/standard/configuration.rs
index f3d66d6..795493d 100644
--- a/src/framework/standard/configuration.rs
+++ b/src/framework/standard/configuration.rs
@@ -10,12 +10,14 @@ use std::{
};
use super::command::PrefixCheck;
-/// The configuration to use for a [`Framework`] associated with a [`Client`]
+/// The configuration to use for a [`StandardFramework`] associated with a [`Client`]
/// instance.
///
/// This allows setting configurations like the depth to search for commands,
/// whether to treat mentions like a command prefix, etc.
///
+/// To see the default values, refer to the [default implementation].
+///
/// # Examples
///
/// Responding to mentions and setting a command prefix of `"~"`:
@@ -38,7 +40,8 @@ use super::command::PrefixCheck;
/// ```
///
/// [`Client`]: ../../client/struct.Client.html
-/// [`Framework`]: struct.Framework.html
+/// [`StandardFramework`]: struct.StandardFramework.html
+/// [default implementation]: #impl-Default
pub struct Configuration {
#[doc(hidden)] pub allow_dm: bool,
#[doc(hidden)] pub allow_whitespace: bool,
@@ -60,6 +63,8 @@ pub struct Configuration {
impl Configuration {
/// If set to false, bot will ignore any private messages.
+ ///
+ /// **Note**: Defaults to `true`.
pub fn allow_dm(mut self, allow_dm: bool) -> Self {
self.allow_dm = allow_dm;
@@ -96,7 +101,9 @@ impl Configuration {
self
}
- /// HashSet of guild Ids where commands will be ignored.
+ /// HashSet of channels Ids where commands will be working.
+ ///
+ /// **Note**: Defaults to an empty HashSet.
///
/// # Examples
///
@@ -108,19 +115,21 @@ impl Configuration {
/// #
/// # impl EventHandler for Handler {}
/// # let mut client = Client::new("token", Handler).unwrap();
- /// use serenity::model::id::GuildId;
+ /// use serenity::model::id::ChannelId;
/// use serenity::framework::StandardFramework;
///
/// client.with_framework(StandardFramework::new().configure(|c| c
- /// .blocked_guilds(vec![GuildId(7), GuildId(77)].into_iter().collect())));
+ /// .allowed_channels(vec![ChannelId(7), ChannelId(77)].into_iter().collect())));
/// ```
- pub fn blocked_guilds(mut self, guilds: HashSet<GuildId>) -> Self {
- self.blocked_guilds = guilds;
+ pub fn allowed_channels(mut self, channels: HashSet<ChannelId>) -> Self {
+ self.allowed_channels = channels;
self
}
- /// HashSet of channels Ids where commands will be working.
+ /// HashSet of guild Ids where commands will be ignored.
+ ///
+ /// **Note**: Defaults to an empty HashSet.
///
/// # Examples
///
@@ -132,21 +141,24 @@ impl Configuration {
/// #
/// # impl EventHandler for Handler {}
/// # let mut client = Client::new("token", Handler).unwrap();
- /// use serenity::model::id::ChannelId;
+ /// use serenity::model::id::GuildId;
/// use serenity::framework::StandardFramework;
///
/// client.with_framework(StandardFramework::new().configure(|c| c
- /// .allowed_channels(vec![ChannelId(7), ChannelId(77)].into_iter().collect())));
+ /// .blocked_guilds(vec![GuildId(7), GuildId(77)].into_iter().collect())));
/// ```
- pub fn allowed_channels(mut self, channels: HashSet<ChannelId>) -> Self {
- self.allowed_channels = channels;
+ pub fn blocked_guilds(mut self, guilds: HashSet<GuildId>) -> Self {
+ self.blocked_guilds = guilds;
self
}
/// HashSet of user Ids whose commands will be ignored.
+ ///
/// Guilds owned by user Ids will also be ignored.
///
+ /// **Note**: Defaults to an empty HashSet.
+ ///
/// # Examples
///
/// Create a HashSet in-place:
@@ -169,9 +181,12 @@ impl Configuration {
self
}
- /// The default depth of the message to check for commands. Defaults to 5.
+ /// The default depth of the message to check for commands.
+ ///
/// This determines how "far" into a message to check for a valid command.
///
+ /// **Note**: Defaults to 5.
+ ///
/// # Examples
///
/// If you set a depth of `1`, and make a command of `"music play"`, but
@@ -185,6 +200,8 @@ impl Configuration {
/// HashSet of command names that won't be run.
///
+ /// **Note**: Defaults to an empty HashSet.
+ ///
/// # Examples
///
/// Ignore a set of commands, assuming they exist:
@@ -218,6 +235,8 @@ impl Configuration {
/// Return `None` to not have a special prefix for the dispatch, and to
/// instead use the inherited prefix.
///
+ /// **Note**: Defaults to no dynamic prefix check.
+ ///
/// # Examples
///
/// If the Id of the channel is divisible by 5, return a prefix of `"!"`,
@@ -256,6 +275,8 @@ impl Configuration {
///
/// For example, if this is set to false, then the bot will respond to any
/// other bots including itself.
+ ///
+ /// **Note**: Defaults to `true`.
pub fn ignore_bots(mut self, ignore_bots: bool) -> Self {
self.ignore_bots = ignore_bots;
@@ -263,7 +284,8 @@ impl Configuration {
}
/// If set to true, bot will ignore all commands called by webhooks.
- /// True by default.
+ ///
+ /// **Note**: Defaults to `true`.
pub fn ignore_webhooks(mut self, ignore_webhooks: bool) -> Self {
self.ignore_webhooks = ignore_webhooks;
@@ -273,7 +295,7 @@ impl Configuration {
/// Whether or not to respond to commands initiated with a mention. Note
/// that this can be used in conjunction with [`prefix`].
///
- /// By default this is set to `false`.
+ /// **Note**: Defaults to `false`.
///
/// # Examples
///
@@ -308,6 +330,8 @@ impl Configuration {
/// A `HashSet` of user Ids checks won't apply to.
///
+ /// **Note**: Defaults to an empty HashSet.
+ ///
/// # Examples
///
/// Create a HashSet in-place:
@@ -352,6 +376,8 @@ impl Configuration {
/// Sets the prefix to respond to. A prefix can be a string slice of any
/// non-zero length.
///
+ /// **Note**: Defaults to an empty vector.
+ ///
/// # Examples
///
/// Assign a basic prefix:
@@ -377,6 +403,8 @@ impl Configuration {
/// Sets the prefixes to respond to. Each can be a string slice of any
/// non-zero length.
///
+ /// **Note**: Refer to [`prefix`] for the default value.
+ ///
/// # Examples
///
/// Assign a set of prefixes the bot can respond to:
@@ -393,6 +421,8 @@ impl Configuration {
/// client.with_framework(StandardFramework::new().configure(|c| c
/// .prefixes(vec!["!", ">", "+"])));
/// ```
+ ///
+ /// [`prefix`]: #method.prefix
pub fn prefixes<T: ToString, It: IntoIterator<Item=T>>(mut self, prefixes: It) -> Self {
self.prefixes = prefixes.into_iter().map(|x| x.to_string()).collect();
@@ -401,7 +431,10 @@ impl Configuration {
/// Sets whether command execution can done without a prefix. Works only in private channels.
///
+ /// **Note**: Defaults to `false`.
+ ///
/// # Note
+ ///
/// Needs the `cache` feature to be enabled. Otherwise this does nothing.
pub fn no_dm_prefix(mut self, b: bool) -> Self {
self.no_dm_prefix = b;
@@ -411,6 +444,8 @@ impl Configuration {
/// Sets a delimiter to be used when splitting the content after a command.
///
+ /// **Note**: Defaults to a vector with a single element of `" "`.
+ ///
/// # Examples
///
/// Have the args be seperated by a comma and a space:
@@ -436,6 +471,8 @@ impl Configuration {
/// Sets multiple delimiters to be used when splitting the content after a command.
/// Additionally cleans the default delimiter from the vector.
///
+ /// **Note**: Refer to [`delimiter`] for the default value.
+ ///
/// # Examples
///
/// Have the args be seperated by a comma and a space; and a regular space:
@@ -452,6 +489,8 @@ impl Configuration {
/// client.with_framework(StandardFramework::new().configure(|c| c
/// .delimiters(vec![", ", " "])));
/// ```
+ ///
+ /// [`delimiter`]: #method.delimiter
pub fn delimiters<T: ToString, It: IntoIterator<Item=T>>(mut self, delimiters: It) -> Self {
self.delimiters.clear();
self.delimiters
@@ -460,9 +499,13 @@ impl Configuration {
self
}
- /// Whether the framework shouldn't care about the user's input if it's: `~command`,
- /// `~Command`, `~COMMAND`.
- /// Setting this to `true` will result in *all* command names to be case insensitive.
+ /// Whether the framework shouldn't care about the user's input if it's:
+ /// `~command`, `~Command`, or `~COMMAND`.
+ ///
+ /// Setting this to `true` will result in *all* command names to be case
+ /// insensitive.
+ ///
+ /// **Note**: Defaults to `false`.
pub fn case_insensitivity(mut self, cs: bool) -> Self {
self.case_insensitive = cs;
@@ -473,31 +516,40 @@ impl Configuration {
impl Default for Configuration {
/// Builds a default framework configuration, setting the following:
///
+ /// - **allow_dm** to `true`
/// - **allow_whitespace** to `false`
+ /// - **allowed_channels** to an empty HashSet
+ /// - **blocked_guilds** to an empty HashSet
+ /// - **blocked_users** to an empty HashSet
+ /// - **case_insensitive** to `false`
+ /// - **delimiters** to `vec![" "]`
/// - **depth** to `5`
- /// - **on_mention** to `false` (basically)
- /// - **prefix** to `None`
+ /// - **disabled_commands** to an empty HashSet
+ /// - **dynamic_prefix** to no dynamic prefix check
+ /// - **ignore_bots** to `true`
+ /// - **ignore_webhooks** to `true`
/// - **no_dm_prefix** to `false`
- /// - **delimiters** to vec![" "]
- /// - **case_insensitive** to `false`
+ /// - **on_mention** to `false` (basically)
+ /// - **owners** to an empty HashSet
+ /// - **prefix** to an empty vector
fn default() -> Configuration {
Configuration {
- depth: 5,
- on_mention: None,
- dynamic_prefix: None,
+ allow_dm: true,
allow_whitespace: false,
- prefixes: vec![],
- no_dm_prefix: false,
- ignore_bots: true,
- owners: HashSet::default(),
- blocked_users: HashSet::default(),
- blocked_guilds: HashSet::default(),
allowed_channels: HashSet::default(),
- disabled_commands: HashSet::default(),
- allow_dm: true,
- ignore_webhooks: true,
+ blocked_guilds: HashSet::default(),
+ blocked_users: HashSet::default(),
case_insensitive: false,
delimiters: vec![" ".to_string()],
+ depth: 5,
+ disabled_commands: HashSet::default(),
+ dynamic_prefix: None,
+ ignore_bots: true,
+ ignore_webhooks: true,
+ no_dm_prefix: false,
+ on_mention: None,
+ owners: HashSet::default(),
+ prefixes: vec![],
}
}
}
diff --git a/src/framework/standard/help_commands.rs b/src/framework/standard/help_commands.rs
index fd74ace..e891ff5 100644
--- a/src/framework/standard/help_commands.rs
+++ b/src/framework/standard/help_commands.rs
@@ -266,7 +266,7 @@ pub fn with_embeds<H: BuildHasher>(
&help_options.striked_commands_tip_in_dm
};
- if let Some(ref striked_command_text) = striked_command_tip {
+ if let &Some(ref striked_command_text) = striked_command_tip {
e = e.colour(help_options.embed_success_colour).description(
format!("{}\n{}", &help_options.individual_command_tip, striked_command_text),
);
@@ -510,7 +510,7 @@ pub fn plain<H: BuildHasher>(
&help_options.striked_commands_tip_in_dm
};
- if let Some(ref striked_command_text) = striked_command_tip {
+ if let &Some(ref striked_command_text) = striked_command_tip {
let _ = writeln!(result, "{}\n{}\n", &help_options.individual_command_tip, striked_command_text);
} else {
let _ = writeln!(result, "{}\n", &help_options.individual_command_tip);
diff --git a/src/framework/standard/mod.rs b/src/framework/standard/mod.rs
index d947bfd..86b8c59 100644
--- a/src/framework/standard/mod.rs
+++ b/src/framework/standard/mod.rs
@@ -53,12 +53,12 @@ use client::CACHE;
#[cfg(feature = "cache")]
use model::channel::Channel;
-/// A convenience macro for generating a struct fulfilling the [`Command`] trait.
+/// A convenience macro for generating a struct fulfilling the [`Command`][command trait] trait.
///
-/// This is meant for use with the [`Framework`], specifically `Framework`::{[`cmd`]/[`command`]}.
+/// This is meant for use with the [`StandardFramework`], specifically `Framework`::{[`cmd`]/[`command`]}.
///
///
-/// If you're just looking for a simple "register this function as a command", use [`Framework::on`].
+/// If you're just looking for a simple "register this function as a command", use [`StandardFramework::on`].
///
/// # Examples
///
@@ -87,11 +87,11 @@ use model::channel::Channel;
/// });
/// ```
///
-/// [`Framework`]: framework/index.html
-/// [`cmd`]: struct.Framework.html#method.cmd
-/// [`command`]: struct.Framework.html#method.command
-/// [`Framework::on`]: struct.Framework.html#method.on
-/// [`Command`]: trait.Command.html
+/// [command trait]: framework/standard/trait.Command.html
+/// [`StandardFramework`]: framework/standard/struct.StandardFramework.html
+/// [`cmd`]: framework/standard/struct.StandardFramework.html#method.cmd
+/// [`command`]: framework/standard/struct.StandardFramework.html#method.command
+/// [`StandardFramework::on`]: framework/standard/struct.StandardFramework.html#method.on
#[macro_export]
macro_rules! command {
($fname:ident($c:ident) $b:block) => {
@@ -147,6 +147,17 @@ macro_rules! command {
};
}
+macro_rules! command_and_help_args {
+ ($message_content:expr, $position:expr, $command_length:expr, $delimiters:expr) => {
+ {
+ let content = $message_content.chars().skip($position).skip_while(|x| x.is_whitespace())
+ .skip($command_length).collect::<String>();
+
+ Args::new(&content.trim(), $delimiters)
+ }
+ };
+}
+
/// An enum representing all possible fail conditions under which a command won't
/// be executed.
#[derive(Debug)]
@@ -215,13 +226,12 @@ pub struct StandardFramework {
/// - a command check has been set.
///
/// This is used internally to determine whether or not - in addition to
- /// dispatching to the [`EventHandler::on_message`] handler - to have the
+ /// dispatching to the [`EventHandler::message`] handler - to have the
/// framework check if a [`Event::MessageCreate`] should be processed by
/// itself.
///
- /// [`EventHandler::on_message`]:
- /// ../client/event_handler/trait.EventHandler.html#method.on_message
- /// [`Event::MessageCreate`]: ../model/event/enum.Event.html#variant.MessageCreate
+ /// [`EventHandler::message`]: ../../client/trait.EventHandler.html#method.message
+ /// [`Event::MessageCreate`]: ../../model/event/enum.Event.html#variant.MessageCreate
pub initialized: bool,
user_id: u64,
}
@@ -255,7 +265,7 @@ impl StandardFramework {
/// .prefix("~")));
/// ```
///
- /// [`Client`]: ../client/struct.Client.html
+ /// [`Client`]: ../../client/struct.Client.html
/// [`Configuration::default`]: struct.Configuration.html#method.default
/// [`depth`]: struct.Configuration.html#method.depth
/// [`prefix`]: struct.Configuration.html#method.prefix
@@ -325,12 +335,10 @@ impl StandardFramework {
///
/// client.with_framework(StandardFramework::new()
/// .complex_bucket("basic", 2, 10, 3, |_, guild_id, channel_id, user_id| {
- /// // check if the guild is `123` and the channel where the command(s) was called:
- /// // `456`
- /// // and if the user who called the command(s) is `789`
- /// // otherwise don't apply the bucket at all.
- /// guild_id.is_some() && guild_id.unwrap() == 123 && channel_id == 456
- /// && user_id == 789
+ /// // Our bucket is very strict. It cannot apply in DMs.
+ /// // And can only apply if it's in the specific guild, channel and by the specific user.
+ /// guild_id.is_some() && guild_id.unwrap() == 123 && channel_id == 456
+ /// && user_id == 789
/// })
/// .command("ping", |c| c
/// .bucket("basic")
@@ -338,7 +346,9 @@ impl StandardFramework {
/// msg.channel_id.say("pong!")?;
///
/// Ok(())
- /// })));
+ /// })
+ /// )
+ /// );
/// ```
///
/// [`bucket`]: #method.bucket
@@ -384,14 +394,18 @@ impl StandardFramework {
///
/// client.with_framework(StandardFramework::new()
/// .complex_bucket("basic", 2, 10, 3, |_, channel_id, user_id| {
- /// // check if the channel's id where the command(s) was called is `456`
- /// // and if the user who called the command(s) is `789`
- /// // otherwise don't apply the bucket at all.
+ /// Our bucket is somewhat strict. It can only apply in the specific channel and by the specific user.
/// channel_id == 456 && user_id == 789
/// })
/// .command("ping", |c| c
/// .bucket("basic")
- /// .exec_str("pong!")));
+ /// .exec(|_, msg, _| {
+ /// msg.channel_id.say("pong!")?;
+ ///
+ /// Ok(())
+ /// })
+ /// )
+ /// );
/// ```
///
/// [`bucket`]: #method.bucket
@@ -1033,14 +1047,16 @@ impl Framework for StandardFramework {
}
let mut check_contains_group_prefix = false;
+ let mut longest_matching_prefix_len = 0;
let to_check = if let Some(ref prefixes) = group.prefixes {
// Once `built` starts with a set prefix,
// we want to make sure that all following matching prefixes are longer
// than the last matching one, this prevents picking a wrong prefix,
// e.g. "f" instead of "ferris" due to "f" having a lower index in the `Vec`.
- let longest_matching_prefix_len = prefixes.iter().fold(0, |longest_prefix_len, prefix|
- if prefix.len() > longest_prefix_len && built.starts_with(prefix)
- && (orginal_round.len() == built.len() || command_length > prefix.len() + 1) {
+ longest_matching_prefix_len = prefixes.iter().fold(0, |longest_prefix_len, prefix|
+ if prefix.len() > longest_prefix_len
+ && built.starts_with(prefix)
+ && (orginal_round.len() == prefix.len() || built.get(prefix.len()..prefix.len() + 1) == Some(" ")) {
prefix.len()
} else {
longest_prefix_len
@@ -1060,13 +1076,6 @@ impl Framework for StandardFramework {
built.clone()
};
- let mut args = {
- let content = message.content.chars().skip(position).skip_while(|x| x.is_whitespace())
- .skip(command_length).collect::<String>();
-
- Args::new(&content.trim(), &self.configuration.delimiters)
- };
-
let before = self.before.clone();
let after = self.after.clone();
@@ -1075,6 +1084,8 @@ impl Framework for StandardFramework {
if let Some(help) = help {
let groups = self.groups.clone();
+ let mut args = command_and_help_args!(&message.content, position, command_length, &self.configuration.delimiters);
+
threadpool.execute(move || {
if let Some(before) = before {
@@ -1094,12 +1105,12 @@ impl Framework for StandardFramework {
}
}
-
if !to_check.is_empty() {
if let Some(&CommandOrAlias::Command(ref command)) =
group.commands.get(&to_check) {
let command = Arc::clone(command);
+ let mut args = command_and_help_args!(&message.content, position, command_length, &self.configuration.delimiters);
if let Some(error) = self.should_fail(
&mut context,
@@ -1142,12 +1153,16 @@ impl Framework for StandardFramework {
}
if check_contains_group_prefix {
- if let Some(CommandOrAlias::Command(ref command)) = &group.default_command {
+
+ if let &Some(CommandOrAlias::Command(ref command)) = &group.default_command {
let command = Arc::clone(command);
+ let mut args = {
+ Args::new(&orginal_round[longest_matching_prefix_len..], &self.configuration.delimiters)
+ };
threadpool.execute(move || {
if let Some(before) = before {
- if !(before)(&mut context, &message, &built) {
+ if !(before)(&mut context, &message, &args.full()) {
return;
}
}
@@ -1172,7 +1187,7 @@ impl Framework for StandardFramework {
}
}
- if let Some(unrecognised_command) = &self.unrecognised_command {
+ if let &Some(ref unrecognised_command) = &self.unrecognised_command {
let unrecognised_command = unrecognised_command.clone();
threadpool.execute(move || {
(unrecognised_command)(&mut context, &message, &unrecognised_command_name);
diff --git a/src/gateway/mod.rs b/src/gateway/mod.rs
index ffacba7..07d882c 100644
--- a/src/gateway/mod.rs
+++ b/src/gateway/mod.rs
@@ -3,9 +3,9 @@
//!
//! A shard is an interface for the lower-level receiver and sender. It provides
//! what can otherwise be thought of as "sugar methods". A shard represents a
-//! single connection to Discord. If acting as a [`Bot`] user, you can make
-//! use of a method named "sharding" to have multiple shards, potentially
-//! offloading some server load to another server(s).
+//! single connection to Discord. You can make use of a method named "sharding"
+//! to have multiple shards, potentially offloading some server load to another
+//! server(s).
//!
//! # Sharding
//!
@@ -38,15 +38,12 @@
//! instance. This should be used when you, for example, want to split 10 shards
//! across 3 instances.
//!
-//! **Note**: User accounts can not shard. Use [`Client::start`].
-//!
-//! [`Bot`]: ../enum.LoginType.html#variant.Bot
-//! [`Client`]: ../struct.Client.html
-//! [`Client::start`]: ../struct.Client.html#method.start
-//! [`Client::start_autosharded`]: ../struct.Client.html#method.start_autosharded
-//! [`Client::start_shard`]: ../struct.Client.html#method.start_shard
-//! [`Client::start_shard_range`]: ../struct.Client.html#method.start_shard_range
-//! [`Client::start_shards`]: ../struct.Client.html#method.start_shards
+//! [`Client`]: ../client/struct.Client.html
+//! [`Client::start`]: ../client/struct.Client.html#method.start
+//! [`Client::start_autosharded`]: ../client/struct.Client.html#method.start_autosharded
+//! [`Client::start_shard`]: ../client/struct.Client.html#method.start_shard
+//! [`Client::start_shard_range`]: ../client/struct.Client.html#method.start_shard_range
+//! [`Client::start_shards`]: ../client/struct.Client.html#method.start_shards
//! [docs]: https://discordapp.com/developers/docs/topics/gateway#sharding
mod error;
diff --git a/src/gateway/shard.rs b/src/gateway/shard.rs
index 13944e6..09a3b69 100644
--- a/src/gateway/shard.rs
+++ b/src/gateway/shard.rs
@@ -56,7 +56,7 @@ use websocket::{
///
/// See the documentation for [`new`] on how to use this.
///
-/// [`Client`]: ../struct.Client.html
+/// [`Client`]: ../client/struct.Client.html
/// [`new`]: #method.new
/// [`receive`]: #method.receive
/// [docs]: https://discordapp.com/developers/docs/topics/gateway#sharding
@@ -640,8 +640,8 @@ impl Shard {
/// Note that, if the shard is already in a stage of
/// [`ConnectionStage::Connecting`], then no action will be performed.
///
- /// [`ConnectionStage::Connecting`]: ../../../gateway/enum.ConnectionStage.html#variant.Connecting
- /// [`session_id`]: ../../../gateway/struct.Shard.html#method.session_id
+ /// [`ConnectionStage::Connecting`]: ../gateway/enum.ConnectionStage.html#variant.Connecting
+ /// [`session_id`]: ../gateway/struct.Shard.html#method.session_id
pub fn should_reconnect(&mut self) -> Option<ReconnectType> {
if self.stage == ConnectionStage::Connecting {
return None;
@@ -733,10 +733,9 @@ impl Shard {
/// # }
/// ```
///
- /// [`Event::GuildMembersChunk`]:
- /// ../../model/event/enum.Event.html#variant.GuildMembersChunk
- /// [`Guild`]: ../../model/guild/struct.Guild.html
- /// [`Member`]: ../../model/guild/struct.Member.html
+ /// [`Event::GuildMembersChunk`]: ../model/event/enum.Event.html#variant.GuildMembersChunk
+ /// [`Guild`]: ../model/guild/struct.Guild.html
+ /// [`Member`]: ../model/guild/struct.Member.html
pub fn chunk_guilds<It>(
&mut self,
guild_ids: It,
diff --git a/src/http/mod.rs b/src/http/mod.rs
index 71c57c6..d9a6a62 100644
--- a/src/http/mod.rs
+++ b/src/http/mod.rs
@@ -20,25 +20,26 @@
//! instance methods where possible, as they each offer different
//! levels of a high-level interface to the HTTP module.
//!
-//! [`Client`]: ../struct.Client.html
+//! [`Client`]: ../client/struct.Client.html
//! [model]: ../model/index.html
pub mod ratelimiting;
+pub mod request;
+pub mod routing;
mod error;
-pub use self::error::Error as HttpError;
pub use hyper::status::{StatusClass, StatusCode};
+pub use self::error::Error as HttpError;
use constants;
use hyper::{
client::{
Client as HyperClient,
- Request,
- RequestBuilder,
+ Request as HyperRequest,
Response as HyperResponse
},
- header::ContentType,
+ header::{ContentType, Headers},
method::Method,
mime::{Mime, SubLevel, TopLevel},
net::HttpsConnector,
@@ -52,25 +53,35 @@ use internal::prelude::*;
use model::prelude::*;
use multipart::client::Multipart;
use parking_lot::Mutex;
-use self::ratelimiting::Route;
+use self::{
+ request::Request,
+ routing::RouteInfo,
+};
+use serde::de::DeserializeOwned;
use serde_json;
use std::{
collections::BTreeMap,
default::Default,
- fmt::Write as FmtWrite,
fs::File,
io::ErrorKind as IoErrorKind,
path::{Path, PathBuf},
sync::Arc
};
+lazy_static! {
+ static ref CLIENT: HyperClient = {
+ let tc = NativeTlsClient::new().expect("Unable to make http client");
+ let connector = HttpsConnector::new(tc);
+
+ HyperClient::with_connector(connector)
+ };
+}
+
/// An method used for ratelimiting special routes.
///
/// This is needed because `hyper`'s `Method` enum does not derive Copy.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum LightMethod {
- /// Indicates that a route is for "any" method.
- Any,
/// Indicates that a route is for the `DELETE` method only.
Delete,
/// Indicates that a route is for the `GET` method only.
@@ -83,6 +94,18 @@ pub enum LightMethod {
Put,
}
+impl LightMethod {
+ pub fn hyper_method(&self) -> Method {
+ match *self {
+ LightMethod::Delete => Method::Delete,
+ LightMethod::Get => Method::Get,
+ LightMethod::Patch => Method::Patch,
+ LightMethod::Post => Method::Post,
+ LightMethod::Put => Method::Put,
+ }
+ }
+}
+
lazy_static! {
static ref TOKEN: Arc<Mutex<String>> = Arc::new(Mutex::new(String::default()));
}
@@ -121,16 +144,11 @@ pub fn set_token(token: &str) { TOKEN.lock().clone_from(&token.to_string()); }
/// [`Group::add_recipient`]: ../model/channel/struct.Group.html#method.add_recipient
/// [`User`]: ../model/user/struct.User.html
pub fn add_group_recipient(group_id: u64, user_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::None,
- put,
- "/channels/{}/recipients/{}",
- group_id,
- user_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::AddGroupRecipient { group_id, user_id },
+ })
}
/// Adds a single [`Role`] to a [`Member`] in a [`Guild`].
@@ -141,19 +159,13 @@ pub fn add_group_recipient(group_id: u64, user_id: u64) -> Result<()> {
/// [`Guild`]: ../model/guild/struct.Guild.html
/// [`Member`]: ../model/guild/struct.Member.html
/// [`Role`]: ../model/guild/struct.Role.html
-/// [Manage Roles]: ../model/permissions/constant.MANAGE_ROLES.html
+/// [Manage Roles]: ../model/permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
pub fn add_member_role(guild_id: u64, user_id: u64, role_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::GuildsIdMembersIdRolesId(guild_id),
- put,
- "/guilds/{}/members/{}/roles/{}",
- guild_id,
- user_id,
- role_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::AddMemberRole { guild_id, role_id, user_id },
+ })
}
/// Bans a [`User`] from a [`Guild`], removing their messages sent in the last
@@ -166,20 +178,18 @@ pub fn add_member_role(guild_id: u64, user_id: u64, role_id: u64) -> Result<()>
///
/// [`Guild`]: ../model/guild/struct.Guild.html
/// [`User`]: ../model/user/struct.User.html
-/// [Ban Members]: ../model/permissions/constant.BAN_MEMBERS.html
+/// [Ban Members]: ../model/permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
pub fn ban_user(guild_id: u64, user_id: u64, delete_message_days: u8, reason: &str) -> Result<()> {
- verify(
- 204,
- request!(
- Route::GuildsIdBansUserId(guild_id),
- put,
- "/guilds/{}/bans/{}?delete_message_days={}&reason={}",
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GuildBanUser {
+ delete_message_days: Some(delete_message_days),
+ reason: Some(reason),
guild_id,
user_id,
- delete_message_days,
- reason
- ),
- )
+ },
+ })
}
/// Ban zeyla from a [`Guild`], removing her messages sent in the last X number
@@ -191,7 +201,7 @@ pub fn ban_user(guild_id: u64, user_id: u64, delete_message_days: u8, reason: &s
/// **Note**: Requires that you have the [Ban Members] permission.
///
/// [`Guild`]: ../model/guild/struct.Guild.html
-/// [Ban Members]: ../model/permissions/constant.BAN_MEMBERS.html
+/// [Ban Members]: ../model/permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
pub fn ban_zeyla(guild_id: u64, delete_message_days: u8, reason: &str) -> Result<()> {
ban_user(guild_id, 114_941_315_417_899_012, delete_message_days, reason)
}
@@ -205,7 +215,7 @@ pub fn ban_zeyla(guild_id: u64, delete_message_days: u8, reason: &str) -> Result
/// **Note**: Requires that you have the [Ban Members] permission.
///
/// [`Guild`]: ../model/guild/struct.Guild.html
-/// [Ban Members]: ../model/permissions/constant.BAN_MEMBERS.html
+/// [Ban Members]: ../model/permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
pub fn ban_luna(guild_id: u64, delete_message_days: u8, reason: &str) -> Result<()> {
ban_user(guild_id, 180_731_582_049_550_336, delete_message_days, reason)
}
@@ -219,7 +229,7 @@ pub fn ban_luna(guild_id: u64, delete_message_days: u8, reason: &str) -> Result<
/// **Note**: Requires that you have the [Ban Members] permission.
///
/// [`Guild`]: ../model/guild/struct.Guild.html
-/// [Ban Members]: ../model/permissions/constant.BAN_MEMBERS.html
+/// [Ban Members]: ../model/permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
pub fn ban_servermoms(guild_id: u64, delete_message_days: u8, reason: &str) -> Result<()> {
ban_zeyla(guild_id, delete_message_days, reason)?;
ban_luna(guild_id, delete_message_days, reason)
@@ -235,15 +245,11 @@ pub fn ban_servermoms(guild_id: u64, delete_message_days: u8, reason: &str) -> R
///
/// [`Channel`]: ../model/channel/enum.Channel.html
pub fn broadcast_typing(channel_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::ChannelsIdTyping(channel_id),
- post,
- "/channels/{}/typing",
- channel_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::BroadcastTyping { channel_id },
+ })
}
/// Creates a [`GuildChannel`] in the [`Guild`] given its Id.
@@ -255,41 +261,31 @@ pub fn broadcast_typing(channel_id: u64) -> Result<()> {
/// [`Guild`]: ../model/guild/struct.Guild.html
/// [`GuildChannel`]: ../model/channel/struct.GuildChannel.html
/// [docs]: https://discordapp.com/developers/docs/resources/guild#create-guild-channel
-/// [Manage Channels]: ../model/permissions/constant.MANAGE_CHANNELS.html
+/// [Manage Channels]: ../model/permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNELS
pub fn create_channel(guild_id: u64, map: &Value) -> Result<GuildChannel> {
- let body = map.to_string();
- let response = request!(
- Route::GuildsIdChannels(guild_id),
- post(body),
- "/guilds/{}/channels",
- guild_id
- );
-
- serde_json::from_reader::<HyperResponse, GuildChannel>(response)
- .map_err(From::from)
+ fire(Request {
+ body: Some(map.to_string().as_bytes()),
+ headers: None,
+ route: RouteInfo::CreateChannel { guild_id },
+ })
}
/// Creates an emoji in the given [`Guild`] with the given data.
///
-/// View the source code for [`Context::create_emoji`] to see what fields this
-/// requires.
+/// View the source code for [`Guild`]'s [`create_emoji`] method to see what
+/// fields this requires.
///
/// **Note**: Requires the [Manage Emojis] permission.
///
-/// [`Context::create_emoji`]: ../struct.Context.html#method.create_emoji
+/// [`create_emoji`]: ../model/guild/struct.Guild.html#method.create_emoji
/// [`Guild`]: ../model/guild/struct.Guild.html
-/// [Manage Emojis]: ../model/permissions/constant.MANAGE_EMOJIS.html
+/// [Manage Emojis]: ../model/permissions/struct.Permissions.html#associatedconstant.MANAGE_EMOJIS
pub fn create_emoji(guild_id: u64, map: &Value) -> Result<Emoji> {
- let body = map.to_string();
- let response = request!(
- Route::GuildsIdEmojis(guild_id),
- post(body),
- "/guilds/{}/emojis",
- guild_id
- );
-
- serde_json::from_reader::<HyperResponse, Emoji>(response)
- .map_err(From::from)
+ fire(Request {
+ body: Some(map.to_string().as_bytes()),
+ headers: None,
+ route: RouteInfo::CreateEmoji { guild_id },
+ })
}
/// Creates a guild with the data provided.
@@ -329,11 +325,11 @@ pub fn create_emoji(guild_id: u64, map: &Value) -> Result<Emoji> {
/// https://discordapp.com/developers/docs/resources/guild#create-guild
/// [whitelist]: https://discordapp.com/developers/docs/resources/guild#create-guild
pub fn create_guild(map: &Value) -> Result<PartialGuild> {
- let body = map.to_string();
- let response = request!(Route::Guilds, post(body), "/guilds");
-
- serde_json::from_reader::<HyperResponse, PartialGuild>(response)
- .map_err(From::from)
+ fire(Request {
+ body: Some(map.to_string().as_bytes()),
+ headers: None,
+ route: RouteInfo::CreateGuild,
+ })
}
/// Creates an [`Integration`] for a [`Guild`].
@@ -344,21 +340,14 @@ pub fn create_guild(map: &Value) -> Result<PartialGuild> {
///
/// [`Guild`]: ../model/guild/struct.Guild.html
/// [`Integration`]: ../model/guild/struct.Integration.html
-/// [Manage Guild]: ../model/permissions/constant.MANAGE_GUILD.html
+/// [Manage Guild]: ../model/permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
/// [docs]: https://discordapp.com/developers/docs/resources/guild#create-guild-integration
pub fn create_guild_integration(guild_id: u64, integration_id: u64, map: &Value) -> Result<()> {
- let body = map.to_string();
-
- verify(
- 204,
- request!(
- Route::GuildsIdIntegrations(guild_id),
- post(body),
- "/guilds/{}/integrations/{}",
- guild_id,
- integration_id
- ),
- )
+ wind(204, Request {
+ body: Some(map.to_string().as_bytes()),
+ headers: None,
+ route: RouteInfo::CreateGuildIntegration { guild_id, integration_id },
+ })
}
/// Creates a [`RichInvite`] for the given [channel][`GuildChannel`].
@@ -370,45 +359,39 @@ pub fn create_guild_integration(guild_id: u64, integration_id: u64, map: &Value)
/// **Note**: Requires the [Create Invite] permission.
///
/// [`GuildChannel`]: ../model/channel/struct.GuildChannel.html
-/// [`RichInvite`]: ../model/guild/struct.RichInvite.html
-/// [Create Invite]: ../model/permissions/constant.CREATE_INVITE.html
+/// [`RichInvite`]: ../model/invite/struct.RichInvite.html
+/// [Create Invite]: ../model/permissions/struct.Permissions.html#associatedconstant.CREATE_INVITE
/// [docs]: https://discordapp.com/developers/docs/resources/channel#create-channel-invite
pub fn create_invite(channel_id: u64, map: &JsonMap) -> Result<RichInvite> {
- let body = serde_json::to_string(map)?;
- let response = request!(
- Route::ChannelsIdInvites(channel_id),
- post(body),
- "/channels/{}/invites",
- channel_id
- );
-
- serde_json::from_reader::<HyperResponse, RichInvite>(response)
- .map_err(From::from)
+ let body = serde_json::to_vec(map)?;
+
+ fire(Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::CreateInvite { channel_id },
+ })
}
/// Creates a permission override for a member or a role in a channel.
pub fn create_permission(channel_id: u64, target_id: u64, map: &Value) -> Result<()> {
- let body = map.to_string();
-
- verify(
- 204,
- request!(
- Route::ChannelsIdPermissionsOverwriteId(channel_id),
- put(body),
- "/channels/{}/permissions/{}",
- channel_id,
- target_id
- ),
- )
+ let body = serde_json::to_vec(map)?;
+
+ wind(204, Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::CreatePermission { channel_id, target_id },
+ })
}
/// Creates a private channel with a user.
pub fn create_private_channel(map: &Value) -> Result<PrivateChannel> {
- let body = map.to_string();
- let response = request!(Route::UsersMeChannels, post(body), "/users/@me/channels");
+ let body = serde_json::to_vec(map)?;
- serde_json::from_reader::<HyperResponse, PrivateChannel>(response)
- .map_err(From::from)
+ fire(Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::GetUserDmChannels,
+ })
}
/// Reacts to a message.
@@ -416,31 +399,26 @@ pub fn create_reaction(channel_id: u64,
message_id: u64,
reaction_type: &ReactionType)
-> Result<()> {
- verify(
- 204,
- request!(
- Route::ChannelsIdMessagesIdReactionsUserIdType(channel_id),
- put,
- "/channels/{}/messages/{}/reactions/{}/@me",
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::CreateReaction {
+ reaction: &reaction_type.as_data(),
channel_id,
message_id,
- reaction_type.as_data()
- ),
- )
+ },
+ })
}
/// Creates a role.
pub fn create_role(guild_id: u64, map: &JsonMap) -> Result<Role> {
- let body = serde_json::to_string(map)?;
- let response = request!(
- Route::GuildsIdRoles(guild_id),
- post(body),
- "/guilds/{}/roles",
- guild_id
- );
-
- serde_json::from_reader::<HyperResponse, Role>(response)
- .map_err(From::from)
+ let body = serde_json::to_vec(map)?;
+
+ fire(Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::CreateRole {guild_id },
+ })
}
/// Creates a webhook for the given [channel][`GuildChannel`]'s Id, passing in
@@ -474,103 +452,77 @@ pub fn create_role(guild_id: u64, map: &JsonMap) -> Result<Role> {
///
/// [`GuildChannel`]: ../model/channel/struct.GuildChannel.html
pub fn create_webhook(channel_id: u64, map: &Value) -> Result<Webhook> {
- let body = map.to_string();
- let response = request!(
- Route::ChannelsIdWebhooks(channel_id),
- post(body),
- "/channels/{}/webhooks",
- channel_id
- );
-
- serde_json::from_reader::<HyperResponse, Webhook>(response)
- .map_err(From::from)
+ let body = serde_json::to_vec(map)?;
+
+ fire(Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::CreateWebhook { channel_id },
+ })
}
/// Deletes a private channel or a channel in a guild.
pub fn delete_channel(channel_id: u64) -> Result<Channel> {
- let response = request!(
- Route::ChannelsId(channel_id),
- delete,
- "/channels/{}",
- channel_id
- );
-
- serde_json::from_reader::<HyperResponse, Channel>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::DeleteChannel { channel_id },
+ })
}
/// Deletes an emoji from a server.
pub fn delete_emoji(guild_id: u64, emoji_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::GuildsIdEmojisId(guild_id),
- delete,
- "/guilds/{}/emojis/{}",
- guild_id,
- emoji_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::DeleteEmoji { guild_id, emoji_id },
+ })
}
/// Deletes a guild, only if connected account owns it.
pub fn delete_guild(guild_id: u64) -> Result<PartialGuild> {
- let response = request!(Route::GuildsId(guild_id), delete, "/guilds/{}", guild_id);
-
- serde_json::from_reader::<HyperResponse, PartialGuild>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::DeleteGuild { guild_id },
+ })
}
/// Remvoes an integration from a guild.
pub fn delete_guild_integration(guild_id: u64, integration_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::GuildsIdIntegrationsId(guild_id),
- delete,
- "/guilds/{}/integrations/{}",
- guild_id,
- integration_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::DeleteGuildIntegration { guild_id, integration_id },
+ })
}
/// Deletes an invite by code.
pub fn delete_invite(code: &str) -> Result<Invite> {
- let response = request!(Route::InvitesCode, delete, "/invites/{}", code);
-
- serde_json::from_reader::<HyperResponse, Invite>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::DeleteInvite { code },
+ })
}
/// Deletes a message if created by us or we have
/// specific permissions.
pub fn delete_message(channel_id: u64, message_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::ChannelsIdMessagesId(LightMethod::Delete, channel_id),
- delete,
- "/channels/{}/messages/{}",
- channel_id,
- message_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::DeleteMessage { channel_id, message_id },
+ })
}
/// Deletes a bunch of messages, only works for bots.
pub fn delete_messages(channel_id: u64, map: &Value) -> Result<()> {
- let body = map.to_string();
-
- verify(
- 204,
- request!(
- Route::ChannelsIdMessagesBulkDelete(channel_id),
- post(body),
- "/channels/{}/messages/bulk-delete",
- channel_id
- ),
- )
+ wind(204, Request {
+ body: Some(map.to_string().as_bytes()),
+ headers: None,
+ route: RouteInfo::DeleteMessages { channel_id },
+ })
}
/// Deletes all of the [`Reaction`]s associated with a [`Message`].
@@ -591,30 +543,20 @@ pub fn delete_messages(channel_id: u64, map: &Value) -> Result<()> {
/// [`Message`]: ../model/channel/struct.Message.html
/// [`Reaction`]: ../model/channel/struct.Reaction.html
pub fn delete_message_reactions(channel_id: u64, message_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::ChannelsIdMessagesIdReactions(channel_id),
- delete,
- "/channels/{}/messages/{}/reactions",
- channel_id,
- message_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::DeleteMessageReactions { channel_id, message_id },
+ })
}
/// Deletes a permission override from a role or a member in a channel.
pub fn delete_permission(channel_id: u64, target_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::ChannelsIdPermissionsOverwriteId(channel_id),
- delete,
- "/channels/{}/permissions/{}",
- channel_id,
- target_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::DeletePermission { channel_id, target_id },
+ })
}
/// Deletes a reaction from a message if owned by us or
@@ -628,32 +570,25 @@ pub fn delete_reaction(channel_id: u64,
.map(|uid| uid.to_string())
.unwrap_or_else(|| "@me".to_string());
- verify(
- 204,
- request!(
- Route::ChannelsIdMessagesIdReactionsUserIdType(channel_id),
- delete,
- "/channels/{}/messages/{}/reactions/{}/{}",
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::DeleteReaction {
+ reaction: &reaction_type.as_data(),
+ user: &user,
channel_id,
message_id,
- reaction_type.as_data(),
- user
- ),
- )
+ },
+ })
}
/// Deletes a role from a server. Can't remove the default everyone role.
pub fn delete_role(guild_id: u64, role_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::GuildsIdRolesId(guild_id),
- delete,
- "/guilds/{}/roles/{}",
- guild_id,
- role_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::DeleteRole { guild_id, role_id },
+ })
}
/// Deletes a [`Webhook`] given its Id.
@@ -666,7 +601,7 @@ pub fn delete_role(guild_id: u64, role_id: u64) -> Result<()> {
/// Deletes a webhook given its Id:
///
/// ```rust,no_run
-/// use serenity::{Client, http};
+/// use serenity::http;
/// use std::env;
///
/// // Due to the `delete_webhook` function requiring you to authenticate, you
@@ -679,15 +614,11 @@ pub fn delete_role(guild_id: u64, role_id: u64) -> Result<()> {
/// [`Webhook`]: ../model/webhook/struct.Webhook.html
/// [`delete_webhook_with_token`]: fn.delete_webhook_with_token.html
pub fn delete_webhook(webhook_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::WebhooksId(webhook_id),
- delete,
- "/webhooks/{}",
- webhook_id,
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::DeleteWebhook { webhook_id },
+ })
}
/// Deletes a [`Webhook`] given its Id and unique token.
@@ -709,123 +640,93 @@ pub fn delete_webhook(webhook_id: u64) -> Result<()> {
///
/// [`Webhook`]: ../model/webhook/struct.Webhook.html
pub fn delete_webhook_with_token(webhook_id: u64, token: &str) -> Result<()> {
- let client = request_client!();
-
- verify(
- 204,
- retry(|| {
- client
- .delete(&format!(api!("/webhooks/{}/{}"), webhook_id, token))
- }).map_err(Error::Hyper)?,
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::DeleteWebhookWithToken { token, webhook_id },
+ })
}
/// Changes channel information.
pub fn edit_channel(channel_id: u64, map: &JsonMap) -> Result<GuildChannel> {
- let body = serde_json::to_string(map)?;
- let response = request!(
- Route::ChannelsId(channel_id),
- patch(body),
- "/channels/{}",
- channel_id
- );
-
- serde_json::from_reader::<HyperResponse, GuildChannel>(response)
- .map_err(From::from)
+ let body = serde_json::to_vec(map)?;
+
+ fire(Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::EditChannel {channel_id },
+ })
}
/// Changes emoji information.
pub fn edit_emoji(guild_id: u64, emoji_id: u64, map: &Value) -> Result<Emoji> {
- let body = map.to_string();
- let response = request!(
- Route::GuildsIdEmojisId(guild_id),
- patch(body),
- "/guilds/{}/emojis/{}",
- guild_id,
- emoji_id
- );
-
- serde_json::from_reader::<HyperResponse, Emoji>(response)
- .map_err(From::from)
+ let body = serde_json::to_vec(map)?;
+
+ fire(Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::EditEmoji { guild_id, emoji_id },
+ })
}
/// Changes guild information.
pub fn edit_guild(guild_id: u64, map: &JsonMap) -> Result<PartialGuild> {
- let body = serde_json::to_string(map)?;
- let response = request!(
- Route::GuildsId(guild_id),
- patch(body),
- "/guilds/{}",
- guild_id
- );
-
- serde_json::from_reader::<HyperResponse, PartialGuild>(response)
- .map_err(From::from)
+ let body = serde_json::to_vec(map)?;
+
+ fire(Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::EditGuild { guild_id },
+ })
}
/// Edits the positions of a guild's channels.
pub fn edit_guild_channel_positions(guild_id: u64, value: &Value)
-> Result<()> {
- let body = serde_json::to_string(value)?;
-
- verify(
- 204,
- request!(
- Route::GuildsIdChannels(guild_id),
- patch(body),
- "/guilds/{}/channels",
- guild_id,
- ),
- )
+ let body = serde_json::to_vec(value)?;
+
+ wind(204, Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::EditGuildChannels { guild_id },
+ })
}
/// Edits a [`Guild`]'s embed setting.
///
/// [`Guild`]: ../model/guild/struct.Guild.html
pub fn edit_guild_embed(guild_id: u64, map: &Value) -> Result<GuildEmbed> {
- let body = map.to_string();
- let response = request!(
- Route::GuildsIdEmbed(guild_id),
- patch(body),
- "/guilds/{}/embed",
- guild_id
- );
-
- serde_json::from_reader::<HyperResponse, GuildEmbed>(response)
- .map_err(From::from)
+ let body = serde_json::to_vec(map)?;
+
+ fire(Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::EditGuildEmbed { guild_id },
+ })
}
/// Does specific actions to a member.
pub fn edit_member(guild_id: u64, user_id: u64, map: &JsonMap) -> Result<()> {
- let body = serde_json::to_string(map)?;
-
- verify(
- 204,
- request!(
- Route::GuildsIdMembersId(guild_id),
- patch(body),
- "/guilds/{}/members/{}",
- guild_id,
- user_id
- ),
- )
+ let body = serde_json::to_vec(map)?;
+
+ wind(204, Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::EditMember { guild_id, user_id },
+ })
}
/// Edits a message by Id.
///
/// **Note**: Only the author of a message can modify it.
pub fn edit_message(channel_id: u64, message_id: u64, map: &Value) -> Result<Message> {
- let body = map.to_string();
- let response = request!(
- Route::ChannelsIdMessagesId(LightMethod::Any, channel_id),
- patch(body),
- "/channels/{}/messages/{}",
- channel_id,
- message_id
- );
+ let body = serde_json::to_vec(map)?;
- serde_json::from_reader::<HyperResponse, Message>(response)
- .map_err(From::from)
+ fire(Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::EditMessage { channel_id, message_id },
+ })
}
/// Edits the current user's nickname for the provided [`Guild`] via its Id.
@@ -835,15 +736,13 @@ pub fn edit_message(channel_id: u64, message_id: u64, map: &Value) -> Result<Mes
/// [`Guild`]: ../model/guild/struct.Guild.html
pub fn edit_nickname(guild_id: u64, new_nickname: Option<&str>) -> Result<()> {
let map = json!({ "nick": new_nickname });
- let body = map.to_string();
- let response = request!(
- Route::GuildsIdMembersMeNick(guild_id),
- patch(body),
- "/guilds/{}/members/@me/nick",
- guild_id
- );
+ let body = serde_json::to_vec(&map)?;
- verify(200, response)
+ wind(200, Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::EditNickname { guild_id },
+ })
}
/// Edits the current user's profile settings.
@@ -859,8 +758,13 @@ pub fn edit_nickname(guild_id: u64, new_nickname: Option<&str>) -> Result<()> {
/// change and when the token is internally changed to be invalid requests, as
/// the token may be outdated.
pub fn edit_profile(map: &JsonMap) -> Result<CurrentUser> {
- let body = serde_json::to_string(map)?;
- let response = request!(Route::UsersMe, patch(body), "/users/@me");
+ let body = serde_json::to_vec(map)?;
+
+ let response = request(Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::EditProfile,
+ })?;
let mut value = serde_json::from_reader::<HyperResponse, Value>(response)?;
@@ -872,41 +776,32 @@ pub fn edit_profile(map: &JsonMap) -> Result<CurrentUser> {
}
}
- serde_json::from_value::<CurrentUser>(value)
- .map_err(From::from)
+ serde_json::from_value::<CurrentUser>(value).map_err(From::from)
}
/// Changes a role in a guild.
pub fn edit_role(guild_id: u64, role_id: u64, map: &JsonMap) -> Result<Role> {
- let body = serde_json::to_string(map)?;
- let response = request!(
- Route::GuildsIdRolesId(guild_id),
- patch(body),
- "/guilds/{}/roles/{}",
- guild_id,
- role_id
- );
-
- serde_json::from_reader::<HyperResponse, Role>(response)
- .map_err(From::from)
+ let body = serde_json::to_vec(&map)?;
+
+ fire(Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::EditRole { guild_id, role_id },
+ })
}
/// Changes the position of a role in a guild.
pub fn edit_role_position(guild_id: u64, role_id: u64, position: u64) -> Result<Vec<Role>> {
- let body = serde_json::to_string(&json!({
+ let body = serde_json::to_vec(&json!({
"id": role_id,
"position": position,
}))?;
- let response = request!(
- Route::GuildsIdRolesId(guild_id),
- patch(body),
- "/guilds/{}/roles/{}",
- guild_id,
- role_id
- );
-
- serde_json::from_reader::<HyperResponse, Vec<Role>>(response)
- .map_err(From::from)
+
+ fire(Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::EditRole { guild_id, role_id },
+ })
}
/// Edits a the webhook with the given data.
@@ -949,16 +844,11 @@ pub fn edit_role_position(guild_id: u64, role_id: u64, position: u64) -> Result<
// The tests are ignored, rather than no_run'd, due to rustdoc tests with
// external crates being incredibly messy and misleading in the end user's view.
pub fn edit_webhook(webhook_id: u64, map: &Value) -> Result<Webhook> {
- let body = map.to_string();
- let response = request!(
- Route::WebhooksId(webhook_id),
- patch(body),
- "/webhooks/{}",
- webhook_id,
- );
-
- serde_json::from_reader::<HyperResponse, Webhook>(response)
- .map_err(From::from)
+ fire(Request {
+ body: Some(map.to_string().as_bytes()),
+ headers: None,
+ route: RouteInfo::EditWebhook { webhook_id },
+ })
}
/// Edits the webhook with the given data.
@@ -988,17 +878,13 @@ pub fn edit_webhook(webhook_id: u64, map: &Value) -> Result<Webhook> {
///
/// [`edit_webhook`]: fn.edit_webhook.html
pub fn edit_webhook_with_token(webhook_id: u64, token: &str, map: &JsonMap) -> Result<Webhook> {
- let body = serde_json::to_string(map)?;
- let client = request_client!();
+ let body = serde_json::to_vec(map)?;
- let response = retry(|| {
- client
- .patch(&format!(api!("/webhooks/{}/{}"), webhook_id, token))
- .body(&body)
- }).map_err(Error::Hyper)?;
-
- serde_json::from_reader::<HyperResponse, Webhook>(response)
- .map_err(From::from)
+ fire(Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::EditWebhookWithToken { token, webhook_id },
+ })
}
/// Executes a webhook, posting a [`Message`] in the webhook's associated
@@ -1066,23 +952,18 @@ pub fn execute_webhook(webhook_id: u64,
wait: bool,
map: &JsonMap)
-> Result<Option<Message>> {
- let body = serde_json::to_string(map)?;
-
- let client = request_client!();
-
- let response = retry(|| {
- client
- .post(&format!(
- api!("/webhooks/{}/{}?wait={}"),
- webhook_id,
- token,
- wait
- ))
- .body(&body)
- .header(ContentType(
- Mime(TopLevel::Application, SubLevel::Json, vec![]),
- ))
- }).map_err(Error::Hyper)?;
+ let body = serde_json::to_vec(map)?;
+
+ let mut headers = Headers::new();
+ headers.set(ContentType(
+ Mime(TopLevel::Application, SubLevel::Json, vec![]),
+ ));
+
+ let response = request(Request {
+ body: Some(&body),
+ headers: Some(headers),
+ route: RouteInfo::ExecuteWebhook { token, wait, webhook_id },
+ })?;
if response.status == StatusCode::NoContent {
return Ok(None);
@@ -1097,10 +978,10 @@ pub fn execute_webhook(webhook_id: u64,
///
/// Does not require authentication.
pub fn get_active_maintenances() -> Result<Vec<Maintenance>> {
- let client = request_client!();
-
- let response = retry(|| {
- client.get(status!("/scheduled-maintenances/active.json"))
+ let response = request(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetActiveMaintenance,
})?;
let mut map: BTreeMap<String, Value> = serde_json::from_reader(response)?;
@@ -1114,15 +995,11 @@ pub fn get_active_maintenances() -> Result<Vec<Maintenance>> {
/// Gets all the users that are banned in specific guild.
pub fn get_bans(guild_id: u64) -> Result<Vec<Ban>> {
- let response = request!(
- Route::GuildsIdBans(guild_id),
- get,
- "/guilds/{}/bans",
- guild_id
- );
-
- serde_json::from_reader::<HyperResponse, Vec<Ban>>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetBans { guild_id },
+ })
}
/// Gets all audit logs in a specific guild.
@@ -1131,57 +1008,35 @@ pub fn get_audit_logs(guild_id: u64,
user_id: Option<u64>,
before: Option<u64>,
limit: Option<u8>) -> Result<AuditLogs> {
- let mut params = Vec::with_capacity(4);
-
- if let Some(action_type) = action_type {
- params.push(format!("action_type={}", action_type));
- }
- if let Some(user_id) = user_id {
- params.push(format!("user_id={}", user_id));
- }
- if let Some(before) = before {
- params.push(format!("before={}", before));
- }
- if let Some(limit) = limit {
- params.push(format!("limit={}", limit));
- }
-
- let mut query_string = params.join("&");
- if !query_string.is_empty() {
- query_string.insert(0, '?');
- }
-
- let response = request!(
- Route::GuildsIdAuditLogs(guild_id),
- get,
- "/guilds/{}/audit-logs{}",
- guild_id,
- query_string
- );
-
- serde_json::from_reader::<HyperResponse, AuditLogs>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetAuditLogs {
+ action_type,
+ before,
+ guild_id,
+ limit,
+ user_id,
+ },
+ })
}
/// Gets current bot gateway.
pub fn get_bot_gateway() -> Result<BotGateway> {
- let response = request!(Route::GatewayBot, get, "/gateway/bot");
-
- serde_json::from_reader::<HyperResponse, BotGateway>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetBotGateway,
+ })
}
/// Gets all invites for a channel.
pub fn get_channel_invites(channel_id: u64) -> Result<Vec<RichInvite>> {
- let response = request!(
- Route::ChannelsIdInvites(channel_id),
- get,
- "/channels/{}/invites",
- channel_id
- );
-
- serde_json::from_reader::<HyperResponse, Vec<RichInvite>>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetChannelInvites { channel_id },
+ })
}
/// Retrieves the webhooks for the given [channel][`GuildChannel`]'s Id.
@@ -1203,114 +1058,94 @@ pub fn get_channel_invites(channel_id: u64) -> Result<Vec<RichInvite>> {
///
/// [`GuildChannel`]: ../model/channel/struct.GuildChannel.html
pub fn get_channel_webhooks(channel_id: u64) -> Result<Vec<Webhook>> {
- let response = request!(
- Route::ChannelsIdWebhooks(channel_id),
- get,
- "/channels/{}/webhooks",
- channel_id
- );
-
- serde_json::from_reader::<HyperResponse, Vec<Webhook>>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetChannelWebhooks { channel_id },
+ })
}
/// Gets channel information.
pub fn get_channel(channel_id: u64) -> Result<Channel> {
- let response = request!(
- Route::ChannelsId(channel_id),
- get,
- "/channels/{}",
- channel_id
- );
-
- serde_json::from_reader::<HyperResponse, Channel>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetChannel { channel_id },
+ })
}
/// Gets all channels in a guild.
pub fn get_channels(guild_id: u64) -> Result<Vec<GuildChannel>> {
- let response = request!(
- Route::ChannelsId(guild_id),
- get,
- "/guilds/{}/channels",
- guild_id
- );
-
- serde_json::from_reader::<HyperResponse, Vec<GuildChannel>>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetChannels { guild_id },
+ })
}
/// Gets information about the current application.
///
/// **Note**: Only applications may use this endpoint.
pub fn get_current_application_info() -> Result<CurrentApplicationInfo> {
- let response = request!(Route::None, get, "/oauth2/applications/@me");
-
- serde_json::from_reader::<HyperResponse, CurrentApplicationInfo>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetCurrentApplicationInfo,
+ })
}
/// Gets information about the user we're connected with.
pub fn get_current_user() -> Result<CurrentUser> {
- let response = request!(Route::UsersMe, get, "/users/@me");
-
- serde_json::from_reader::<HyperResponse, CurrentUser>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetCurrentUser,
+ })
}
/// Gets current gateway.
pub fn get_gateway() -> Result<Gateway> {
- let response = request!(Route::Gateway, get, "/gateway");
-
- serde_json::from_reader::<HyperResponse, Gateway>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetGateway,
+ })
}
/// Gets guild information.
pub fn get_guild(guild_id: u64) -> Result<PartialGuild> {
- let response = request!(Route::GuildsId(guild_id), get, "/guilds/{}", guild_id);
-
- serde_json::from_reader::<HyperResponse, PartialGuild>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetGuild { guild_id },
+ })
}
/// Gets a guild embed information.
pub fn get_guild_embed(guild_id: u64) -> Result<GuildEmbed> {
- let response = request!(
- Route::GuildsIdEmbed(guild_id),
- get,
- "/guilds/{}/embeds",
- guild_id
- );
-
- serde_json::from_reader::<HyperResponse, GuildEmbed>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetGuildEmbed { guild_id },
+ })
}
/// Gets integrations that a guild has.
pub fn get_guild_integrations(guild_id: u64) -> Result<Vec<Integration>> {
- let response = request!(
- Route::GuildsIdIntegrations(guild_id),
- get,
- "/guilds/{}/integrations",
- guild_id
- );
-
- serde_json::from_reader::<HyperResponse, Vec<Integration>>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetGuildIntegrations { guild_id },
+ })
}
/// Gets all invites to a guild.
pub fn get_guild_invites(guild_id: u64) -> Result<Vec<RichInvite>> {
- let response = request!(
- Route::GuildsIdInvites(guild_id),
- get,
- "/guilds/{}/invites",
- guild_id
- );
-
- serde_json::from_reader::<HyperResponse, Vec<RichInvite>>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetGuildInvites { guild_id },
+ })
}
/// Gets a guild's vanity URL if it has one.
@@ -1320,12 +1155,11 @@ pub fn get_guild_vanity_url(guild_id: u64) -> Result<String> {
code: String,
}
- let response = request!(
- Route::GuildsIdVanityUrl(guild_id),
- get,
- "/guilds/{}/vanity-url",
- guild_id
- );
+ let response = request(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetGuildVanityUrl { guild_id },
+ })?;
serde_json::from_reader::<HyperResponse, GuildVanityUrl>(response)
.map(|x| x.code)
@@ -1338,14 +1172,11 @@ pub fn get_guild_members(guild_id: u64,
limit: Option<u64>,
after: Option<u64>)
-> Result<Vec<Member>> {
- let response = request!(
- Route::GuildsIdMembers(guild_id),
- get,
- "/guilds/{}/members?limit={}&after={}",
- guild_id,
- limit.unwrap_or(500),
- after.unwrap_or(0)
- );
+ let response = request(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetGuildMembers { after, guild_id, limit },
+ })?;
let mut v = serde_json::from_reader::<HyperResponse, Value>(response)?;
@@ -1364,45 +1195,43 @@ pub fn get_guild_members(guild_id: u64,
/// Gets the amount of users that can be pruned.
pub fn get_guild_prune_count(guild_id: u64, map: &Value) -> Result<GuildPrune> {
- let body = map.to_string();
- let response = request!(
- Route::GuildsIdPrune(guild_id),
- get(body),
- "/guilds/{}/prune",
- guild_id
- );
-
- serde_json::from_reader::<HyperResponse, GuildPrune>(response)
- .map_err(From::from)
+ // Note for 0.6.x: turn this into a function parameter.
+ #[derive(Deserialize)]
+ struct GetGuildPruneCountRequest {
+ days: u64,
+ }
+
+ let req = serde_json::from_value::<GetGuildPruneCountRequest>(map.clone())?;
+
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetGuildPruneCount {
+ days: req.days,
+ guild_id,
+ },
+ })
}
/// Gets regions that a guild can use. If a guild has the `VIP_REGIONS` feature
/// enabled, then additional VIP-only regions are returned.
pub fn get_guild_regions(guild_id: u64) -> Result<Vec<VoiceRegion>> {
- let response = request!(
- Route::GuildsIdRegions(guild_id),
- get,
- "/guilds/{}/regions",
- guild_id
- );
-
- serde_json::from_reader::<HyperResponse, Vec<VoiceRegion>>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetGuildRegions { guild_id },
+ })
}
/// Retrieves a list of roles in a [`Guild`].
///
/// [`Guild`]: ../model/guild/struct.Guild.html
pub fn get_guild_roles(guild_id: u64) -> Result<Vec<Role>> {
- let response = request!(
- Route::GuildsIdRoles(guild_id),
- get,
- "/guilds/{}/roles",
- guild_id
- );
-
- serde_json::from_reader::<HyperResponse, Vec<Role>>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetGuildRoles { guild_id },
+ })
}
/// Retrieves the webhooks for the given [guild][`Guild`]'s Id.
@@ -1424,15 +1253,11 @@ pub fn get_guild_roles(guild_id: u64) -> Result<Vec<Role>> {
///
/// [`Guild`]: ../model/guild/struct.Guild.html
pub fn get_guild_webhooks(guild_id: u64) -> Result<Vec<Webhook>> {
- let response = request!(
- Route::GuildsIdWebhooks(guild_id),
- get,
- "/guilds/{}/webhooks",
- guild_id
- );
-
- serde_json::from_reader::<HyperResponse, Vec<Webhook>>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetGuildWebhooks { guild_id },
+ })
}
/// Gets a paginated list of the current user's guilds.
@@ -1456,54 +1281,40 @@ pub fn get_guild_webhooks(guild_id: u64) -> Result<Vec<Webhook>> {
///
/// [docs]: https://discordapp.com/developers/docs/resources/user#get-current-user-guilds
pub fn get_guilds(target: &GuildPagination, limit: u64) -> Result<Vec<GuildInfo>> {
- let mut uri = format!("/users/@me/guilds?limit={}", limit);
-
- match *target {
- GuildPagination::After(id) => {
- write!(uri, "&after={}", id)?;
- },
- GuildPagination::Before(id) => {
- write!(uri, "&before={}", id)?;
- },
- }
-
- let response = request!(Route::UsersMeGuilds, get, "{}", uri);
+ let (after, before) = match *target {
+ GuildPagination::After(id) => (Some(id.0), None),
+ GuildPagination::Before(id) => (None, Some(id.0)),
+ };
- serde_json::from_reader::<HyperResponse, Vec<GuildInfo>>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetGuilds { after, before, limit },
+ })
}
/// Gets information about a specific invite.
#[allow(unused_mut)]
-pub fn get_invite(code: &str, stats: bool) -> Result<Invite> {
- let mut invite = code;
-
+pub fn get_invite(mut code: &str, stats: bool) -> Result<Invite> {
#[cfg(feature = "utils")]
{
- invite = ::utils::parse_invite(invite);
- }
-
- let mut uri = format!("/invites/{}", invite);
-
- if stats {
- uri.push_str("?with_counts=true");
+ code = ::utils::parse_invite(code);
}
- let response = request!(Route::InvitesCode, get, "{}", uri);
-
- serde_json::from_reader::<HyperResponse, Invite>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetInvite { code, stats },
+ })
}
/// Gets member of a guild.
pub fn get_member(guild_id: u64, user_id: u64) -> Result<Member> {
- let response = request!(
- Route::GuildsIdMembersId(guild_id),
- get,
- "/guilds/{}/members/{}",
- guild_id,
- user_id
- );
+ let response = request(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetMember { guild_id, user_id },
+ })?;
let mut v = serde_json::from_reader::<HyperResponse, Value>(response)?;
@@ -1516,40 +1327,32 @@ pub fn get_member(guild_id: u64, user_id: u64) -> Result<Member> {
/// Gets a message by an Id, bots only.
pub fn get_message(channel_id: u64, message_id: u64) -> Result<Message> {
- let response = request!(
- Route::ChannelsIdMessagesId(LightMethod::Any, channel_id),
- get,
- "/channels/{}/messages/{}",
- channel_id,
- message_id
- );
-
- serde_json::from_reader::<HyperResponse, Message>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetMessage { channel_id, message_id },
+ })
}
/// Gets X messages from a channel.
pub fn get_messages(channel_id: u64, query: &str) -> Result<Vec<Message>> {
- let url = format!(api!("/channels/{}/messages{}"), channel_id, query);
- let client = request_client!();
-
- let response = request(Route::ChannelsIdMessages(channel_id), || client.get(&url))?;
-
- serde_json::from_reader::<HyperResponse, Vec<Message>>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetMessages {
+ query: query.to_owned(),
+ channel_id,
+ },
+ })
}
/// Gets all pins of a channel.
pub fn get_pins(channel_id: u64) -> Result<Vec<Message>> {
- let response = request!(
- Route::ChannelsIdPins(channel_id),
- get,
- "/channels/{}/pins",
- channel_id
- );
-
- serde_json::from_reader::<HyperResponse, Vec<Message>>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetPins { channel_id },
+ })
}
/// Gets user Ids based on their reaction to a message. This endpoint is dumb.
@@ -1559,36 +1362,30 @@ pub fn get_reaction_users(channel_id: u64,
limit: u8,
after: Option<u64>)
-> Result<Vec<User>> {
- let mut uri = format!(
- "/channels/{}/messages/{}/reactions/{}?limit={}",
- channel_id,
- message_id,
- reaction_type.as_data(),
- limit
- );
-
- if let Some(user_id) = after {
- write!(uri, "&after={}", user_id)?;
- }
-
- let response = request!(
- Route::ChannelsIdMessagesIdReactionsUserIdType(channel_id),
- get,
- "{}",
- uri
- );
+ let reaction = reaction_type.as_data();
- serde_json::from_reader::<HyperResponse, Vec<User>>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetReactionUsers {
+ after,
+ channel_id,
+ limit,
+ message_id,
+ reaction,
+ },
+ })
}
/// Gets the current unresolved incidents from Discord's Status API.
///
/// Does not require authentication.
pub fn get_unresolved_incidents() -> Result<Vec<Incident>> {
- let client = request_client!();
-
- let response = retry(|| client.get(status!("/incidents/unresolved.json")))?;
+ let response = request(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetUnresolvedIncidents,
+ })?;
let mut map: BTreeMap<String, Value> = serde_json::from_reader(response)?;
@@ -1603,10 +1400,10 @@ pub fn get_unresolved_incidents() -> Result<Vec<Incident>> {
///
/// Does not require authentication.
pub fn get_upcoming_maintenances() -> Result<Vec<Maintenance>> {
- let client = request_client!();
-
- let response = retry(|| {
- client.get(status!("/scheduled-maintenances/upcoming.json"))
+ let response = request(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetUpcomingMaintenances,
})?;
let mut map: BTreeMap<String, Value> = serde_json::from_reader(response)?;
@@ -1620,26 +1417,29 @@ pub fn get_upcoming_maintenances() -> Result<Vec<Maintenance>> {
/// Gets a user by Id.
pub fn get_user(user_id: u64) -> Result<User> {
- let response = request!(Route::UsersId, get, "/users/{}", user_id);
-
- serde_json::from_reader::<HyperResponse, User>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetUser { user_id },
+ })
}
/// Gets our DM channels.
pub fn get_user_dm_channels() -> Result<Vec<PrivateChannel>> {
- let response = request!(Route::UsersMeChannels, get, "/users/@me/channels");
-
- serde_json::from_reader::<HyperResponse, Vec<PrivateChannel>>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetUserDmChannels,
+ })
}
/// Gets all voice regions.
pub fn get_voice_regions() -> Result<Vec<VoiceRegion>> {
- let response = request!(Route::VoiceRegions, get, "/voice/regions");
-
- serde_json::from_reader::<HyperResponse, Vec<VoiceRegion>>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetVoiceRegions,
+ })
}
/// Retrieves a webhook given its Id.
@@ -1660,15 +1460,11 @@ pub fn get_voice_regions() -> Result<Vec<VoiceRegion>> {
///
/// [`get_webhook_with_token`]: fn.get_webhook_with_token.html
pub fn get_webhook(webhook_id: u64) -> Result<Webhook> {
- let response = request!(
- Route::WebhooksId(webhook_id),
- get,
- "/webhooks/{}",
- webhook_id,
- );
-
- serde_json::from_reader::<HyperResponse, Webhook>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetWebhook { webhook_id },
+ })
}
/// Retrieves a webhook given its Id and unique token.
@@ -1689,64 +1485,47 @@ pub fn get_webhook(webhook_id: u64) -> Result<Webhook> {
/// .expect("Error getting webhook");
/// ```
pub fn get_webhook_with_token(webhook_id: u64, token: &str) -> Result<Webhook> {
- let client = request_client!();
-
- let response = retry(|| {
- client
- .get(&format!(api!("/webhooks/{}/{}"), webhook_id, token))
- }).map_err(Error::Hyper)?;
-
- serde_json::from_reader::<HyperResponse, Webhook>(response)
- .map_err(From::from)
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::GetWebhookWithToken { token, webhook_id },
+ })
}
/// Kicks a member from a guild.
pub fn kick_member(guild_id: u64, user_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::GuildsIdMembersId(guild_id),
- delete,
- "/guilds/{}/members/{}",
- guild_id,
- user_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::KickMember { guild_id, user_id },
+ })
}
/// Leaves a group DM.
-pub fn leave_group(guild_id: u64) -> Result<Group> {
- let response = request!(Route::None, delete, "/channels/{}", guild_id);
-
- serde_json::from_reader::<HyperResponse, Group>(response)
- .map_err(From::from)
+pub fn leave_group(group_id: u64) -> Result<Group> {
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::LeaveGroup { group_id },
+ })
}
/// Leaves a guild.
pub fn leave_guild(guild_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::UsersMeGuildsId,
- delete,
- "/users/@me/guilds/{}",
- guild_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::LeaveGuild { guild_id },
+ })
}
/// Deletes a user from group DM.
pub fn remove_group_recipient(group_id: u64, user_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::None,
- delete,
- "/channels/{}/recipients/{}",
- group_id,
- user_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::RemoveGroupRecipient { group_id, user_id },
+ })
}
/// Sends file(s) to a channel.
@@ -1760,7 +1539,7 @@ pub fn remove_group_recipient(group_id: u64, user_id: u64) -> Result<()> {
/// [`HttpError::InvalidRequest`]: enum.HttpError.html#variant.InvalidRequest
pub fn send_files<'a, T, It: IntoIterator<Item=T>>(channel_id: u64, files: It, map: JsonMap) -> Result<Message>
where T: Into<AttachmentType<'a>> {
- let uri = format!(api!("/channels/{}/messages"), channel_id);
+ let uri = api!("/channels/{}/messages", channel_id);
let url = match Url::parse(&uri) {
Ok(url) => url,
Err(_) => return Err(Error::Url(uri)),
@@ -1768,7 +1547,7 @@ pub fn send_files<'a, T, It: IntoIterator<Item=T>>(channel_id: u64, files: It, m
let tc = NativeTlsClient::new()?;
let connector = HttpsConnector::new(tc);
- let mut request = Request::with_connector(Method::Post, url, &connector)?;
+ let mut request = HyperRequest::with_connector(Method::Post, url, &connector)?;
request
.headers_mut()
.set(header::Authorization(TOKEN.lock().clone()));
@@ -1817,50 +1596,36 @@ pub fn send_files<'a, T, It: IntoIterator<Item=T>>(channel_id: u64, files: It, m
return Err(Error::Http(HttpError::UnsuccessfulRequest(response)));
}
- serde_json::from_reader::<HyperResponse, Message>(response)
- .map_err(From::from)
+ serde_json::from_reader(response).map_err(From::from)
}
/// Sends a message to a channel.
pub fn send_message(channel_id: u64, map: &Value) -> Result<Message> {
- let body = map.to_string();
- let response = request!(
- Route::ChannelsIdMessages(channel_id),
- post(body),
- "/channels/{}/messages",
- channel_id
- );
+ let body = serde_json::to_vec(map)?;
- serde_json::from_reader::<HyperResponse, Message>(response)
- .map_err(From::from)
+ fire(Request {
+ body: Some(&body),
+ headers: None,
+ route: RouteInfo::CreateMessage { channel_id },
+ })
}
/// Pins a message in a channel.
pub fn pin_message(channel_id: u64, message_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::ChannelsIdPinsMessageId(channel_id),
- put,
- "/channels/{}/pins/{}",
- channel_id,
- message_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::PinMessage { channel_id, message_id },
+ })
}
/// Unbans a user from a guild.
pub fn remove_ban(guild_id: u64, user_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::GuildsIdBansUserId(guild_id),
- delete,
- "/guilds/{}/bans/{}",
- guild_id,
- user_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::RemoveBan { guild_id, user_id },
+ })
}
/// Deletes a single [`Role`] from a [`Member`] in a [`Guild`].
@@ -1871,69 +1636,154 @@ pub fn remove_ban(guild_id: u64, user_id: u64) -> Result<()> {
/// [`Guild`]: ../model/guild/struct.Guild.html
/// [`Member`]: ../model/guild/struct.Member.html
/// [`Role`]: ../model/guild/struct.Role.html
-/// [Manage Roles]: ../model/permissions/constant.MANAGE_ROLES.html
+/// [Manage Roles]: ../model/permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
pub fn remove_member_role(guild_id: u64, user_id: u64, role_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::GuildsIdMembersIdRolesId(guild_id),
- delete,
- "/guilds/{}/members/{}/roles/{}",
- guild_id,
- user_id,
- role_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::RemoveMemberRole { guild_id, user_id, role_id },
+ })
}
/// Starts removing some members from a guild based on the last time they've been online.
pub fn start_guild_prune(guild_id: u64, map: &Value) -> Result<GuildPrune> {
- let body = map.to_string();
- let response = request!(
- Route::GuildsIdPrune(guild_id),
- post(body),
- "/guilds/{}/prune",
- guild_id
- );
-
- serde_json::from_reader::<HyperResponse, GuildPrune>(response)
- .map_err(From::from)
+ // Note for 0.6.x: turn this into a function parameter.
+ #[derive(Deserialize)]
+ struct StartGuildPruneRequest {
+ days: u64,
+ }
+
+ let req = serde_json::from_value::<StartGuildPruneRequest>(map.clone())?;
+
+ fire(Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::StartGuildPrune {
+ days: req.days,
+ guild_id,
+ },
+ })
}
/// Starts syncing an integration with a guild.
pub fn start_integration_sync(guild_id: u64, integration_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::GuildsIdIntegrationsIdSync(guild_id),
- post,
- "/guilds/{}/integrations/{}/sync",
- guild_id,
- integration_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::StartIntegrationSync { guild_id, integration_id },
+ })
}
/// Unpins a message from a channel.
pub fn unpin_message(channel_id: u64, message_id: u64) -> Result<()> {
- verify(
- 204,
- request!(
- Route::ChannelsIdPinsMessageId(channel_id),
- delete,
- "/channels/{}/pins/{}",
- channel_id,
- message_id
- ),
- )
+ wind(204, Request {
+ body: None,
+ headers: None,
+ route: RouteInfo::UnpinMessage { channel_id, message_id },
+ })
}
-fn request<'a, F>(route: Route, f: F) -> Result<HyperResponse>
- where F: Fn() -> RequestBuilder<'a> {
- let response = ratelimiting::perform(route, || {
- f().header(header::Authorization(TOKEN.lock().clone()))
- .header(header::ContentType::json())
- })?;
+/// Fires off a request, deserializing the response reader via the given type
+/// bound.
+///
+/// If you don't need to deserialize the response and want the response instance
+/// itself, use [`request`].
+///
+/// # Examples
+///
+/// Create a new message via the [`RouteInfo::CreateMessage`] endpoint and
+/// deserialize the response into a [`Message`]:
+///
+/// ```rust,no_run
+/// # extern crate serenity;
+/// #
+/// # use std::error::Error;
+/// #
+/// # fn try_main() -> Result<(), Box<Error>> {
+/// #
+/// use serenity::{
+/// http::{
+/// self,
+/// request::RequestBuilder,
+/// routing::RouteInfo,
+/// },
+/// model::channel::Message,
+/// };
+///
+/// let bytes = vec![
+/// // payload bytes here
+/// ];
+/// let channel_id = 381880193700069377;
+/// let route_info = RouteInfo::CreateMessage { channel_id };
+///
+/// let mut request = RequestBuilder::new(route_info);
+/// request.body(Some(&bytes));
+///
+/// let message = http::fire::<Message>(request.build())?;
+///
+/// println!("Message content: {}", message.content);
+/// #
+/// # Ok(())
+/// # }
+/// #
+/// # fn main() {
+/// # try_main().unwrap();
+/// # }
+/// ```
+///
+/// [`request`]: fn.request.html
+pub fn fire<T: DeserializeOwned>(req: Request) -> Result<T> {
+ let response = request(req)?;
+
+ serde_json::from_reader(response).map_err(From::from)
+}
+
+/// Performs a request, ratelimiting it if necessary.
+///
+/// Returns the raw hyper Response. Use [`fire`] to deserialize the response
+/// into some type.
+///
+/// # Examples
+///
+/// Send a body of bytes over the [`RouteInfo::CreateMessage`] endpoint:
+///
+/// ```rust,no_run
+/// # extern crate serenity;
+/// #
+/// # use std::error::Error;
+/// #
+/// # fn try_main() -> Result<(), Box<Error>> {
+/// #
+/// use serenity::http::{
+/// self,
+/// request::RequestBuilder,
+/// routing::RouteInfo,
+/// };
+///
+/// let bytes = vec![
+/// // payload bytes here
+/// ];
+/// let channel_id = 381880193700069377;
+/// let route_info = RouteInfo::CreateMessage { channel_id };
+///
+/// let mut request = RequestBuilder::new(route_info);
+/// request.body(Some(&bytes));
+///
+/// let response = http::request(request.build())?;
+///
+/// println!("Response successful?: {}", response.status.is_success());
+/// #
+/// # Ok(())
+/// # }
+/// #
+/// # fn main() {
+/// # try_main().unwrap();
+/// # }
+/// ```
+///
+/// [`fire`]: fn.fire.html
+pub fn request(req: Request) -> Result<HyperResponse> {
+ let response = ratelimiting::perform(req)?;
if response.status.class() == StatusClass::Success {
Ok(response)
@@ -1942,28 +1792,37 @@ fn request<'a, F>(route: Route, f: F) -> Result<HyperResponse>
}
}
-pub(crate) fn retry<'a, F>(f: F) -> HyperResult<HyperResponse>
- where F: Fn() -> RequestBuilder<'a> {
- let req = || {
- f().header(header::UserAgent(constants::USER_AGENT.to_string()))
- .send()
- };
-
- match req() {
- Err(HyperError::Io(ref io)) if io.kind() == IoErrorKind::ConnectionAborted => req(),
- other => other,
+fn retry(request: &Request) -> HyperResult<HyperResponse> {
+ // Retry the request twice in a loop until it succeeds.
+ //
+ // If it doesn't and the loop breaks, try one last time.
+ for _ in 0..3 {
+ match request.build().send() {
+ Err(HyperError::Io(ref io))
+ if io.kind() == IoErrorKind::ConnectionAborted => continue,
+ other => return other,
+ }
}
+
+ request.build().send()
}
-fn verify(expected: u16, response: HyperResponse) -> Result<()> {
- if response.status.to_u16() == expected {
+/// Performs a request and then verifies that the response status code is equal
+/// to the expected value.
+///
+/// This is a function that performs a light amount of work and returns an
+/// empty tuple, so it's called "wind" to denote that it's lightweight.
+fn wind(expected: u16, req: Request) -> Result<()> {
+ let resp = request(req)?;
+
+ if resp.status.to_u16() == expected {
return Ok(());
}
- debug!("Expected {}, got {}", expected, response.status);
- trace!("Unsuccessful response: {:?}", response);
+ debug!("Expected {}, got {}", expected, resp.status);
+ trace!("Unsuccessful response: {:?}", resp);
- Err(Error::Http(HttpError::UnsuccessfulRequest(response)))
+ Err(Error::Http(HttpError::UnsuccessfulRequest(resp)))
}
/// Enum that allows a user to pass a `Path` or a `File` type to `send_files`
@@ -2008,3 +1867,21 @@ pub enum GuildPagination {
/// The Id to get the guilds before.
Before(GuildId),
}
+
+#[cfg(test)]
+mod test {
+ use super::AttachmentType;
+ use std::path::Path;
+
+ #[test]
+ fn test_attachment_type() {
+ assert!(match AttachmentType::from(Path::new("./dogs/corgis/kona.png")) {
+ AttachmentType::Path(_) => true,
+ _ => false,
+ });
+ assert!(match AttachmentType::from("./cats/copycat.png") {
+ AttachmentType::Path(_) => true,
+ _ => false,
+ });
+ }
+}
diff --git a/src/http/ratelimiting.rs b/src/http/ratelimiting.rs
index b8152d1..298ca6f 100644
--- a/src/http/ratelimiting.rs
+++ b/src/http/ratelimiting.rs
@@ -8,7 +8,7 @@
//! > For example, `/channels/:channel_id` and
//! > `/channels/:channel_id/messages/:message_id` both take `channel_id` into
//! > account when generating rate limits since it's the major parameter. The
-//! only current major parameters are `channel_id` and `guild_id`.
+//! only current major parameters are `channel_id`, `guild_id` and `webhook_id`.
//!
//! This results in the two URIs of `GET /channels/4/messages/7` and
//! `GET /channels/5/messages/8` being rate limited _separately_. However, the
@@ -40,8 +40,10 @@
//! [Taken from]: https://discordapp.com/developers/docs/topics/rate-limits#rate-limits
#![allow(zero_ptr)]
+pub use super::routing::Route;
+
use chrono::{DateTime, Utc};
-use hyper::client::{RequestBuilder, Response};
+use hyper::client::Response;
use hyper::header::Headers;
use hyper::status::StatusCode;
use internal::prelude::*;
@@ -54,7 +56,7 @@ use std::{
thread,
i64
};
-use super::{HttpError, LightMethod};
+use super::{HttpError, Request};
/// Refer to [`offset`].
///
@@ -96,278 +98,28 @@ lazy_static! {
/// ```
///
/// [`RateLimit`]: struct.RateLimit.html
- /// [`Route`]: enum.Route.html
+ /// [`Route`]: ../routing/enum.Route.html
pub static ref ROUTES: Arc<Mutex<HashMap<Route, Arc<Mutex<RateLimit>>>>> = {
Arc::new(Mutex::new(HashMap::default()))
};
}
-/// A representation of all routes registered within the library. These are safe
-/// and memory-efficient representations of each path that functions exist for
-/// in the [`http`] module.
-///
-/// [`http`]: ../index.html
-#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
-pub enum Route {
- /// Route for the `/channels/:channel_id` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsId(u64),
- /// Route for the `/channels/:channel_id/invites` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdInvites(u64),
- /// Route for the `/channels/:channel_id/messages` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdMessages(u64),
- /// Route for the `/channels/:channel_id/messages/bulk-delete` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdMessagesBulkDelete(u64),
- /// Route for the `/channels/:channel_id/messages/:message_id` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- // This route is a unique case. The ratelimit for message _deletions_ is
- // different than the overall route ratelimit.
- //
- // Refer to the docs on [Rate Limits] in the yellow warning section.
- //
- // Additionally, this needs to be a `LightMethod` from the parent module
- // and _not_ a `hyper` `Method` due to `hyper`'s not deriving `Copy`.
- //
- // [Rate Limits]: https://discordapp.com/developers/docs/topics/rate-limits
- ChannelsIdMessagesId(LightMethod, u64),
- /// Route for the `/channels/:channel_id/messages/:message_id/ack` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdMessagesIdAck(u64),
- /// Route for the `/channels/:channel_id/messages/:message_id/reactions`
- /// path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdMessagesIdReactions(u64),
- /// Route for the
- /// `/channels/:channel_id/messages/:message_id/reactions/:reaction/@me`
- /// path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdMessagesIdReactionsUserIdType(u64),
- /// Route for the `/channels/:channel_id/permissions/:target_id` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdPermissionsOverwriteId(u64),
- /// Route for the `/channels/:channel_id/pins` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdPins(u64),
- /// Route for the `/channels/:channel_id/pins/:message_id` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdPinsMessageId(u64),
- /// Route for the `/channels/:channel_id/typing` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdTyping(u64),
- /// Route for the `/channels/:channel_id/webhooks` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdWebhooks(u64),
- /// Route for the `/gateway` path.
- Gateway,
- /// Route for the `/gateway/bot` path.
- GatewayBot,
- /// Route for the `/guilds` path.
- Guilds,
- /// Route for the `/guilds/:guild_id` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsId(u64),
- /// Route for the `/guilds/:guild_id/bans` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdBans(u64),
- /// Route for the `/guilds/:guild_id/audit-logs` path.
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdAuditLogs(u64),
- /// Route for the `/guilds/:guild_id/bans/:user_id` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdBansUserId(u64),
- /// Route for the `/guilds/:guild_id/channels/:channel_id` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdChannels(u64),
- /// Route for the `/guilds/:guild_id/embed` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdEmbed(u64),
- /// Route for the `/guilds/:guild_id/emojis` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdEmojis(u64),
- /// Route for the `/guilds/:guild_id/emojis/:emoji_id` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdEmojisId(u64),
- /// Route for the `/guilds/:guild_id/integrations` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdIntegrations(u64),
- /// Route for the `/guilds/:guild_id/integrations/:integration_id` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdIntegrationsId(u64),
- /// Route for the `/guilds/:guild_id/integrations/:integration_id/sync`
- /// path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdIntegrationsIdSync(u64),
- /// Route for the `/guilds/:guild_id/invites` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdInvites(u64),
- /// Route for the `/guilds/:guild_id/members` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdMembers(u64),
- /// Route for the `/guilds/:guild_id/members/:user_id` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdMembersId(u64),
- /// Route for the `/guilds/:guild_id/members/:user_id/roles/:role_id` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdMembersIdRolesId(u64),
- /// Route for the `/guilds/:guild_id/members/@me/nick` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdMembersMeNick(u64),
- /// Route for the `/guilds/:guild_id/prune` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdPrune(u64),
- /// Route for the `/guilds/:guild_id/regions` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdRegions(u64),
- /// Route for the `/guilds/:guild_id/roles` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdRoles(u64),
- /// Route for the `/guilds/:guild_id/roles/:role_id` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdRolesId(u64),
- /// Route for the `/guilds/:guild_id/vanity-url` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdVanityUrl(u64),
- /// Route for the `/guilds/:guild_id/webhooks` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdWebhooks(u64),
- /// Route for the `/invites/:code` path.
- InvitesCode,
- /// Route for the `/users/:user_id` path.
- UsersId,
- /// Route for the `/users/@me` path.
- UsersMe,
- /// Route for the `/users/@me/channels` path.
- UsersMeChannels,
- /// Route for the `/users/@me/guilds` path.
- UsersMeGuilds,
- /// Route for the `/users/@me/guilds/:guild_id` path.
- UsersMeGuildsId,
- /// Route for the `/voice/regions` path.
- VoiceRegions,
- /// Route for the `/webhooks/:webhook_id` path.
- WebhooksId(u64),
- /// Route where no ratelimit headers are in place (i.e. user account-only
- /// routes).
- ///
- /// This is a special case, in that if the route is `None` then pre- and
- /// post-hooks are not executed.
- None,
-}
-
-pub(crate) fn perform<'a, F>(route: Route, f: F) -> Result<Response>
- where F: Fn() -> RequestBuilder<'a> {
+pub(super) fn perform(req: Request) -> Result<Response> {
loop {
// This will block if another thread already has the global
// unlocked already (due to receiving an x-ratelimit-global).
let _ = GLOBAL.lock();
+ // Destructure the tuple instead of retrieving the third value to
+ // take advantage of the type system. If `RouteInfo::deconstruct`
+ // returns a different number of tuple elements in the future, directly
+ // accessing a certain index (e.g. `req.route.deconstruct().1`) would
+ // mean this code would not indicate it might need to be updated for the
+ // new tuple element amount.
+ //
+ // This isn't normally important, but might be for ratelimiting.
+ let (_, route, _) = req.route.deconstruct();
+
// Perform pre-checking here:
//
// - get the route's relevant rate
@@ -390,7 +142,7 @@ pub(crate) fn perform<'a, F>(route: Route, f: F) -> Result<Response>
let mut lock = bucket.lock();
lock.pre_hook(&route);
- let response = super::retry(&f)?;
+ let response = super::retry(&req)?;
// Check if an offset has been calculated yet to determine the time
// difference from Discord can the client.
@@ -452,7 +204,7 @@ pub(crate) fn perform<'a, F>(route: Route, f: F) -> Result<Response>
/// 429s.
///
/// [`ROUTES`]: struct.ROUTES.html
-/// [`Route`]: enum.Route.html
+/// [`Route`]: ../routing/enum.Route.html
/// [Discord docs]: https://discordapp.com/developers/docs/topics/rate-limits
#[derive(Clone, Debug, Default)]
pub struct RateLimit {
diff --git a/src/http/request.rs b/src/http/request.rs
new file mode 100644
index 0000000..92dd073
--- /dev/null
+++ b/src/http/request.rs
@@ -0,0 +1,116 @@
+use constants;
+use hyper::{
+ client::{Body, RequestBuilder as HyperRequestBuilder},
+ header::{Authorization, ContentType, Headers, UserAgent},
+};
+use super::{
+ CLIENT,
+ TOKEN,
+ routing::RouteInfo,
+};
+
+pub struct RequestBuilder<'a> {
+ body: Option<&'a [u8]>,
+ headers: Option<Headers>,
+ route: RouteInfo<'a>,
+}
+
+impl<'a> RequestBuilder<'a> {
+ pub fn new(route_info: RouteInfo<'a>) -> Self {
+ Self {
+ body: None,
+ headers: None,
+ route: route_info,
+ }
+ }
+
+ pub fn build(self) -> Request<'a> {
+ Request::new(self)
+ }
+
+ pub fn body(&mut self, body: Option<&'a [u8]>) -> &mut Self {
+ self.body = body;
+
+ self
+ }
+
+ pub fn headers(&mut self, headers: Option<Headers>) -> &mut Self {
+ self.headers = headers;
+
+ self
+ }
+
+ pub fn route(&mut self, route_info: RouteInfo<'a>) -> &mut Self {
+ self.route = route_info;
+
+ self
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct Request<'a> {
+ pub(super) body: Option<&'a [u8]>,
+ pub(super) headers: Option<Headers>,
+ pub(super) route: RouteInfo<'a>,
+}
+
+impl<'a> Request<'a> {
+ pub fn new(builder: RequestBuilder<'a>) -> Self {
+ let RequestBuilder { body, headers, route } = builder;
+
+ Self { body, headers, route }
+ }
+
+ pub fn build(&'a self) -> HyperRequestBuilder<'a> {
+ let Request {
+ body,
+ headers: ref request_headers,
+ route: ref route_info,
+ } = *self;
+ let (method, _, path) = route_info.deconstruct();
+
+ let mut builder = CLIENT.request(
+ method.hyper_method(),
+ &path.into_owned(),
+ );
+
+ if let Some(ref bytes) = body {
+ builder = builder.body(Body::BufBody(bytes, bytes.len()));
+ }
+
+ let mut headers = Headers::new();
+ headers.set(UserAgent(constants::USER_AGENT.to_string()));
+ headers.set(Authorization(TOKEN.lock().clone()));
+ headers.set(ContentType::json());
+
+ if let Some(request_headers) = request_headers.clone() {
+ headers.extend(request_headers.iter());
+ }
+
+ builder.headers(headers)
+ }
+
+ pub fn body_ref(&self) -> &Option<&'a [u8]> {
+ &self.body
+ }
+
+ pub fn body_mut(&mut self) -> &mut Option<&'a [u8]> {
+ &mut self.body
+ }
+
+ pub fn headers_ref(&self) -> &Option<Headers> {
+ &self.headers
+ }
+
+ pub fn headers_mut(&mut self) -> &mut Option<Headers> {
+ &mut self.headers
+ }
+
+ pub fn route_ref(&self) -> &RouteInfo {
+ &self.route
+ }
+
+ pub fn route_mut(&mut self) -> &mut RouteInfo<'a> {
+ &mut self.route
+ }
+}
diff --git a/src/http/routing.rs b/src/http/routing.rs
new file mode 100644
index 0000000..52be61c
--- /dev/null
+++ b/src/http/routing.rs
@@ -0,0 +1,1433 @@
+use std::{
+ borrow::Cow,
+ fmt::{Display, Write},
+};
+use super::LightMethod;
+
+/// A representation of all routes registered within the library. These are safe
+/// and memory-efficient representations of each path that functions exist for
+/// in the [`http`] module.
+///
+/// [`http`]: ../index.html
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub enum Route {
+ /// Route for the `/channels/:channel_id` path.
+ ///
+ /// The data is the relevant [`ChannelId`].
+ ///
+ /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
+ ChannelsId(u64),
+ /// Route for the `/channels/:channel_id/invites` path.
+ ///
+ /// The data is the relevant [`ChannelId`].
+ ///
+ /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
+ ChannelsIdInvites(u64),
+ /// Route for the `/channels/:channel_id/messages` path.
+ ///
+ /// The data is the relevant [`ChannelId`].
+ ///
+ /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
+ ChannelsIdMessages(u64),
+ /// Route for the `/channels/:channel_id/messages/bulk-delete` path.
+ ///
+ /// The data is the relevant [`ChannelId`].
+ ///
+ /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
+ ChannelsIdMessagesBulkDelete(u64),
+ /// Route for the `/channels/:channel_id/messages/:message_id` path.
+ ///
+ /// The data is the relevant [`ChannelId`].
+ ///
+ /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
+ // This route is a unique case. The ratelimit for message _deletions_ is
+ // different than the overall route ratelimit.
+ //
+ // Refer to the docs on [Rate Limits] in the yellow warning section.
+ //
+ // Additionally, this needs to be a `LightMethod` from the parent module
+ // and _not_ a `hyper` `Method` due to `hyper`'s not deriving `Copy`.
+ //
+ // [Rate Limits]: https://discordapp.com/developers/docs/topics/rate-limits
+ ChannelsIdMessagesId(LightMethod, u64),
+ /// Route for the `/channels/:channel_id/messages/:message_id/ack` path.
+ ///
+ /// The data is the relevant [`ChannelId`].
+ ///
+ /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
+ ChannelsIdMessagesIdAck(u64),
+ /// Route for the `/channels/:channel_id/messages/:message_id/reactions`
+ /// path.
+ ///
+ /// The data is the relevant [`ChannelId`].
+ ///
+ /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
+ ChannelsIdMessagesIdReactions(u64),
+ /// Route for the
+ /// `/channels/:channel_id/messages/:message_id/reactions/:reaction/@me`
+ /// path.
+ ///
+ /// The data is the relevant [`ChannelId`].
+ ///
+ /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
+ ChannelsIdMessagesIdReactionsUserIdType(u64),
+ /// Route for the `/channels/:channel_id/permissions/:target_id` path.
+ ///
+ /// The data is the relevant [`ChannelId`].
+ ///
+ /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
+ ChannelsIdPermissionsOverwriteId(u64),
+ /// Route for the `/channels/:channel_id/pins` path.
+ ///
+ /// The data is the relevant [`ChannelId`].
+ ///
+ /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
+ ChannelsIdPins(u64),
+ /// Route for the `/channels/:channel_id/pins/:message_id` path.
+ ///
+ /// The data is the relevant [`ChannelId`].
+ ///
+ /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
+ ChannelsIdPinsMessageId(u64),
+ /// Route for the `/channels/:channel_id/typing` path.
+ ///
+ /// The data is the relevant [`ChannelId`].
+ ///
+ /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
+ ChannelsIdTyping(u64),
+ /// Route for the `/channels/:channel_id/webhooks` path.
+ ///
+ /// The data is the relevant [`ChannelId`].
+ ///
+ /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
+ ChannelsIdWebhooks(u64),
+ /// Route for the `/gateway` path.
+ Gateway,
+ /// Route for the `/gateway/bot` path.
+ GatewayBot,
+ /// Route for the `/guilds` path.
+ Guilds,
+ /// Route for the `/guilds/:guild_id` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsId(u64),
+ /// Route for the `/guilds/:guild_id/bans` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdBans(u64),
+ /// Route for the `/guilds/:guild_id/audit-logs` path.
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdAuditLogs(u64),
+ /// Route for the `/guilds/:guild_id/bans/:user_id` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdBansUserId(u64),
+ /// Route for the `/guilds/:guild_id/channels/:channel_id` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdChannels(u64),
+ /// Route for the `/guilds/:guild_id/embed` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdEmbed(u64),
+ /// Route for the `/guilds/:guild_id/emojis` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdEmojis(u64),
+ /// Route for the `/guilds/:guild_id/emojis/:emoji_id` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdEmojisId(u64),
+ /// Route for the `/guilds/:guild_id/integrations` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdIntegrations(u64),
+ /// Route for the `/guilds/:guild_id/integrations/:integration_id` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdIntegrationsId(u64),
+ /// Route for the `/guilds/:guild_id/integrations/:integration_id/sync`
+ /// path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdIntegrationsIdSync(u64),
+ /// Route for the `/guilds/:guild_id/invites` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdInvites(u64),
+ /// Route for the `/guilds/:guild_id/members` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdMembers(u64),
+ /// Route for the `/guilds/:guild_id/members/:user_id` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdMembersId(u64),
+ /// Route for the `/guilds/:guild_id/members/:user_id/roles/:role_id` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdMembersIdRolesId(u64),
+ /// Route for the `/guilds/:guild_id/members/@me/nick` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdMembersMeNick(u64),
+ /// Route for the `/guilds/:guild_id/prune` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdPrune(u64),
+ /// Route for the `/guilds/:guild_id/regions` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdRegions(u64),
+ /// Route for the `/guilds/:guild_id/roles` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdRoles(u64),
+ /// Route for the `/guilds/:guild_id/roles/:role_id` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdRolesId(u64),
+ /// Route for the `/guilds/:guild_id/vanity-url` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdVanityUrl(u64),
+ /// Route for the `/guilds/:guild_id/webhooks` path.
+ ///
+ /// The data is the relevant [`GuildId`].
+ ///
+ /// [`GuildId`]: ../../model/id/struct.GuildId.html
+ GuildsIdWebhooks(u64),
+ /// Route for the `/invites/:code` path.
+ InvitesCode,
+ /// Route for the `/users/:user_id` path.
+ UsersId,
+ /// Route for the `/users/@me` path.
+ UsersMe,
+ /// Route for the `/users/@me/channels` path.
+ UsersMeChannels,
+ /// Route for the `/users/@me/guilds` path.
+ UsersMeGuilds,
+ /// Route for the `/users/@me/guilds/:guild_id` path.
+ UsersMeGuildsId,
+ /// Route for the `/voice/regions` path.
+ VoiceRegions,
+ /// Route for the `/webhooks/:webhook_id` path.
+ WebhooksId(u64),
+ /// Route where no ratelimit headers are in place (i.e. user account-only
+ /// routes).
+ ///
+ /// This is a special case, in that if the route is `None` then pre- and
+ /// post-hooks are not executed.
+ None,
+}
+
+impl Route {
+ pub fn channel(channel_id: u64) -> String {
+ format!(api!("/channels/{}"), channel_id)
+ }
+
+ pub fn channel_invites(channel_id: u64) -> String {
+ format!(api!("/channels/{}/invites"), channel_id)
+ }
+
+ pub fn channel_message(channel_id: u64, message_id: u64) -> String {
+ format!(api!("/channels/{}/messages/{}"), channel_id, message_id)
+ }
+
+ pub fn channel_message_reaction<D, T>(
+ channel_id: u64,
+ message_id: u64,
+ user_id: D,
+ reaction_type: T
+ ) -> String where D: Display, T: Display {
+ format!(
+ api!("/channels/{}/messages/{}/reactions/{}/{}"),
+ channel_id,
+ message_id,
+ reaction_type,
+ user_id,
+ )
+ }
+
+ pub fn channel_message_reactions(
+ channel_id: u64,
+ message_id: u64,
+ ) -> String {
+ api!("/channels/{}/messages/{}/reactions", channel_id, message_id)
+ }
+
+ pub fn channel_message_reactions_list(
+ channel_id: u64,
+ message_id: u64,
+ reaction: &str,
+ limit: u8,
+ after: Option<u64>,
+ ) -> String {
+ let mut uri = format!(
+ api!("/channels/{}/messages/{}/reactions/{}?limit={}"),
+ channel_id,
+ message_id,
+ reaction,
+ limit,
+ );
+
+ if let Some(after) = after {
+ let _ = write!(uri, "&after={}", after);
+ }
+
+ uri
+ }
+
+ pub fn channel_messages(channel_id: u64, query: Option<&str>) -> String {
+ format!(
+ api!("/channels/{}/messages{}"),
+ channel_id,
+ query.unwrap_or(""),
+ )
+ }
+
+ pub fn channel_messages_bulk_delete(channel_id: u64) -> String {
+ format!(api!("/channels/{}/messages/bulk-delete"), channel_id)
+ }
+
+ pub fn channel_permission(channel_id: u64, target_id: u64) -> String {
+ format!(api!("/channels/{}/permissions/{}"), channel_id, target_id)
+ }
+
+ pub fn channel_pin(channel_id: u64, message_id: u64) -> String {
+ format!(api!("/channels/{}/pins/{}"), channel_id, message_id)
+ }
+
+ pub fn channel_pins(channel_id: u64) -> String {
+ format!(api!("/channels/{}/pins"), channel_id)
+ }
+
+ pub fn channel_typing(channel_id: u64) -> String {
+ format!(api!("/channels/{}/typing"), channel_id)
+ }
+
+ pub fn channel_webhooks(channel_id: u64) -> String {
+ format!(api!("/channels/{}/webhooks"), channel_id)
+ }
+
+ pub fn gateway() -> &'static str {
+ api!("/gateway")
+ }
+
+ pub fn gateway_bot() -> &'static str {
+ api!("/gateway/bot")
+ }
+
+ pub fn group_recipient(group_id: u64, user_id: u64) -> String {
+ format!(api!("/channels/{}/recipients/{}"), group_id, user_id)
+ }
+
+ pub fn guild(guild_id: u64) -> String {
+ format!(api!("/guilds/{}"), guild_id)
+ }
+
+ pub fn guild_audit_logs(
+ guild_id: u64,
+ action_type: Option<u8>,
+ user_id: Option<u64>,
+ before: Option<u64>,
+ limit: Option<u8>,
+ ) -> String {
+ let mut s = format!(
+ api!("/guilds/{}/audit-logs?"),
+ guild_id,
+ );
+
+ if let Some(action_type) = action_type {
+ let _ = write!(s, "&action_type={}", action_type);
+ }
+
+ if let Some(before) = before {
+ let _ = write!(s, "&before={}", before);
+ }
+
+ if let Some(limit) = limit {
+ let _ = write!(s, "&limit={}", limit);
+ }
+
+ if let Some(user_id) = user_id {
+ let _ = write!(s, "&user_id={}", user_id);
+ }
+
+ s
+ }
+
+ pub fn guild_ban(guild_id: u64, user_id: u64) -> String {
+ format!(api!("/guilds/{}/bans/{}"), guild_id, user_id)
+ }
+
+ pub fn guild_ban_optioned(
+ guild_id: u64,
+ user_id: u64,
+ delete_message_days: u8,
+ reason: &str,
+ ) -> String {
+ format!(
+ api!("/guilds/{}/bans/{}?delete_message_days={}&reason={}"),
+ guild_id,
+ user_id,
+ delete_message_days,
+ reason,
+ )
+ }
+
+ pub fn guild_bans(guild_id: u64) -> String {
+ format!(api!("/guilds/{}/bans"), guild_id)
+ }
+
+ pub fn guild_channels(guild_id: u64) -> String {
+ format!(api!("/guilds/{}/channels"), guild_id)
+ }
+
+ pub fn guild_embed(guild_id: u64) -> String {
+ format!(api!("/guilds/{}/embed"), guild_id)
+ }
+
+ pub fn guild_emojis(guild_id: u64) -> String {
+ format!(api!("/guilds/{}/emojis"), guild_id)
+ }
+
+ pub fn guild_emoji(guild_id: u64, emoji_id: u64) -> String {
+ format!(api!("/guilds/{}/emojis/{}"), guild_id, emoji_id)
+ }
+
+ pub fn guild_integration(
+ guild_id: u64,
+ integration_id: u64,
+ ) -> String {
+ format!(api!("/guilds/{}/integrations/{}"), guild_id, integration_id)
+ }
+
+ pub fn guild_integration_sync(
+ guild_id: u64,
+ integration_id: u64,
+ ) -> String {
+ format!(
+ api!("/guilds/{}/integrations/{}/sync"),
+ guild_id,
+ integration_id,
+ )
+ }
+
+ pub fn guild_integrations(guild_id: u64) -> String {
+ format!(api!("/guilds/{}/integrations"), guild_id)
+ }
+
+ pub fn guild_invites(guild_id: u64) -> String {
+ format!(api!("/guilds/{}/invites"), guild_id)
+ }
+
+ pub fn guild_member(guild_id: u64, user_id: u64) -> String {
+ format!(api!("/guilds/{}/members/{}"), guild_id, user_id)
+ }
+
+ pub fn guild_member_role(
+ guild_id: u64,
+ user_id: u64,
+ role_id: u64,
+ ) -> String {
+ format!(
+ api!("/guilds/{}/members/{}/roles/{}"),
+ guild_id,
+ user_id,
+ role_id,
+ )
+ }
+
+ pub fn guild_members(guild_id: u64) -> String {
+ format!(api!("/guilds/{}/members"), guild_id)
+ }
+
+ pub fn guild_members_optioned(
+ guild_id: u64,
+ after: Option<u64>,
+ limit: Option<u64>,
+ ) -> String {
+ let mut s = format!(api!("/guilds/{}/members?"), guild_id);
+
+ if let Some(after) = after {
+ let _ = write!(s, "&after={}", after);
+ }
+
+ if let Some(limit) = limit {
+ let _ = write!(s, "&limit={}", limit);
+ }
+
+ s
+ }
+
+ pub fn guild_nickname(guild_id: u64) -> String {
+ format!(api!("/guilds/{}/members/@me/nick"), guild_id)
+ }
+
+ pub fn guild_prune(guild_id: u64, days: u64) -> String {
+ format!(api!("/guilds/{}/prune?days={}"), guild_id, days)
+ }
+
+ pub fn guild_regions(guild_id: u64) -> String {
+ format!(api!("/guilds/{}/regions"), guild_id)
+ }
+
+ pub fn guild_role(guild_id: u64, role_id: u64) -> String {
+ format!(api!("/guilds/{}/roles/{}"), guild_id, role_id)
+ }
+
+ pub fn guild_roles(guild_id: u64) -> String {
+ format!(api!("/guilds/{}/roles"), guild_id)
+ }
+
+ pub fn guild_vanity_url(guild_id: u64) -> String {
+ format!(api!("/guilds/{}/vanity-url"), guild_id)
+ }
+
+ pub fn guild_webhooks(guild_id: u64) -> String {
+ format!(api!("/guilds/{}/webhooks"), guild_id)
+ }
+
+ pub fn guilds() -> &'static str {
+ api!("/guilds")
+ }
+
+ pub fn invite(code: &str) -> String {
+ format!(api!("/invites/{}"), code)
+ }
+
+ pub fn invite_optioned(code: &str, stats: bool) -> String {
+ format!(api!("/invites/{}?with_counts={}"), code, stats)
+ }
+
+ pub fn oauth2_application_current() -> &'static str {
+ api!("/oauth2/applications/@me")
+ }
+
+ pub fn private_channel() -> &'static str {
+ api!("/users/@me/channels")
+ }
+
+ pub fn status_incidents_unresolved() -> &'static str {
+ status!("/incidents/unresolved.json")
+ }
+
+ pub fn status_maintenances_active() -> &'static str {
+ status!("/scheduled-maintenances/active.json")
+ }
+
+ pub fn status_maintenances_upcoming() -> &'static str {
+ status!("/scheduled-maintenances/upcoming.json")
+ }
+
+ pub fn user<D: Display>(target: D) -> String {
+ format!(api!("/users/{}"), target)
+ }
+
+ pub fn user_dm_channels<D: Display>(target: D) -> String {
+ format!(api!("/users/{}/channels"), target)
+ }
+
+ pub fn user_guild<D: Display>(target: D, guild_id: u64) -> String {
+ format!(api!("/users/{}/guilds/{}"), target, guild_id)
+ }
+
+ pub fn user_guilds<D: Display>(target: D) -> String {
+ format!(api!("/users/{}/guilds"), target)
+ }
+
+ pub fn user_guilds_optioned<D: Display>(
+ target: D,
+ after: Option<u64>,
+ before: Option<u64>,
+ limit: u64,
+ ) -> String {
+ let mut s = format!(api!("/users/{}/guilds?limit={}&"), target, limit);
+
+ if let Some(after) = after {
+ let _ = write!(s, "&after={}", after);
+ }
+
+ if let Some(before) = before {
+ let _ = write!(s, "&before={}", before);
+ }
+
+ s
+ }
+
+ pub fn voice_regions() -> &'static str {
+ api!("/voice/regions")
+ }
+
+ pub fn webhook(webhook_id: u64) -> String {
+ format!(api!("/webhooks/{}"), webhook_id)
+ }
+
+ pub fn webhook_with_token<D>(webhook_id: u64, token: D) -> String
+ where D: Display {
+ format!(api!("/webhooks/{}/{}"), webhook_id, token)
+ }
+
+ pub fn webhook_with_token_optioned<D>(webhook_id: u64, token: D, wait: bool)
+ -> String where D: Display {
+ format!(api!("/webhooks/{}/{}?wait={}"), webhook_id, token, wait)
+ }
+}
+
+#[derive(Clone, Debug)]
+pub enum RouteInfo<'a> {
+ AddGroupRecipient {
+ group_id: u64,
+ user_id: u64,
+ },
+ AddMemberRole {
+ guild_id: u64,
+ role_id: u64,
+ user_id: u64,
+ },
+ GuildBanUser {
+ guild_id: u64,
+ user_id: u64,
+ delete_message_days: Option<u8>,
+ reason: Option<&'a str>,
+ },
+ BroadcastTyping {
+ channel_id: u64,
+ },
+ CreateChannel {
+ guild_id: u64,
+ },
+ CreateEmoji {
+ guild_id: u64,
+ },
+ CreateGuild,
+ CreateGuildIntegration {
+ guild_id: u64,
+ integration_id: u64,
+ },
+ CreateInvite {
+ channel_id: u64,
+ },
+ CreateMessage {
+ channel_id: u64,
+ },
+ CreatePermission {
+ channel_id: u64,
+ target_id: u64,
+ },
+ CreatePrivateChannel,
+ CreateReaction {
+ channel_id: u64,
+ message_id: u64,
+ reaction: &'a str,
+ },
+ CreateRole {
+ guild_id: u64,
+ },
+ CreateWebhook {
+ channel_id: u64,
+ },
+ DeleteChannel {
+ channel_id: u64,
+ },
+ DeleteEmoji {
+ guild_id: u64,
+ emoji_id: u64,
+ },
+ DeleteGuild {
+ guild_id: u64,
+ },
+ DeleteGuildIntegration {
+ guild_id: u64,
+ integration_id: u64,
+ },
+ DeleteInvite {
+ code: &'a str,
+ },
+ DeleteMessage {
+ channel_id: u64,
+ message_id: u64,
+ },
+ DeleteMessages {
+ channel_id: u64,
+ },
+ DeleteMessageReactions {
+ channel_id: u64,
+ message_id: u64,
+ },
+ DeletePermission {
+ channel_id: u64,
+ target_id: u64,
+ },
+ DeleteReaction {
+ channel_id: u64,
+ message_id: u64,
+ user: &'a str,
+ reaction: &'a str,
+ },
+ DeleteRole {
+ guild_id: u64,
+ role_id: u64,
+ },
+ DeleteWebhook {
+ webhook_id: u64,
+ },
+ DeleteWebhookWithToken {
+ token: &'a str,
+ webhook_id: u64,
+ },
+ EditChannel {
+ channel_id: u64,
+ },
+ EditEmoji {
+ guild_id: u64,
+ emoji_id: u64,
+ },
+ EditGuild {
+ guild_id: u64,
+ },
+ EditGuildChannels {
+ guild_id: u64,
+ },
+ EditGuildEmbed {
+ guild_id: u64,
+ },
+ EditMember {
+ guild_id: u64,
+ user_id: u64,
+ },
+ EditMessage {
+ channel_id: u64,
+ message_id: u64,
+ },
+ EditNickname {
+ guild_id: u64,
+ },
+ EditProfile,
+ EditRole {
+ guild_id: u64,
+ role_id: u64,
+ },
+ EditWebhook {
+ webhook_id: u64,
+ },
+ EditWebhookWithToken {
+ token: &'a str,
+ webhook_id: u64,
+ },
+ ExecuteWebhook {
+ token: &'a str,
+ wait: bool,
+ webhook_id: u64,
+ },
+ GetActiveMaintenance,
+ GetAuditLogs {
+ action_type: Option<u8>,
+ before: Option<u64>,
+ guild_id: u64,
+ limit: Option<u8>,
+ user_id: Option<u64>,
+ },
+ GetBans {
+ guild_id: u64,
+ },
+ GetBotGateway,
+ GetChannel {
+ channel_id: u64,
+ },
+ GetChannelInvites {
+ channel_id: u64,
+ },
+ GetChannelWebhooks {
+ channel_id: u64,
+ },
+ GetChannels {
+ guild_id: u64,
+ },
+ GetCurrentApplicationInfo,
+ GetCurrentUser,
+ GetGateway,
+ GetGuild {
+ guild_id: u64,
+ },
+ GetGuildEmbed {
+ guild_id: u64,
+ },
+ GetGuildIntegrations {
+ guild_id: u64,
+ },
+ GetGuildInvites {
+ guild_id: u64,
+ },
+ GetGuildMembers {
+ after: Option<u64>,
+ limit: Option<u64>,
+ guild_id: u64,
+ },
+ GetGuildPruneCount {
+ days: u64,
+ guild_id: u64,
+ },
+ GetGuildRegions {
+ guild_id: u64,
+ },
+ GetGuildRoles {
+ guild_id: u64,
+ },
+ GetGuildVanityUrl {
+ guild_id: u64,
+ },
+ GetGuildWebhooks {
+ guild_id: u64,
+ },
+ GetGuilds {
+ after: Option<u64>,
+ before: Option<u64>,
+ limit: u64,
+ },
+ GetInvite {
+ code: &'a str,
+ stats: bool,
+ },
+ GetMember {
+ guild_id: u64,
+ user_id: u64,
+ },
+ GetMessage {
+ channel_id: u64,
+ message_id: u64,
+ },
+ GetMessages {
+ channel_id: u64,
+ query: String,
+ },
+ GetPins {
+ channel_id: u64,
+ },
+ GetReactionUsers {
+ after: Option<u64>,
+ channel_id: u64,
+ limit: u8,
+ message_id: u64,
+ reaction: String,
+ },
+ GetUnresolvedIncidents,
+ GetUpcomingMaintenances,
+ GetUser {
+ user_id: u64,
+ },
+ GetUserDmChannels,
+ GetVoiceRegions,
+ GetWebhook {
+ webhook_id: u64,
+ },
+ GetWebhookWithToken {
+ token: &'a str,
+ webhook_id: u64,
+ },
+ KickMember {
+ guild_id: u64,
+ user_id: u64,
+ },
+ LeaveGroup {
+ group_id: u64,
+ },
+ LeaveGuild {
+ guild_id: u64,
+ },
+ RemoveGroupRecipient {
+ group_id: u64,
+ user_id: u64,
+ },
+ PinMessage {
+ channel_id: u64,
+ message_id: u64,
+ },
+ RemoveBan {
+ guild_id: u64,
+ user_id: u64,
+ },
+ RemoveMemberRole {
+ guild_id: u64,
+ role_id: u64,
+ user_id: u64,
+ },
+ StartGuildPrune {
+ days: u64,
+ guild_id: u64,
+ },
+ StartIntegrationSync {
+ guild_id: u64,
+ integration_id: u64,
+ },
+ StatusIncidentsUnresolved,
+ StatusMaintenancesActive,
+ StatusMaintenancesUpcoming,
+ UnpinMessage {
+ channel_id: u64,
+ message_id: u64,
+ },
+}
+
+impl<'a> RouteInfo<'a> {
+ pub fn deconstruct(&self) -> (LightMethod, Route, Cow<str>) {
+ match *self {
+ RouteInfo::AddGroupRecipient { group_id, user_id } => (
+ LightMethod::Put,
+ Route::None,
+ Cow::from(Route::group_recipient(group_id, user_id)),
+ ),
+ RouteInfo::AddMemberRole { guild_id, role_id, user_id } => (
+ LightMethod::Put,
+ Route::GuildsIdMembersIdRolesId(guild_id),
+ Cow::from(Route::guild_member_role(guild_id, user_id, role_id)),
+ ),
+ RouteInfo::GuildBanUser {
+ guild_id,
+ delete_message_days,
+ reason,
+ user_id,
+ } => (
+ // TODO
+ LightMethod::Put,
+ Route::GuildsIdBansUserId(guild_id),
+ Cow::from(Route::guild_ban_optioned(
+ guild_id,
+ user_id,
+ delete_message_days.unwrap_or(0),
+ reason.unwrap_or(""),
+ )),
+ ),
+ RouteInfo::BroadcastTyping { channel_id } => (
+ LightMethod::Post,
+ Route::ChannelsIdTyping(channel_id),
+ Cow::from(Route::channel_typing(channel_id)),
+ ),
+ RouteInfo::CreateChannel { guild_id } => (
+ LightMethod::Post,
+ Route::GuildsIdChannels(guild_id),
+ Cow::from(Route::guild_channels(guild_id)),
+ ),
+ RouteInfo::CreateEmoji { guild_id } => (
+ LightMethod::Post,
+ Route::GuildsIdEmojis(guild_id),
+ Cow::from(Route::guild_emojis(guild_id)),
+ ),
+ RouteInfo::CreateGuild => (
+ LightMethod::Post,
+ Route::Guilds,
+ Cow::from(Route::guilds()),
+ ),
+ RouteInfo::CreateGuildIntegration { guild_id, integration_id } => (
+ LightMethod::Post,
+ Route::GuildsIdIntegrationsId(guild_id),
+ Cow::from(Route::guild_integration(guild_id, integration_id)),
+ ),
+ RouteInfo::CreateInvite { channel_id } => (
+ LightMethod::Post,
+ Route::ChannelsIdInvites(channel_id),
+ Cow::from(Route::channel_invites(channel_id)),
+ ),
+ RouteInfo::CreateMessage { channel_id } => (
+ LightMethod::Post,
+ Route::ChannelsIdMessages(channel_id),
+ Cow::from(Route::channel_messages(channel_id, None)),
+ ),
+ RouteInfo::CreatePermission { channel_id, target_id } => (
+ LightMethod::Put,
+ Route::ChannelsIdPermissionsOverwriteId(channel_id),
+ Cow::from(Route::channel_permission(channel_id, target_id)),
+ ),
+ RouteInfo::CreatePrivateChannel => (
+ LightMethod::Post,
+ Route::UsersMeChannels,
+ Cow::from(Route::user_dm_channels("@me")),
+ ),
+ RouteInfo::CreateReaction { channel_id, message_id, reaction } => (
+ LightMethod::Put,
+ Route::ChannelsIdMessagesIdReactionsUserIdType(channel_id),
+ Cow::from(Route::channel_message_reaction(
+ channel_id,
+ message_id,
+ "@me",
+ reaction,
+ )),
+ ),
+ RouteInfo::CreateRole { guild_id } => (
+ LightMethod::Post,
+ Route::GuildsIdRoles(guild_id),
+ Cow::from(Route::guild_roles(guild_id)),
+ ),
+ RouteInfo::CreateWebhook { channel_id } => (
+ LightMethod::Post,
+ Route::ChannelsIdWebhooks(channel_id),
+ Cow::from(Route::channel_webhooks(channel_id)),
+ ),
+ RouteInfo::DeleteChannel { channel_id } => (
+ LightMethod::Delete,
+ Route::ChannelsId(channel_id),
+ Cow::from(Route::channel(channel_id)),
+ ),
+ RouteInfo::DeleteEmoji { emoji_id, guild_id } => (
+ LightMethod::Delete,
+ Route::GuildsIdEmojisId(guild_id),
+ Cow::from(Route::guild_emoji(guild_id, emoji_id)),
+ ),
+ RouteInfo::DeleteGuild { guild_id } => (
+ LightMethod::Delete,
+ Route::GuildsId(guild_id),
+ Cow::from(Route::guild(guild_id)),
+ ),
+ RouteInfo::DeleteGuildIntegration { guild_id, integration_id } => (
+ LightMethod::Delete,
+ Route::GuildsIdIntegrationsId(guild_id),
+ Cow::from(Route::guild_integration(guild_id, integration_id)),
+ ),
+ RouteInfo::DeleteInvite { code } => (
+ LightMethod::Delete,
+ Route::InvitesCode,
+ Cow::from(Route::invite(code)),
+ ),
+ RouteInfo::DeleteMessageReactions { channel_id, message_id } => (
+ LightMethod::Delete,
+ Route::ChannelsIdMessagesIdReactions(channel_id),
+ Cow::from(Route::channel_message_reactions(
+ channel_id,
+ message_id,
+ )),
+ ),
+ RouteInfo::DeleteMessage { channel_id, message_id } => (
+ LightMethod::Delete,
+ Route::ChannelsIdMessagesId(LightMethod::Delete, message_id),
+ Cow::from(Route::channel_message(channel_id, message_id)),
+ ),
+ RouteInfo::DeleteMessages { channel_id } => (
+ LightMethod::Delete,
+ Route::ChannelsIdMessagesBulkDelete(channel_id),
+ Cow::from(Route::channel_messages_bulk_delete(channel_id)),
+ ),
+ RouteInfo::DeletePermission { channel_id, target_id } => (
+ LightMethod::Delete,
+ Route::ChannelsIdPermissionsOverwriteId(channel_id),
+ Cow::from(Route::channel_permission(channel_id, target_id)),
+ ),
+ RouteInfo::DeleteReaction {
+ channel_id,
+ message_id,
+ reaction,
+ user,
+ } => (
+ LightMethod::Delete,
+ Route::ChannelsIdMessagesIdReactionsUserIdType(channel_id),
+ Cow::from(Route::channel_message_reaction(
+ channel_id,
+ message_id,
+ user,
+ reaction,
+ ))
+ ),
+ RouteInfo::DeleteRole { guild_id, role_id } => (
+ LightMethod::Delete,
+ Route::GuildsIdRolesId(guild_id),
+ Cow::from(Route::guild_role(guild_id, role_id)),
+ ),
+ RouteInfo::DeleteWebhook { webhook_id } => (
+ LightMethod::Delete,
+ Route::WebhooksId(webhook_id),
+ Cow::from(Route::webhook(webhook_id)),
+ ),
+ RouteInfo::DeleteWebhookWithToken { token, webhook_id } => (
+ LightMethod::Delete,
+ Route::WebhooksId(webhook_id),
+ Cow::from(Route::webhook_with_token(webhook_id, token)),
+ ),
+ RouteInfo::EditChannel { channel_id } => (
+ LightMethod::Patch,
+ Route::ChannelsId(channel_id),
+ Cow::from(Route::channel(channel_id)),
+ ),
+ RouteInfo::EditEmoji { emoji_id, guild_id } => (
+ LightMethod::Patch,
+ Route::GuildsIdEmojisId(guild_id),
+ Cow::from(Route::guild_emoji(guild_id, emoji_id)),
+ ),
+ RouteInfo::EditGuild { guild_id } => (
+ LightMethod::Patch,
+ Route::GuildsId(guild_id),
+ Cow::from(Route::guild(guild_id)),
+ ),
+ RouteInfo::EditGuildChannels { guild_id } => (
+ LightMethod::Patch,
+ Route::GuildsIdChannels(guild_id),
+ Cow::from(Route::guild_channels(guild_id)),
+ ),
+ RouteInfo::EditGuildEmbed { guild_id } => (
+ LightMethod::Patch,
+ Route::GuildsIdEmbed(guild_id),
+ Cow::from(Route::guild_embed(guild_id)),
+ ),
+ RouteInfo::EditMember { guild_id, user_id } => (
+ LightMethod::Patch,
+ Route::GuildsIdMembersId(guild_id),
+ Cow::from(Route::guild_member(guild_id, user_id)),
+ ),
+ RouteInfo::EditMessage { channel_id, message_id } => (
+ LightMethod::Patch,
+ Route::ChannelsIdMessagesId(LightMethod::Patch, channel_id),
+ Cow::from(Route::channel_message(channel_id, message_id)),
+ ),
+ RouteInfo::EditNickname { guild_id } => (
+ LightMethod::Patch,
+ Route::GuildsIdMembersMeNick(guild_id),
+ Cow::from(Route::guild_nickname(guild_id)),
+ ),
+ RouteInfo::EditProfile => (
+ LightMethod::Patch,
+ Route::UsersMe,
+ Cow::from(Route::user("@me")),
+ ),
+ RouteInfo::EditRole { guild_id, role_id } => (
+ LightMethod::Patch,
+ Route::GuildsIdRolesId(guild_id),
+ Cow::from(Route::guild_role(guild_id, role_id)),
+ ),
+ RouteInfo::EditWebhook { webhook_id } => (
+ LightMethod::Patch,
+ Route::WebhooksId(webhook_id),
+ Cow::from(Route::webhook(webhook_id)),
+ ),
+ RouteInfo::EditWebhookWithToken { token, webhook_id } => (
+ LightMethod::Patch,
+ Route::WebhooksId(webhook_id),
+ Cow::from(Route::webhook_with_token(webhook_id, token)),
+ ),
+ RouteInfo::ExecuteWebhook { token, wait, webhook_id } => (
+ LightMethod::Post,
+ Route::WebhooksId(webhook_id),
+ Cow::from(Route::webhook_with_token_optioned(
+ webhook_id,
+ token,
+ wait,
+ )),
+ ),
+ RouteInfo::GetActiveMaintenance => (
+ LightMethod::Get,
+ Route::None,
+ Cow::from(Route::status_maintenances_active()),
+ ),
+ RouteInfo::GetAuditLogs {
+ action_type,
+ before,
+ guild_id,
+ limit,
+ user_id,
+ } => (
+ LightMethod::Get,
+ Route::GuildsIdAuditLogs(guild_id),
+ Cow::from(Route::guild_audit_logs(
+ guild_id,
+ action_type,
+ user_id,
+ before,
+ limit,
+ )),
+ ),
+ RouteInfo::GetBans { guild_id } => (
+ LightMethod::Get,
+ Route::GuildsIdBans(guild_id),
+ Cow::from(Route::guild_bans(guild_id)),
+ ),
+ RouteInfo::GetBotGateway => (
+ LightMethod::Get,
+ Route::GatewayBot,
+ Cow::from(Route::gateway_bot()),
+ ),
+ RouteInfo::GetChannel { channel_id } => (
+ LightMethod::Get,
+ Route::ChannelsId(channel_id),
+ Cow::from(Route::channel(channel_id)),
+ ),
+ RouteInfo::GetChannelInvites { channel_id } => (
+ LightMethod::Get,
+ Route::ChannelsIdInvites(channel_id),
+ Cow::from(Route::channel_invites(channel_id)),
+ ),
+ RouteInfo::GetChannelWebhooks { channel_id } => (
+ LightMethod::Get,
+ Route::ChannelsIdWebhooks(channel_id),
+ Cow::from(Route::channel_webhooks(channel_id)),
+ ),
+ RouteInfo::GetChannels { guild_id } => (
+ LightMethod::Get,
+ Route::GuildsIdChannels(guild_id),
+ Cow::from(Route::guild_channels(guild_id)),
+ ),
+ RouteInfo::GetCurrentApplicationInfo => (
+ LightMethod::Get,
+ Route::None,
+ Cow::from(Route::oauth2_application_current()),
+ ),
+ RouteInfo::GetCurrentUser => (
+ LightMethod::Get,
+ Route::UsersMe,
+ Cow::from(Route::user("@me")),
+ ),
+ RouteInfo::GetGateway => (
+ LightMethod::Get,
+ Route::Gateway,
+ Cow::from(Route::gateway()),
+ ),
+ RouteInfo::GetGuild { guild_id } => (
+ LightMethod::Get,
+ Route::GuildsId(guild_id),
+ Cow::from(Route::guild(guild_id)),
+ ),
+ RouteInfo::GetGuildEmbed { guild_id } => (
+ LightMethod::Get,
+ Route::GuildsIdEmbed(guild_id),
+ Cow::from(Route::guild_embed(guild_id)),
+ ),
+ RouteInfo::GetGuildIntegrations { guild_id } => (
+ LightMethod::Get,
+ Route::GuildsIdIntegrations(guild_id),
+ Cow::from(Route::guild_integrations(guild_id)),
+ ),
+ RouteInfo::GetGuildInvites { guild_id } => (
+ LightMethod::Get,
+ Route::GuildsIdInvites(guild_id),
+ Cow::from(Route::guild_invites(guild_id)),
+ ),
+ RouteInfo::GetGuildMembers { after, guild_id, limit } => (
+ LightMethod::Get,
+ Route::GuildsIdMembers(guild_id),
+ Cow::from(Route::guild_members_optioned(guild_id, after, limit)),
+ ),
+ RouteInfo::GetGuildPruneCount { days, guild_id } => (
+ LightMethod::Get,
+ Route::GuildsIdPrune(guild_id),
+ Cow::from(Route::guild_prune(guild_id, days)),
+ ),
+ RouteInfo::GetGuildRegions { guild_id } => (
+ LightMethod::Get,
+ Route::GuildsIdRegions(guild_id),
+ Cow::from(Route::guild_regions(guild_id)),
+ ),
+ RouteInfo::GetGuildRoles { guild_id } => (
+ LightMethod::Get,
+ Route::GuildsIdRoles(guild_id),
+ Cow::from(Route::guild_roles(guild_id)),
+ ),
+ RouteInfo::GetGuildVanityUrl { guild_id } => (
+ LightMethod::Get,
+ Route::GuildsIdVanityUrl(guild_id),
+ Cow::from(Route::guild_vanity_url(guild_id)),
+ ),
+ RouteInfo::GetGuildWebhooks { guild_id } => (
+ LightMethod::Get,
+ Route::GuildsIdWebhooks(guild_id),
+ Cow::from(Route::guild_webhooks(guild_id)),
+ ),
+ RouteInfo::GetGuilds { after, before, limit } => (
+ LightMethod::Get,
+ Route::UsersMeGuilds,
+ Cow::from(Route::user_guilds_optioned(
+ "@me",
+ after,
+ before,
+ limit,
+ )),
+ ),
+ RouteInfo::GetInvite { code, stats } => (
+ LightMethod::Get,
+ Route::InvitesCode,
+ Cow::from(Route::invite_optioned(code, stats)),
+ ),
+ RouteInfo::GetMember { guild_id, user_id } => (
+ LightMethod::Get,
+ Route::GuildsIdMembersId(guild_id),
+ Cow::from(Route::guild_member(guild_id, user_id)),
+ ),
+ RouteInfo::GetMessage { channel_id, message_id } => (
+ LightMethod::Get,
+ Route::ChannelsIdMessagesId(LightMethod::Get, channel_id),
+ Cow::from(Route::channel_message(channel_id, message_id)),
+ ),
+ RouteInfo::GetMessages { channel_id, ref query } => (
+ LightMethod::Get,
+ Route::ChannelsIdMessages(channel_id),
+ Cow::from(Route::channel_messages(
+ channel_id,
+ Some(query.as_ref()),
+ )),
+ ),
+ RouteInfo::GetPins { channel_id } => (
+ LightMethod::Get,
+ Route::ChannelsIdPins(channel_id),
+ Cow::from(Route::channel_pins(channel_id)),
+ ),
+ RouteInfo::GetReactionUsers {
+ after,
+ channel_id,
+ limit,
+ message_id,
+ ref reaction,
+ } => (
+ LightMethod::Get,
+ Route::ChannelsIdMessagesIdReactions(channel_id),
+ Cow::from(Route::channel_message_reactions_list(
+ channel_id,
+ message_id,
+ reaction,
+ limit,
+ after,
+ )),
+ ),
+ RouteInfo::GetUnresolvedIncidents => (
+ LightMethod::Get,
+ Route::None,
+ Cow::from(Route::status_incidents_unresolved()),
+ ),
+ RouteInfo::GetUpcomingMaintenances => (
+ LightMethod::Get,
+ Route::None,
+ Cow::from(Route::status_maintenances_upcoming()),
+ ),
+ RouteInfo::GetUser { user_id } => (
+ LightMethod::Get,
+ Route::UsersId,
+ Cow::from(Route::user(user_id)),
+ ),
+ RouteInfo::GetUserDmChannels => (
+ LightMethod::Get,
+ Route::UsersMeChannels,
+ Cow::from(Route::user_dm_channels("@me")),
+ ),
+ RouteInfo::GetVoiceRegions => (
+ LightMethod::Get,
+ Route::VoiceRegions,
+ Cow::from(Route::voice_regions()),
+ ),
+ RouteInfo::GetWebhook { webhook_id } => (
+ LightMethod::Get,
+ Route::WebhooksId(webhook_id),
+ Cow::from(Route::webhook(webhook_id)),
+ ),
+ RouteInfo::GetWebhookWithToken { token, webhook_id } => (
+ LightMethod::Get,
+ Route::WebhooksId(webhook_id),
+ Cow::from(Route::webhook_with_token(webhook_id, token)),
+ ),
+ RouteInfo::KickMember { guild_id, user_id } => (
+ LightMethod::Delete,
+ Route::GuildsIdMembersId(guild_id),
+ Cow::from(Route::guild_member(guild_id, user_id)),
+ ),
+ RouteInfo::LeaveGroup { group_id } => (
+ LightMethod::Delete,
+ Route::ChannelsId(group_id),
+ Cow::from(Route::channel(group_id)),
+ ),
+ RouteInfo::LeaveGuild { guild_id } => (
+ LightMethod::Delete,
+ Route::UsersMeGuildsId,
+ Cow::from(Route::user_guild("@me", guild_id)),
+ ),
+ RouteInfo::RemoveGroupRecipient { group_id, user_id } => (
+ LightMethod::Delete,
+ Route::None,
+ Cow::from(Route::group_recipient(group_id, user_id)),
+ ),
+ RouteInfo::PinMessage { channel_id, message_id } => (
+ LightMethod::Put,
+ Route::ChannelsIdPins(channel_id),
+ Cow::from(Route::channel_pin(channel_id, message_id)),
+ ),
+ RouteInfo::RemoveBan { guild_id, user_id } => (
+ LightMethod::Delete,
+ Route::GuildsIdBansUserId(guild_id),
+ Cow::from(Route::guild_ban(guild_id, user_id)),
+ ),
+ RouteInfo::RemoveMemberRole { guild_id, role_id, user_id } => (
+ LightMethod::Delete,
+ Route::GuildsIdMembersIdRolesId(guild_id),
+ Cow::from(Route::guild_member_role(guild_id, user_id, role_id)),
+ ),
+ RouteInfo::StartGuildPrune { days, guild_id } => (
+ LightMethod::Post,
+ Route::GuildsIdPrune(guild_id),
+ Cow::from(Route::guild_prune(guild_id, days)),
+ ),
+ RouteInfo::StartIntegrationSync { guild_id, integration_id } => (
+ LightMethod::Post,
+ Route::GuildsIdIntegrationsId(guild_id),
+ Cow::from(Route::guild_integration_sync(
+ guild_id,
+ integration_id,
+ )),
+ ),
+ RouteInfo::StatusIncidentsUnresolved => (
+ LightMethod::Get,
+ Route::None,
+ Cow::from(Route::status_incidents_unresolved()),
+ ),
+ RouteInfo::StatusMaintenancesActive => (
+ LightMethod::Get,
+ Route::None,
+ Cow::from(Route::status_maintenances_active()),
+ ),
+ RouteInfo::StatusMaintenancesUpcoming => (
+ LightMethod::Get,
+ Route::None,
+ Cow::from(Route::status_maintenances_upcoming()),
+ ),
+ RouteInfo::UnpinMessage { channel_id, message_id } => (
+ LightMethod::Delete,
+ Route::ChannelsIdPinsMessageId(channel_id),
+ Cow::from(Route::channel_pin(channel_id, message_id)),
+ ),
+ }
+ }
+}
diff --git a/src/internal/macros.rs b/src/internal/macros.rs
index c4d2b6f..a03e9bd 100644
--- a/src/internal/macros.rs
+++ b/src/internal/macros.rs
@@ -1,36 +1,6 @@
//! A set of macros for easily working with internals.
-#[cfg(feature = "http")]
-macro_rules! request {
- ($route:expr, $method:ident($body:expr), $url:expr, $($rest:tt)*) => {{
- let client = request_client!();
-
- request($route, || client
- .$method(&format!(api!($url), $($rest)*))
- .body(&$body))?
- }};
- ($route:expr, $method:ident($body:expr), $url:expr) => {{
- let client = request_client!();
-
- request($route, || client
- .$method(api!($url))
- .body(&$body))?
- }};
- ($route:expr, $method:ident, $url:expr, $($rest:tt)*) => {{
- let client = request_client!();
-
- request($route, || client
- .$method(&format!(api!($url), $($rest)*)))?
- }};
- ($route:expr, $method:ident, $url:expr) => {{
- let client = request_client!();
-
- request($route, || client
- .$method(api!($url)))?
- }};
-}
-
-#[cfg(feature = "http")]
+#[cfg(feature = "model")]
macro_rules! request_client {
() => {{
use hyper::net::HttpsConnector;
@@ -89,15 +59,6 @@ macro_rules! feature_cache {
}
}
-#[cfg(all(feature = "client", not(feature = "framework")))]
-macro_rules! feature_framework {
- ($enabled:block else $disabled:block) => {
- {
- $disabled
- }
- }
-}
-
macro_rules! enum_number {
($name:ident { $($variant:ident, )* }) => {
impl ::serde::Serialize for $name {
diff --git a/src/lib.rs b/src/lib.rs
index 74f050a..0971092 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -32,8 +32,10 @@
//! ```rust,no_run
//! #[macro_use] extern crate serenity;
//!
-//! use serenity::client::Client;
-//! use serenity::prelude::EventHandler;
+//! # #[cfg(all(feature = "client", feature = "standard_framework"))]
+//! # mod inner {
+//! #
+//! use serenity::client::{Client, EventHandler};
//! use serenity::framework::standard::StandardFramework;
//! use std::env;
//!
@@ -41,10 +43,11 @@
//!
//! impl EventHandler for Handler {}
//!
-//! fn main() {
+//! pub fn main() {
//! // Login with a bot token from the environment
//! let mut client = Client::new(&env::var("DISCORD_TOKEN").expect("token"), Handler)
//! .expect("Error creating client");
+//!
//! client.with_framework(StandardFramework::new()
//! .configure(|c| c.prefix("~")) // set the bot's prefix to "~"
//! .cmd("ping", ping));
@@ -58,6 +61,13 @@
//! command!(ping(_context, message) {
//! let _ = message.reply("Pong!");
//! });
+//! #
+//! # }
+//! #
+//! # #[cfg(all(feature = "client", feature = "standard_framework"))]
+//! # fn main() { inner::main() }
+//! # #[cfg(not(all(feature = "client", feature = "standard_framework")))]
+//! # fn main() {}
//! ```
//!
//! ### Full Examples
@@ -100,10 +110,12 @@
#[macro_use]
extern crate bitflags;
+#[allow(unused_imports)]
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;
+#[allow(unused_imports)]
#[macro_use]
extern crate serde_json;
@@ -140,6 +152,11 @@ extern crate typemap;
#[cfg(feature = "evzht9h3nznqzwl")]
extern crate evzht9h3nznqzwl as websocket;
+#[allow(unused_imports)]
+#[cfg(test)]
+#[macro_use]
+extern crate matches;
+
#[macro_use]
mod internal;
@@ -211,7 +228,7 @@ lazy_static! {
/// CACHE.write().settings_mut().max_messages(10);
/// ```
///
- /// [`CurrentUser`]: model/struct.CurrentUser.html
+ /// [`CurrentUser`]: model/user/struct.CurrentUser.html
/// [`Cache`]: cache/struct.Cache.html
/// [cache module documentation]: cache/index.html
pub static ref CACHE: RwLock<Cache> = RwLock::new(Cache::default());
diff --git a/src/model/application.rs b/src/model/application.rs
index 894e8df..004f40d 100644
--- a/src/model/application.rs
+++ b/src/model/application.rs
@@ -20,7 +20,7 @@ pub struct ApplicationInfo {
/// If a bot is public, anyone may invite it to their [`Guild`]. While a bot
/// is private, only the owner may add it to a guild.
///
- /// [`Guild`]: struct.Guild.html
+ /// [`Guild`]: ../guild/struct.Guild.html
#[serde(default = "default_true")]
pub bot_public: bool,
/// Indicator of whether the bot requires an OAuth2 code grant.
diff --git a/src/model/channel/attachment.rs b/src/model/channel/attachment.rs
index 0530b6f..23d69dd 100644
--- a/src/model/channel/attachment.rs
+++ b/src/model/channel/attachment.rs
@@ -103,8 +103,8 @@ impl Attachment {
/// Returns an [`Error::Hyper`] when there is a problem retrieving the
/// attachment.
///
- /// [`Error::Hyper`]: ../enum.Error.html#variant.Hyper
- /// [`Error::Io`]: ../enum.Error.html#variant.Io
+ /// [`Error::Hyper`]: ../../enum.Error.html#variant.Hyper
+ /// [`Error::Io`]: ../../enum.Error.html#variant.Io
/// [`Message`]: struct.Message.html
pub fn download(&self) -> Result<Vec<u8>> {
let hyper = request_client!();
diff --git a/src/model/channel/channel_category.rs b/src/model/channel/channel_category.rs
index 7a49c7e..47b2281 100644
--- a/src/model/channel/channel_category.rs
+++ b/src/model/channel/channel_category.rs
@@ -49,7 +49,7 @@ impl ChannelCategory {
///
/// **Note**: Requires the [Manage Channel] permission.
///
- /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html
+ /// [Manage Channel]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNELS
#[inline]
pub fn delete_permission(&self, permission_type: PermissionOverwriteType) -> Result<()> {
self.id.delete_permission(permission_type)
diff --git a/src/model/channel/channel_id.rs b/src/model/channel/channel_id.rs
index 48003ef..6715562 100644
--- a/src/model/channel/channel_id.rs
+++ b/src/model/channel/channel_id.rs
@@ -40,7 +40,7 @@ impl ChannelId {
/// let _successful = ChannelId(7).broadcast_typing();
/// ```
///
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
+ /// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
#[inline]
pub fn broadcast_typing(&self) -> Result<()> { http::broadcast_typing(self.0) }
@@ -52,11 +52,11 @@ impl ChannelId {
///
/// Requires the [Manage Channels] permission.
///
- /// [`GuildChannel::create_permission`]: struct.GuildChannel.html#method.create_permission
- /// [`Member`]: struct.Member.html
- /// [`PermissionOverwrite`]: struct.PermissionOverwrite.html
- /// [`Role`]: struct.Role.html
- /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
+ /// [`GuildChannel::create_permission`]: ../channel/struct.GuildChannel.html#method.create_permission
+ /// [`Member`]: ../guild/struct.Member.html
+ /// [`PermissionOverwrite`]: ../channel/struct.PermissionOverwrite.html
+ /// [`Role`]: ../guild/struct.Role.html
+ /// [Manage Channels]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNELS
pub fn create_permission(&self, target: &PermissionOverwrite) -> Result<()> {
let (id, kind) = match target.kind {
PermissionOverwriteType::Member(id) => (id.0, "member"),
@@ -81,10 +81,10 @@ impl ChannelId {
/// Requires the [Add Reactions] permission, _if_ the current user is the
/// first user to perform a react with a certain emoji.
///
- /// [`Emoji`]: struct.Emoji.html
- /// [`Message`]: struct.Message.html
- /// [`Message::react`]: struct.Message.html#method.react
- /// [Add Reactions]: permissions/constant.ADD_REACTIONS.html
+ /// [`Emoji`]: ../guild/struct.Emoji.html
+ /// [`Message`]: ../channel/struct.Message.html
+ /// [`Message::react`]: ../channel/struct.Message.html#method.react
+ /// [Add Reactions]: ../permissions/struct.Permissions.html#associatedconstant.ADD_REACTIONS
#[inline]
pub fn create_reaction<M, R>(&self, message_id: M, reaction_type: R) -> Result<()>
where M: Into<MessageId>, R: Into<ReactionType> {
@@ -110,9 +110,9 @@ impl ChannelId {
/// Requires the [Manage Messages] permission, if the current user is not
/// the author of the message.
///
- /// [`Message`]: struct.Message.html
- /// [`Message::delete`]: struct.Message.html#method.delete
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [`Message`]: ../channel/struct.Message.html
+ /// [`Message::delete`]: ../channel/struct.Message.html#method.delete
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
#[inline]
pub fn delete_message<M: Into<MessageId>>(&self, message_id: M) -> Result<()> {
self._delete_message(message_id.into())
@@ -137,9 +137,9 @@ impl ChannelId {
/// Returns [`ModelError::BulkDeleteAmount`] if an attempt was made to
/// delete either 0 or more than 100 messages.
///
- /// [`Channel::delete_messages`]: enum.Channel.html#method.delete_messages
- /// [`ModelError::BulkDeleteAmount`]: ../enum.ModelError.html#variant.BulkDeleteAmount
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [`Channel::delete_messages`]: ../channel/enum.Channel.html#method.delete_messages
+ /// [`ModelError::BulkDeleteAmount`]: ../error/enum.Error.html#variant.BulkDeleteAmount
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
pub fn delete_messages<T: AsRef<MessageId>, It: IntoIterator<Item=T>>(&self, message_ids: It) -> Result<()> {
let ids = message_ids
.into_iter()
@@ -167,7 +167,7 @@ impl ChannelId {
///
/// **Note**: Requires the [Manage Channel] permission.
///
- /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html
+ /// [Manage Channel]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNELS
pub fn delete_permission(&self, permission_type: PermissionOverwriteType) -> Result<()> {
http::delete_permission(
self.0,
@@ -183,8 +183,8 @@ impl ChannelId {
/// **Note**: Requires the [Manage Messages] permission, _if_ the current
/// user did not perform the reaction.
///
- /// [`Reaction`]: struct.Reaction.html
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [`Reaction`]: ../channel/struct.Reaction.html
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
#[inline]
pub fn delete_reaction<M, R>(&self,
message_id: M,
@@ -230,8 +230,8 @@ impl ChannelId {
/// channel_id.edit(|c| c.name("test").bitrate(64000));
/// ```
///
- /// [`Channel`]: enum.Channel.html
- /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html
+ /// [`Channel`]: ../channel/enum.Channel.html
+ /// [Manage Channel]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNELS
#[cfg(feature = "utils")]
#[inline]
pub fn edit<F: FnOnce(EditChannel) -> EditChannel>(&self, f: F) -> Result<GuildChannel> {
@@ -255,10 +255,10 @@ impl ChannelId {
/// is over the [`the limit`], containing the number of unicode code points
/// over the limit.
///
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
- /// [`EditMessage`]: ../builder/struct.EditMessage.html
- /// [`Message`]: struct.Message.html
- /// [`the limit`]: ../builder/struct.EditMessage.html#method.content
+ /// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
+ /// [`EditMessage`]: ../../builder/struct.EditMessage.html
+ /// [`Message`]: ../channel/struct.Message.html
+ /// [`the limit`]: ../../builder/struct.EditMessage.html#method.content
#[cfg(feature = "utils")]
#[inline]
pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
@@ -303,7 +303,7 @@ impl ChannelId {
/// Gets all of the channel's invites.
///
/// Requires the [Manage Channels] permission.
- /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
+ /// [Manage Channels]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNELS
#[inline]
pub fn invites(&self) -> Result<Vec<RichInvite>> { http::get_channel_invites(self.0) }
@@ -311,7 +311,7 @@ impl ChannelId {
///
/// Requires the [Read Message History] permission.
///
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
#[inline]
pub fn message<M: Into<MessageId>>(&self, message_id: M) -> Result<Message> {
self._message(message_id.into())
@@ -331,8 +331,8 @@ impl ChannelId {
///
/// Requires the [Read Message History] permission.
///
- /// [`Channel::messages`]: enum.Channel.html#method.messages
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [`Channel::messages`]: ../channel/enum.Channel.html#method.messages
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
pub fn messages<F>(&self, f: F) -> Result<Vec<Message>>
where F: FnOnce(GetMessages) -> GetMessages {
let mut map = f(GetMessages::default()).0;
@@ -387,7 +387,7 @@ impl ChannelId {
/// Pins a [`Message`] to the channel.
///
- /// [`Message`]: struct.Message.html
+ /// [`Message`]: ../channel/struct.Message.html
#[inline]
pub fn pin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> {
self._pin(message_id.into())
@@ -399,7 +399,7 @@ impl ChannelId {
/// Gets the list of [`Message`]s which are pinned to the channel.
///
- /// [`Message`]: struct.Message.html
+ /// [`Message`]: ../channel/struct.Message.html
#[inline]
pub fn pins(&self) -> Result<Vec<Message>> { http::get_pins(self.0) }
@@ -410,11 +410,11 @@ impl ChannelId {
///
/// **Note**: Requires the [Read Message History] permission.
///
- /// [`Channel::reaction_users`]: enum.Channel.html#method.reaction_users
- /// [`Emoji`]: struct.Emoji.html
- /// [`Message`]: struct.Message.html
- /// [`User`]: struct.User.html
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [`Channel::reaction_users`]: ../channel/enum.Channel.html#method.reaction_users
+ /// [`Emoji`]: ../guild/struct.Emoji.html
+ /// [`Message`]: ../channel/struct.Message.html
+ /// [`User`]: ../user/struct.User.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
pub fn reaction_users<M, R, U>(&self,
message_id: M,
reaction_type: R,
@@ -458,7 +458,7 @@ impl ChannelId {
/// over the limit.
///
/// [`ChannelId`]: struct.ChannelId.html
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
+ /// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
#[inline]
pub fn say<D: ::std::fmt::Display>(&self, content: D) -> Result<Message> {
self.send_message(|m| m.content(content))
@@ -514,12 +514,12 @@ impl ChannelId {
/// [`HttpError::InvalidRequest(PayloadTooLarge)`][`HttpError::InvalidRequest`]
/// if the file is too large to send.
///
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [`HttpError::InvalidRequest`]: ../http/enum.HttpError.html#variant.InvalidRequest
- /// [`CreateMessage::content`]: ../utils/builder/struct.CreateMessage.html#method.content
+ /// [`ClientError::MessageTooLong`]: ../../client/enum.ClientError.html#variant.MessageTooLong
+ /// [`HttpError::InvalidRequest`]: ../../http/enum.HttpError.html#variant.InvalidRequest
+ /// [`CreateMessage::content`]: ../../builder/struct.CreateMessage.html#method.content
/// [`GuildChannel`]: struct.GuildChannel.html
- /// [Attach Files]: permissions/constant.ATTACH_FILES.html
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
+ /// [Attach Files]: ../permissions/struct.Permissions.html#associatedconstant.ATTACH_FILES
+ /// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
#[cfg(feature = "utils")]
pub fn send_files<'a, F, T, It: IntoIterator<Item=T>>(&self, files: It, f: F) -> Result<Message>
where F: FnOnce(CreateMessage) -> CreateMessage, T: Into<AttachmentType<'a>> {
@@ -556,10 +556,10 @@ impl ChannelId {
/// is over the above limit, containing the number of unicode code points
/// over the limit.
///
- /// [`Channel`]: enum.Channel.html
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
- /// [`CreateMessage`]: ../builder/struct.CreateMessage.html
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
+ /// [`Channel`]: ../channel/enum.Channel.html
+ /// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
+ /// [`CreateMessage`]: ../../builder/struct.CreateMessage.html
+ /// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
#[cfg(feature = "utils")]
pub fn send_message<F>(&self, f: F) -> Result<Message>
where F: FnOnce(CreateMessage) -> CreateMessage {
@@ -584,8 +584,8 @@ impl ChannelId {
///
/// Requires the [Manage Messages] permission.
///
- /// [`Message`]: struct.Message.html
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [`Message`]: ../channel/struct.Message.html
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
#[inline]
pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> {
self._unpin(message_id.into())
@@ -599,7 +599,7 @@ impl ChannelId {
///
/// **Note**: Requires the [Manage Webhooks] permission.
///
- /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
+ /// [Manage Webhooks]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_WEBHOOKS
#[inline]
pub fn webhooks(&self) -> Result<Vec<Webhook>> { http::get_channel_webhooks(self.0) }
}
diff --git a/src/model/channel/embed.rs b/src/model/channel/embed.rs
index 0e6fe8e..6dcccb9 100644
--- a/src/model/channel/embed.rs
+++ b/src/model/channel/embed.rs
@@ -75,7 +75,7 @@ impl Embed {
///
/// This should only be useful in conjunction with [`Webhook::execute`].
///
- /// [`Webhook::execute`]: struct.Webhook.html
+ /// [`Webhook::execute`]: ../webhook/struct.Webhook.html
///
/// # Examples
///
diff --git a/src/model/channel/group.rs b/src/model/channel/group.rs
index 4baa654..ad16fa0 100644
--- a/src/model/channel/group.rs
+++ b/src/model/channel/group.rs
@@ -19,8 +19,8 @@ use std::fmt::Write as FmtWrite;
/// A group channel - potentially including other [`User`]s - separate from a
/// [`Guild`].
///
-/// [`Guild`]: struct.Guild.html
-/// [`User`]: struct.User.html
+/// [`Guild`]: ../guild/struct.Guild.html
+/// [`User`]: ../user/struct.User.html
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Group {
/// The Id of the group channel.
@@ -52,7 +52,7 @@ impl Group {
/// **Note**: Groups have a limit of 10 recipients, including the current
/// user.
///
- /// [`http::add_group_recipient`]: ../http/fn.add_group_recipient.html
+ /// [`http::add_group_recipient`]: ../../http/fn.add_group_recipient.html
#[inline]
pub fn add_recipient<U: Into<UserId>>(&self, user: U) -> Result<()> {
self._add_recipient(user.into())
@@ -79,10 +79,10 @@ impl Group {
/// Requires the [Add Reactions] permission, _if_ the current user is the
/// first user to perform a react with a certain emoji.
///
- /// [`Emoji`]: struct.Emoji.html
+ /// [`Emoji`]: ../guild/struct.Emoji.html
/// [`Message`]: struct.Message.html
/// [`Message::react`]: struct.Message.html#method.react
- /// [Add Reactions]: permissions/constant.ADD_REACTIONS.html
+ /// [Add Reactions]: ../permissions/struct.Permissions.html#associatedconstant.ADD_REACTIONS
#[inline]
pub fn create_reaction<M, R>(&self, message_id: M, reaction_type: R) -> Result<()>
where M: Into<MessageId>, R: Into<ReactionType> {
@@ -104,8 +104,8 @@ impl Group {
/// delete either 0 or more than 100 messages.
///
/// [`Channel::delete_messages`]: enum.Channel.html#method.delete_messages
- /// [`ModelError::BulkDeleteAmount`]: ../enum.ModelError.html#variant.BulkDeleteAmount
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [`ModelError::BulkDeleteAmount`]: ../error/enum.Error.html#variant.BulkDeleteAmount
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
#[inline]
pub fn delete_messages<T: AsRef<MessageId>, It: IntoIterator<Item=T>>(&self, message_ids: It) -> Result<()> {
self.channel_id.delete_messages(message_ids)
@@ -116,7 +116,7 @@ impl Group {
///
/// **Note**: Requires the [Manage Channel] permission.
///
- /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html
+ /// [Manage Channel]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNELS
#[inline]
pub fn delete_permission(&self, permission_type: PermissionOverwriteType) -> Result<()> {
self.channel_id.delete_permission(permission_type)
@@ -128,7 +128,7 @@ impl Group {
/// user did not perform the reaction.
///
/// [`Reaction`]: struct.Reaction.html
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
#[inline]
pub fn delete_reaction<M, R>(&self,
message_id: M,
@@ -155,10 +155,10 @@ impl Group {
/// is over the [`the limit`], containing the number of unicode code points
/// over the limit.
///
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
- /// [`EditMessage`]: ../builder/struct.EditMessage.html
+ /// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
+ /// [`EditMessage`]: ../../builder/struct.EditMessage.html
/// [`Message`]: struct.Message.html
- /// [`the limit`]: ../builder/struct.EditMessage.html#method.content
+ /// [`the limit`]: ../../builder/struct.EditMessage.html#method.content
#[inline]
pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
where F: FnOnce(EditMessage) -> EditMessage, M: Into<MessageId> {
@@ -191,7 +191,7 @@ impl Group {
///
/// Requires the [Read Message History] permission.
///
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
#[inline]
pub fn message<M: Into<MessageId>>(&self, message_id: M) -> Result<Message> {
self.channel_id.message(message_id)
@@ -201,7 +201,7 @@ impl Group {
///
/// Requires the [Read Message History] permission.
///
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
#[inline]
pub fn messages<F>(&self, f: F) -> Result<Vec<Message>>
where F: FnOnce(GetMessages) -> GetMessages {
@@ -243,10 +243,10 @@ impl Group {
/// **Note**: Requires the [Read Message History] permission.
///
/// [`Channel::reaction_users`]: enum.Channel.html#method.reaction_users
- /// [`Emoji`]: struct.Emoji.html
+ /// [`Emoji`]: ../guild/struct.Emoji.html
/// [`Message`]: struct.Message.html
- /// [`User`]: struct.User.html
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [`User`]: ../user/struct.User.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
#[inline]
pub fn reaction_users<M, R, U>(
&self,
@@ -286,8 +286,8 @@ impl Group {
/// is over the above limit, containing the number of unicode code points
/// over the limit.
///
- /// [`ChannelId`]: ../model/id/struct.ChannelId.html
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
+ /// [`ChannelId`]: ../id/struct.ChannelId.html
+ /// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
#[inline]
pub fn say(&self, content: &str) -> Result<Message> { self.channel_id.say(content) }
@@ -305,10 +305,10 @@ impl Group {
/// [`ClientError::MessageTooLong`] will be returned, containing the number
/// of unicode code points over the limit.
///
- /// [`ChannelId::send_files`]: struct.ChannelId.html#method.send_files
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [Attach Files]: permissions/constant.ATTACH_FILES.html
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
+ /// [`ChannelId::send_files`]: ../id/struct.ChannelId.html#method.send_files
+ /// [`ClientError::MessageTooLong`]: ../../client/enum.ClientError.html#variant.MessageTooLong
+ /// [Attach Files]: ../permissions/struct.Permissions.html#associatedconstant.ATTACH_FILES
+ /// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
#[inline]
pub fn send_files<'a, F, T, It: IntoIterator<Item=T>>(&self, files: It, f: F) -> Result<Message>
where F: FnOnce(CreateMessage) -> CreateMessage, T: Into<AttachmentType<'a>> {
@@ -322,8 +322,8 @@ impl Group {
///
/// **Note**: Requires the [Send Messages] permission.
///
- /// [`CreateMessage`]: ../builder/struct.CreateMessage.html
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
+ /// [`CreateMessage`]: ../../builder/struct.CreateMessage.html
+ /// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
#[inline]
pub fn send_message<F: FnOnce(CreateMessage) -> CreateMessage>(&self, f: F) -> Result<Message> {
self.channel_id.send_message(f)
@@ -334,7 +334,7 @@ impl Group {
/// Requires the [Manage Messages] permission.
///
/// [`Message`]: struct.Message.html
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
#[inline]
pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> {
self.channel_id.unpin(message_id)
diff --git a/src/model/channel/guild_channel.rs b/src/model/channel/guild_channel.rs
index 2f54fa1..b1fec05 100644
--- a/src/model/channel/guild_channel.rs
+++ b/src/model/channel/guild_channel.rs
@@ -63,8 +63,8 @@ pub struct GuildChannel {
pub name: String,
/// Permission overwrites for [`Member`]s and for [`Role`]s.
///
- /// [`Member`]: struct.Member.html
- /// [`Role`]: struct.Role.html
+ /// [`Member`]: ../guild/struct.Member.html
+ /// [`Role`]: ../guild/struct.Role.html
pub permission_overwrites: Vec<PermissionOverwrite>,
/// The position of the channel.
///
@@ -102,8 +102,8 @@ impl GuildChannel {
/// Returns a [`ModelError::InvalidPermissions`] if the current user does
/// not have the required permissions.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
pub fn broadcast_typing(&self) -> Result<()> { self.id.broadcast_typing() }
/// Creates an invite leading to the given channel.
@@ -225,16 +225,17 @@ impl GuildChannel {
/// ```
///
/// [`Channel`]: enum.Channel.html
- /// [`Member`]: struct.Member.html
+ /// [`Member`]: ../guild/struct.Member.html
/// [`PermissionOverwrite`]: struct.PermissionOverwrite.html
/// [`PermissionOverwrite::Member`]: struct.PermissionOverwrite.html#variant.Member
/// [`PermissionOverwrite::Role`]: struct.PermissionOverwrite.html#variant.Role
- /// [`Role`]: struct.Role.html
- /// [Attach Files]: permissions/constant.ATTACH_FILES.html
- /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
- /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
- /// [Send TTS Messages]: permissions/constant.SEND_TTS_MESSAGES.html
+ /// [`Role`]: ../guild/struct.Role.html
+ /// [Attach Files]:
+ /// ../permissions/struct.Permissions.html#associatedconstant.ATTACH_FILES
+ /// [Manage Channels]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNELS
+ /// [Manage Webhooks]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_WEBHOOKS
+ /// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
+ /// [Send TTS Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_TTS_MESSAGES
#[inline]
pub fn create_permission(&self, target: &PermissionOverwrite) -> Result<()> {
self.id.create_permission(target)
@@ -269,8 +270,8 @@ impl GuildChannel {
/// delete either 0 or more than 100 messages.
///
/// [`Channel::delete_messages`]: enum.Channel.html#method.delete_messages
- /// [`ModelError::BulkDeleteAmount`]: ../enum.ModelError.html#variant.BulkDeleteAmount
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [`ModelError::BulkDeleteAmount`]: ../error/enum.Error.html#variant.BulkDeleteAmount
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
#[inline]
pub fn delete_messages<T: AsRef<MessageId>, It: IntoIterator<Item=T>>(&self, message_ids: It) -> Result<()> {
self.id.delete_messages(message_ids)
@@ -281,7 +282,7 @@ impl GuildChannel {
///
/// **Note**: Requires the [Manage Channel] permission.
///
- /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html
+ /// [Manage Channel]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNELS
#[inline]
pub fn delete_permission(&self, permission_type: PermissionOverwriteType) -> Result<()> {
self.id.delete_permission(permission_type)
@@ -293,7 +294,7 @@ impl GuildChannel {
/// user did not perform the reaction.
///
/// [`Reaction`]: struct.Reaction.html
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
#[inline]
pub fn delete_reaction<M, R>(&self,
message_id: M,
@@ -359,10 +360,10 @@ impl GuildChannel {
/// is over the [`the limit`], containing the number of unicode code points
/// over the limit.
///
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
- /// [`EditMessage`]: ../builder/struct.EditMessage.html
+ /// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
+ /// [`EditMessage`]: ../../builder/struct.EditMessage.html
/// [`Message`]: struct.Message.html
- /// [`the limit`]: ../builder/struct.EditMessage.html#method.content
+ /// [`the limit`]: ../../builder/struct.EditMessage.html#method.content
#[inline]
pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
where F: FnOnce(EditMessage) -> EditMessage, M: Into<MessageId> {
@@ -379,7 +380,7 @@ impl GuildChannel {
/// Gets all of the channel's invites.
///
/// Requires the [Manage Channels] permission.
- /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
+ /// [Manage Channels]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNELS
#[inline]
pub fn invites(&self) -> Result<Vec<RichInvite>> { self.id.invites() }
@@ -403,7 +404,7 @@ impl GuildChannel {
///
/// Requires the [Read Message History] permission.
///
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
#[inline]
pub fn message<M: Into<MessageId>>(&self, message_id: M) -> Result<Message> {
self.id.message(message_id)
@@ -416,7 +417,7 @@ impl GuildChannel {
/// Requires the [Read Message History] permission.
///
/// [`Channel::messages`]: enum.Channel.html#method.messages
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
#[inline]
pub fn messages<F>(&self, f: F) -> Result<Vec<Message>>
where F: FnOnce(GetMessages) -> GetMessages {
@@ -511,14 +512,14 @@ impl GuildChannel {
/// Returns a [`ModelError::GuildNotFound`] if the channel's guild could
/// not be found in the [`Cache`].
///
- /// [`Cache`]: ../cache/struct.Cache.html
- /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound
- /// [`Guild`]: struct.Guild.html
- /// [`Member`]: struct.Member.html
+ /// [`Cache`]: ../../cache/struct.Cache.html
+ /// [`ModelError::GuildNotFound`]: ../error/enum.Error.html#variant.GuildNotFound
+ /// [`Guild`]: ../guild/struct.Guild.html
+ /// [`Member`]: ../guild/struct.Member.html
/// [`Message`]: struct.Message.html
- /// [`User`]: struct.User.html
- /// [Attach Files]: permissions/constant.ATTACH_FILES.html
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
+ /// [`User`]: ../user/struct.User.html
+ /// [Attach Files]: ../permissions/struct.Permissions.html#associatedconstant.ATTACH_FILES
+ /// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
#[cfg(feature = "cache")]
#[inline]
pub fn permissions_for<U: Into<UserId>>(&self, user_id: U) -> Result<Permissions> {
@@ -532,6 +533,8 @@ impl GuildChannel {
}
/// Pins a [`Message`] to the channel.
+ ///
+ /// [`Message`]: struct.Message.html
#[inline]
pub fn pin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> { self.id.pin(message_id) }
@@ -547,10 +550,10 @@ impl GuildChannel {
/// **Note**: Requires the [Read Message History] permission.
///
/// [`Channel::reaction_users`]: enum.Channel.html#method.reaction_users
- /// [`Emoji`]: struct.Emoji.html
+ /// [`Emoji`]: ../guild/struct.Emoji.html
/// [`Message`]: struct.Message.html
- /// [`User`]: struct.User.html
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [`User`]: ../user/struct.User.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
pub fn reaction_users<M, R, U>(
&self,
message_id: M,
@@ -571,8 +574,8 @@ impl GuildChannel {
/// is over the above limit, containing the number of unicode code points
/// over the limit.
///
- /// [`ChannelId`]: struct.ChannelId.html
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
+ /// [`ChannelId`]: ../id/struct.ChannelId.html
+ /// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
#[inline]
pub fn say(&self, content: &str) -> Result<Message> { self.id.say(content) }
@@ -590,10 +593,10 @@ impl GuildChannel {
/// [`ClientError::MessageTooLong`] will be returned, containing the number
/// of unicode code points over the limit.
///
- /// [`ChannelId::send_files`]: struct.ChannelId.html#method.send_files
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [Attach Files]: permissions/constant.ATTACH_FILES.html
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
+ /// [`ChannelId::send_files`]: ../id/struct.ChannelId.html#method.send_files
+ /// [`ClientError::MessageTooLong`]: ../../client/enum.ClientError.html#variant.MessageTooLong
+ /// [Attach Files]: ../permissions/struct.Permissions.html#associatedconstant.ATTACH_FILES
+ /// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
#[inline]
pub fn send_files<'a, F, T, It: IntoIterator<Item=T>>(&self, files: It, f: F) -> Result<Message>
where F: FnOnce(CreateMessage) -> CreateMessage, T: Into<AttachmentType<'a>> {
@@ -615,10 +618,10 @@ impl GuildChannel {
/// Returns a [`ModelError::InvalidPermissions`] if the current user does
/// not have the required permissions.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
/// [`Message`]: struct.Message.html
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
+ /// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
pub fn send_message<F: FnOnce(CreateMessage) -> CreateMessage>(&self, f: F) -> Result<Message> {
#[cfg(feature = "cache")]
{
@@ -637,7 +640,7 @@ impl GuildChannel {
/// Requires the [Manage Messages] permission.
///
/// [`Message`]: struct.Message.html
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
#[inline]
pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> {
self.id.unpin(message_id)
@@ -647,7 +650,7 @@ impl GuildChannel {
///
/// **Note**: Requires the [Manage Webhooks] permission.
///
- /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
+ /// [Manage Webhooks]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_WEBHOOKS
#[inline]
pub fn webhooks(&self) -> Result<Vec<Webhook>> { self.id.webhooks() }
}
diff --git a/src/model/channel/message.rs b/src/model/channel/message.rs
index 3134725..8b580ff 100644
--- a/src/model/channel/message.rs
+++ b/src/model/channel/message.rs
@@ -52,7 +52,7 @@ pub struct Message {
pub mention_everyone: bool,
/// Array of [`Role`]s' Ids mentioned in the message.
///
- /// [`Role`]: struct.Role.html
+ /// [`Role`]: ../guild/struct.Role.html
pub mention_roles: Vec<RoleId>,
/// Array of users mentioned in the message.
pub mentions: Vec<User>,
@@ -138,9 +138,9 @@ impl Message {
/// [`ModelError::InvalidPermissions`] if the current user does not have
/// the required permissions.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [`ModelError::InvalidUser`]: enum.ModelError.html#variant.InvalidUser
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [`ModelError::InvalidUser`]: ../error/enum.Error.html#variant.InvalidUser
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
pub fn delete(&self) -> Result<()> {
#[cfg(feature = "cache")]
{
@@ -166,9 +166,9 @@ impl Message {
/// [`ModelError::InvalidPermissions`] if the current user does not have
/// the required permissions.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [`Reaction`]: struct.Reaction.html
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
pub fn delete_reactions(&self) -> Result<()> {
#[cfg(feature = "cache")]
{
@@ -210,10 +210,10 @@ impl Message {
/// is over [`the limit`], containing the number of unicode code points
/// over the limit.
///
- /// [`ModelError::InvalidUser`]: enum.ModelError.html#variant.InvalidUser
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
- /// [`EditMessage`]: ../builder/struct.EditMessage.html
- /// [`the limit`]: ../builder/struct.EditMessage.html#method.content
+ /// [`ModelError::InvalidUser`]: ../error/enum.Error.html#variant.InvalidUser
+ /// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
+ /// [`EditMessage`]: ../../builder/struct.EditMessage.html
+ /// [`the limit`]: ../../builder/struct.EditMessage.html#method.content
pub fn edit<F>(&mut self, f: F) -> Result<()>
where F: FnOnce(EditMessage) -> EditMessage {
#[cfg(feature = "cache")]
@@ -312,10 +312,10 @@ impl Message {
///
/// **Note**: Requires the [Read Message History] permission.
///
- /// [`Emoji`]: struct.Emoji.html
+ /// [`Emoji`]: ../guild/struct.Emoji.html
/// [`Message`]: struct.Message.html
- /// [`User`]: struct.User.html
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [`User`]: ../user/struct.User.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
#[inline]
pub fn reaction_users<R, U>(
&self,
@@ -402,8 +402,8 @@ impl Message {
/// [`ModelError::InvalidPermissions`] if the current user does not have
/// the required permissions.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES.html
pub fn pin(&self) -> Result<()> {
#[cfg(feature = "cache")]
{
@@ -427,10 +427,11 @@ impl Message {
/// [`ModelError::InvalidPermissions`] if the current user does not have the
/// required [permissions].
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [`Emoji`]: struct.Emoji.html
- /// [Add Reactions]: permissions/constant.ADD_REACTIONS.html
- /// [permissions]: permissions
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [`Emoji`]: ../guild/struct.Emoji.html
+ /// [Add Reactions]:
+ /// ../permissions/struct.Permissions.html#associatedconstant.ADD_REACTIONS
+ /// [permissions]: ../permissions/index.html
#[inline]
pub fn react<R: Into<ReactionType>>(&self, reaction_type: R) -> Result<()> {
self._react(&reaction_type.into())
@@ -468,9 +469,9 @@ impl Message {
/// is over the above limit, containing the number of unicode code points
/// over the limit.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
+ /// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
pub fn reply(&self, content: &str) -> Result<Message> {
if let Some(length_over) = Message::overflow_length(content) {
return Err(Error::Model(ModelError::MessageTooLong(length_over)));
@@ -499,7 +500,7 @@ impl Message {
/// Checks whether the message mentions passed [`UserId`].
///
- /// [`UserId`]: ../../model/id/struct.UserId.html
+ /// [`UserId`]: ../id/struct.UserId.html
#[inline]
pub fn mentions_user_id<I: Into<UserId>>(&self, id: I) -> bool {
self._mentions_user_id(id.into())
@@ -526,8 +527,8 @@ impl Message {
/// [`ModelError::InvalidPermissions`] if the current user does not have
/// the required permissions.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
pub fn unpin(&self) -> Result<()> {
#[cfg(feature = "cache")]
{
@@ -605,6 +606,12 @@ impl Message {
}
}
+impl AsRef<MessageId> for Message {
+ fn as_ref(&self) -> &MessageId {
+ &self.id
+ }
+}
+
impl From<Message> for MessageId {
/// Gets the Id of a `Message`.
fn from(message: Message) -> MessageId { message.id }
diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs
index 8d586a6..05a117d 100644
--- a/src/model/channel/mod.rs
+++ b/src/model/channel/mod.rs
@@ -41,14 +41,14 @@ pub enum Channel {
Group(Arc<RwLock<Group>>),
/// A [text] or [voice] channel within a [`Guild`].
///
- /// [`Guild`]: struct.Guild.html
+ /// [`Guild`]: ../guild/struct.Guild.html
/// [text]: enum.ChannelType.html#variant.Text
/// [voice]: enum.ChannelType.html#variant.Voice
Guild(Arc<RwLock<GuildChannel>>),
/// A private channel to another [`User`]. No other users may access the
/// channel. For multi-user "private channels", use a group.
///
- /// [`User`]: struct.User.html
+ /// [`User`]: ../user/struct.User.html
Private(Arc<RwLock<PrivateChannel>>),
/// A category of [`GuildChannel`]s
///
@@ -57,15 +57,11 @@ pub enum Channel {
}
impl Channel {
-
- /////////////////////////////////////////////////////////////////////////
- // Adapter for each variant
- /////////////////////////////////////////////////////////////////////////
-
/// Converts from `Channel` to `Option<Arc<RwLock<Group>>>`.
///
/// Converts `self` into an `Option<Arc<RwLock<Group>>>`, consuming `self`,
- /// and discarding a GuildChannel, PrivateChannel, or ChannelCategory, if any.
+ /// and discarding a `GuildChannel`, `PrivateChannel`, or `ChannelCategory`,
+ /// if any.
///
/// # Examples
///
@@ -73,9 +69,13 @@ impl Channel {
///
/// ```rust,no_run
/// # extern crate serenity;
+ /// #
/// # use self::serenity::model::id::ChannelId;
+ /// #
+ /// # #[cfg(feature = "model")]
/// # fn main() {
- /// # let channel = ChannelId(0).get().unwrap();
+ /// # let channel = ChannelId(0).get().unwrap();
+ /// #
/// match channel.group() {
/// Some(group_lock) => {
/// if let Some(ref name) = group_lock.read().name {
@@ -86,10 +86,12 @@ impl Channel {
/// },
/// None => { println!("It's not a group!"); },
/// }
+ /// #
/// # }
+ /// #
+ /// # #[cfg(not(feature = "model"))]
+ /// fn main() {}
/// ```
-
-
pub fn group(self) -> Option<Arc<RwLock<Group>>> {
match self {
Channel::Group(lock) => Some(lock),
@@ -99,8 +101,9 @@ impl Channel {
/// Converts from `Channel` to `Option<Arc<RwLock<GuildChannel>>>`.
///
- /// Converts `self` into an `Option<Arc<RwLock<GuildChannel>>>`, consuming `self`,
- /// and discarding a Group, PrivateChannel, or ChannelCategory, if any.
+ /// Converts `self` into an `Option<Arc<RwLock<GuildChannel>>>`, consuming
+ /// `self`, and discarding a `Group`, `PrivateChannel`, or
+ /// `ChannelCategory`, if any.
///
/// # Examples
///
@@ -108,18 +111,25 @@ impl Channel {
///
/// ```rust,no_run
/// # extern crate serenity;
+ /// #
/// # use self::serenity::model::id::ChannelId;
+ /// #
+ /// # #[cfg(feature = "model")]
/// # fn main() {
- /// let channel = ChannelId(0).get().unwrap();
+ /// # let channel = ChannelId(0).get().unwrap();
+ /// #
/// match channel.guild() {
/// Some(guild_lock) => {
/// println!("It's a guild named {}!", guild_lock.read().name);
/// },
/// None => { println!("It's not a guild!"); },
/// }
+ /// #
/// # }
+ /// #
+ /// # #[cfg(not(feature = "model"))]
+ /// fn main() {}
/// ```
-
pub fn guild(self) -> Option<Arc<RwLock<GuildChannel>>> {
match self {
Channel::Guild(lock) => Some(lock),
@@ -129,8 +139,9 @@ impl Channel {
/// Converts from `Channel` to `Option<Arc<RwLock<PrivateChannel>>>`.
///
- /// Converts `self` into an `Option<Arc<RwLock<PrivateChannel>>>`, consuming `self`,
- /// and discarding a Group, GuildChannel, or ChannelCategory, if any.
+ /// Converts `self` into an `Option<Arc<RwLock<PrivateChannel>>>`, consuming
+ /// `self`, and discarding a `Group`, `GuildChannel`, or `ChannelCategory`,
+ /// if any.
///
/// # Examples
///
@@ -138,9 +149,13 @@ impl Channel {
///
/// ```rust,no_run
/// # extern crate serenity;
+ /// #
/// # use self::serenity::model::id::ChannelId;
+ /// #
+ /// # #[cfg(feature = "model")]
/// # fn main() {
- /// # let channel = ChannelId(0).get().unwrap();
+ /// # let channel = ChannelId(0).get().unwrap();
+ /// #
/// match channel.private() {
/// Some(private_lock) => {
/// let private = private_lock.read();
@@ -150,9 +165,12 @@ impl Channel {
/// },
/// None => { println!("It's not a private channel!"); },
/// }
+ /// #
/// # }
+ /// #
+ /// # #[cfg(not(feature = "model"))]
+ /// fn main() {}
/// ```
-
pub fn private(self) -> Option<Arc<RwLock<PrivateChannel>>> {
match self {
Channel::Private(lock) => Some(lock),
@@ -162,8 +180,9 @@ impl Channel {
/// Converts from `Channel` to `Option<Arc<RwLock<ChannelCategory>>>`.
///
- /// Converts `self` into an `Option<Arc<RwLock<ChannelCategory>>>`, consuming `self`,
- /// and discarding a Group, GuildChannel, or PrivateChannel, if any.
+ /// Converts `self` into an `Option<Arc<RwLock<ChannelCategory>>>`,
+ /// consuming `self`, and discarding a `Group`, `GuildChannel`, or
+ /// `PrivateChannel`, if any.
///
/// # Examples
///
@@ -171,18 +190,25 @@ impl Channel {
///
/// ```rust,no_run
/// # extern crate serenity;
+ /// #
/// # use self::serenity::model::id::ChannelId;
+ /// #
+ /// # #[cfg(feature = "model")]
/// # fn main() {
/// # let channel = ChannelId(0).get().unwrap();
+ /// #
/// match channel.category() {
/// Some(category_lock) => {
/// println!("It's a category named {}!", category_lock.read().name);
/// },
/// None => { println!("It's not a category!"); },
/// }
+ /// #
/// # }
+ /// #
+ /// # #[cfg(not(feature = "model"))]
+ /// fn main() {}
/// ```
-
pub fn category(self) -> Option<Arc<RwLock<ChannelCategory>>> {
match self {
Channel::Category(lock) => Some(lock),
@@ -198,10 +224,10 @@ impl Channel {
/// Requires the [Add Reactions] permission, _if_ the current user is the
/// first user to perform a react with a certain emoji.
///
- /// [`Emoji`]: struct.Emoji.html
+ /// [`Emoji`]: ../guild/struct.Emoji.html
/// [`Message`]: struct.Message.html
/// [`Message::react`]: struct.Message.html#method.react
- /// [Add Reactions]: permissions/constant.ADD_REACTIONS.html
+ /// [Add Reactions]: ../permissions/struct.Permissions.html#associatedconstant.ADD_REACTIONS
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
@@ -245,7 +271,7 @@ impl Channel {
///
/// [`Message`]: struct.Message.html
/// [`Message::delete`]: struct.Message.html#method.delete
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
@@ -259,7 +285,7 @@ impl Channel {
/// user did not perform the reaction.
///
/// [`Reaction`]: struct.Reaction.html
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
@@ -288,10 +314,10 @@ impl Channel {
/// is over the [`the limit`], containing the number of unicode code points
/// over the limit.
///
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
- /// [`EditMessage`]: ../builder/struct.EditMessage.html
+ /// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
+ /// [`EditMessage`]: ../../builder/struct.EditMessage.html
/// [`Message`]: struct.Message.html
- /// [`the limit`]: ../builder/struct.EditMessage.html#method.content
+ /// [`the limit`]: ../../builder/struct.EditMessage.html#method.content
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
@@ -319,7 +345,7 @@ impl Channel {
///
/// Requires the [Read Message History] permission.
///
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
@@ -342,7 +368,7 @@ impl Channel {
/// let _messages = channel.messages(|g| g.after(id).limit(100));
/// ```
///
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
@@ -363,10 +389,10 @@ impl Channel {
///
/// **Note**: Requires the [Read Message History] permission.
///
- /// [`Emoji`]: struct.Emoji.html
+ /// [`Emoji`]: ../guild/struct.Emoji.html
/// [`Message`]: struct.Message.html
- /// [`User`]: struct.User.html
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [`User`]: ../user/struct.User.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
@@ -404,8 +430,8 @@ impl Channel {
/// is over the above limit, containing the number of unicode code points
/// over the limit.
///
- /// [`ChannelId`]: struct.ChannelId.html
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
+ /// [`ChannelId`]: ../id/struct.ChannelId.html
+ /// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
@@ -425,10 +451,10 @@ impl Channel {
/// [`ClientError::MessageTooLong`] will be returned, containing the number
/// of unicode code points over the limit.
///
- /// [`ChannelId::send_files`]: struct.ChannelId.html#method.send_files
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [Attach Files]: permissions/constant.ATTACH_FILES.html
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
+ /// [`ChannelId::send_files`]: ../id/struct.ChannelId.html#method.send_files
+ /// [`ClientError::MessageTooLong`]: ../../client/enum.ClientError.html#variant.MessageTooLong
+ /// [Attach Files]: ../permissions/struct.Permissions.html#associatedconstant.ATTACH_FILES
+ /// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
@@ -453,9 +479,9 @@ impl Channel {
/// over the limit.
///
/// [`Channel`]: enum.Channel.html
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
- /// [`CreateMessage`]: ../builder/struct.CreateMessage.html
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
+ /// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
+ /// [`CreateMessage`]: ../../builder/struct.CreateMessage.html
+ /// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
@@ -469,7 +495,7 @@ impl Channel {
/// Requires the [Manage Messages] permission.
///
/// [`Message`]: struct.Message.html
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
#[cfg(feature = "model")]
#[deprecated(since = "0.4.2", note = "Use the inner channel's method")]
#[inline]
@@ -677,3 +703,93 @@ pub enum PermissionOverwriteType {
/// A role which is having its permission overwrites edited.
Role(RoleId),
}
+
+#[cfg(test)]
+mod test {
+ #[cfg(all(feature = "model", feature = "utils"))]
+ mod model_utils {
+ use model::prelude::*;
+ use parking_lot::RwLock;
+ use std::collections::HashMap;
+ use std::sync::Arc;
+
+ fn group() -> Group {
+ Group {
+ channel_id: ChannelId(1),
+ icon: None,
+ last_message_id: None,
+ last_pin_timestamp: None,
+ name: None,
+ owner_id: UserId(2),
+ recipients: HashMap::new(),
+ }
+ }
+
+ fn guild_channel() -> GuildChannel {
+ GuildChannel {
+ id: ChannelId(1),
+ bitrate: None,
+ category_id: None,
+ guild_id: GuildId(2),
+ kind: ChannelType::Text,
+ last_message_id: None,
+ last_pin_timestamp: None,
+ name: "nsfw-stuff".to_string(),
+ permission_overwrites: vec![],
+ position: 0,
+ topic: None,
+ user_limit: None,
+ nsfw: false,
+ }
+ }
+
+ fn private_channel() -> PrivateChannel {
+ PrivateChannel {
+ id: ChannelId(1),
+ last_message_id: None,
+ last_pin_timestamp: None,
+ kind: ChannelType::Private,
+ recipient: Arc::new(RwLock::new(User {
+ id: UserId(2),
+ avatar: None,
+ bot: false,
+ discriminator: 1,
+ name: "ab".to_string(),
+ })),
+ }
+ }
+
+ #[test]
+ fn nsfw_checks() {
+ let mut channel = guild_channel();
+ assert!(channel.is_nsfw());
+ channel.kind = ChannelType::Voice;
+ assert!(!channel.is_nsfw());
+
+ channel.kind = ChannelType::Text;
+ channel.name = "nsfw-".to_string();
+ assert!(!channel.is_nsfw());
+
+ channel.name = "nsfw".to_string();
+ assert!(channel.is_nsfw());
+ channel.kind = ChannelType::Voice;
+ assert!(!channel.is_nsfw());
+ channel.kind = ChannelType::Text;
+
+ channel.name = "nsf".to_string();
+ channel.nsfw = true;
+ assert!(channel.is_nsfw());
+ channel.nsfw = false;
+ assert!(!channel.is_nsfw());
+
+ let channel = Channel::Guild(Arc::new(RwLock::new(channel)));
+ assert!(!channel.is_nsfw());
+
+ let group = group();
+ assert!(!group.is_nsfw());
+
+ let private_channel = private_channel();
+ assert!(!private_channel.is_nsfw());
+ }
+ }
+}
diff --git a/src/model/channel/private_channel.rs b/src/model/channel/private_channel.rs
index 91a019c..2657d3b 100644
--- a/src/model/channel/private_channel.rs
+++ b/src/model/channel/private_channel.rs
@@ -58,10 +58,10 @@ impl PrivateChannel {
/// Requires the [Add Reactions] permission, _if_ the current user is the
/// first user to perform a react with a certain emoji.
///
- /// [`Emoji`]: struct.Emoji.html
+ /// [`Emoji`]: ../guild/struct.Emoji.html
/// [`Message`]: struct.Message.html
/// [`Message::react`]: struct.Message.html#method.react
- /// [Add Reactions]: permissions/constant.ADD_REACTIONS.html
+ /// [Add Reactions]: ../permissions/struct.Permissions.html#associatedconstant.ADD_REACTIONS
pub fn create_reaction<M, R>(&self, message_id: M, reaction_type: R) -> Result<()>
where M: Into<MessageId>, R: Into<ReactionType> {
self.id.create_reaction(message_id, reaction_type)
@@ -88,8 +88,8 @@ impl PrivateChannel {
/// delete either 0 or more than 100 messages.
///
/// [`Channel::delete_messages`]: enum.Channel.html#method.delete_messages
- /// [`ModelError::BulkDeleteAmount`]: ../enum.ModelError.html#variant.BulkDeleteAmount
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [`ModelError::BulkDeleteAmount`]: ../error/enum.Error.html#variant.BulkDeleteAmount
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
#[inline]
pub fn delete_messages<T: AsRef<MessageId>, It: IntoIterator<Item=T>>(&self, message_ids: It) -> Result<()> {
self.id.delete_messages(message_ids)
@@ -100,7 +100,7 @@ impl PrivateChannel {
///
/// **Note**: Requires the [Manage Channel] permission.
///
- /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html
+ /// [Manage Channel]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNELS
#[inline]
pub fn delete_permission(&self, permission_type: PermissionOverwriteType) -> Result<()> {
self.id.delete_permission(permission_type)
@@ -112,7 +112,7 @@ impl PrivateChannel {
/// user did not perform the reaction.
///
/// [`Reaction`]: struct.Reaction.html
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
#[inline]
pub fn delete_reaction<M, R>(&self,
message_id: M,
@@ -138,10 +138,10 @@ impl PrivateChannel {
/// is over the [`the limit`], containing the number of unicode code points
/// over the limit.
///
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
- /// [`EditMessage`]: ../builder/struct.EditMessage.html
+ /// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
+ /// [`EditMessage`]: ../../builder/struct.EditMessage.html
/// [`Message`]: struct.Message.html
- /// [`the limit`]: ../builder/struct.EditMessage.html#method.content
+ /// [`the limit`]: ../../builder/struct.EditMessage.html#method.content
#[inline]
pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
where F: FnOnce(EditMessage) -> EditMessage, M: Into<MessageId> {
@@ -163,7 +163,7 @@ impl PrivateChannel {
///
/// Requires the [Read Message History] permission.
///
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
#[inline]
pub fn message<M: Into<MessageId>>(&self, message_id: M) -> Result<Message> {
self.id.message(message_id)
@@ -176,7 +176,7 @@ impl PrivateChannel {
/// Requires the [Read Message History] permission.
///
/// [`Channel::messages`]: enum.Channel.html#method.messages
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
#[inline]
pub fn messages<F>(&self, f: F) -> Result<Vec<Message>>
where F: FnOnce(GetMessages) -> GetMessages {
@@ -194,10 +194,10 @@ impl PrivateChannel {
/// **Note**: Requires the [Read Message History] permission.
///
/// [`Channel::reaction_users`]: enum.Channel.html#method.reaction_users
- /// [`Emoji`]: struct.Emoji.html
+ /// [`Emoji`]: ../guild/struct.Emoji.html
/// [`Message`]: struct.Message.html
- /// [`User`]: struct.User.html
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [`User`]: ../user/struct.User.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
#[inline]
pub fn reaction_users<M, R, U>(&self,
message_id: M,
@@ -229,8 +229,8 @@ impl PrivateChannel {
/// is over the above limit, containing the number of unicode code points
/// over the limit.
///
- /// [`ChannelId`]: ../model/id/struct.ChannelId.html
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
+ /// [`ChannelId`]: ../id/struct.ChannelId.html
+ /// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
#[inline]
pub fn say<D: ::std::fmt::Display>(&self, content: D) -> Result<Message> { self.id.say(content) }
@@ -248,10 +248,10 @@ impl PrivateChannel {
/// [`ClientError::MessageTooLong`] will be returned, containing the number
/// of unicode code points over the limit.
///
- /// [`ChannelId::send_files`]: struct.ChannelId.html#method.send_files
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [Attach Files]: permissions/constant.ATTACH_FILES.html
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
+ /// [`ChannelId::send_files`]: ../id/struct.ChannelId.html#method.send_files
+ /// [`ClientError::MessageTooLong`]: ../../client/enum.ClientError.html#variant.MessageTooLong
+ /// [Attach Files]: ../permissions/struct.Permissions.html#associatedconstant.ATTACH_FILES
+ /// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
#[inline]
pub fn send_files<'a, F, T, It: IntoIterator<Item=T>>(&self, files: It, f: F) -> Result<Message>
where F: FnOnce(CreateMessage) -> CreateMessage, T: Into<AttachmentType<'a>> {
@@ -269,8 +269,8 @@ impl PrivateChannel {
/// is over the above limit, containing the number of unicode code points
/// over the limit.
///
- /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong
- /// [`CreateMessage`]: ../builder/struct.CreateMessage.html
+ /// [`ModelError::MessageTooLong`]: ../error/enum.Error.html#variant.MessageTooLong
+ /// [`CreateMessage`]: ../../builder/struct.CreateMessage.html
/// [`Message`]: struct.Message.html
#[inline]
pub fn send_message<F: FnOnce(CreateMessage) -> CreateMessage>(&self, f: F) -> Result<Message> {
@@ -282,7 +282,7 @@ impl PrivateChannel {
/// Requires the [Manage Messages] permission.
///
/// [`Message`]: struct.Message.html
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
#[inline]
pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> {
self.id.unpin(message_id)
diff --git a/src/model/channel/reaction.rs b/src/model/channel/reaction.rs
index 0f65e8b..94b3c32 100644
--- a/src/model/channel/reaction.rs
+++ b/src/model/channel/reaction.rs
@@ -34,7 +34,7 @@ pub struct Reaction {
pub message_id: MessageId,
/// The Id of the [`User`] that sent the reaction.
///
- /// [`User`]: struct.User.html
+ /// [`User`]: ../user/struct.User.html
pub user_id: UserId,
}
@@ -48,7 +48,7 @@ impl Reaction {
///
/// Requires the [Read Message History] permission.
///
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
#[inline]
pub fn channel(&self) -> Result<Channel> {
self.channel_id.get()
@@ -66,9 +66,9 @@ impl Reaction {
/// [`ModelError::InvalidPermissions`] if the current user does not have
/// the required [permissions].
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
- /// [permissions]: permissions
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [Manage Messages]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
+ /// [permissions]: ../permissions/index.html
pub fn delete(&self) -> Result<()> {
let user_id = feature_cache! {
{
@@ -109,7 +109,7 @@ impl Reaction {
/// your own message cache or otherwise having the message available if
/// possible.
///
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
/// [`Message`]: struct.Message.html
#[inline]
pub fn message(&self) -> Result<Message> {
@@ -145,12 +145,12 @@ impl Reaction {
/// Returns a [`ModelError::InvalidPermissions`] if the current user does
/// not have the required [permissions].
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [`Emoji`]: struct.Emoji.html
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [`Emoji`]: ../guild/struct.Emoji.html
/// [`Message`]: struct.Message.html
- /// [`User`]: struct.User.html
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
- /// [permissions]: permissions
+ /// [`User`]: ../user/struct.User.html
+ /// [Read Message History]: ../permissions/struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
+ /// [permissions]: ../permissions/index.html
#[inline]
pub fn users<R, U>(&self,
reaction_type: R,
@@ -185,14 +185,14 @@ pub enum ReactionType {
/// A reaction with a [`Guild`]s custom [`Emoji`], which is unique to the
/// guild.
///
- /// [`Emoji`]: struct.Emoji.html
- /// [`Guild`]: struct.Guild.html
+ /// [`Emoji`]: ../guild/struct.Emoji.html
+ /// [`Guild`]: ../guild/struct.Guild.html
Custom {
/// Whether the emoji is animated.
animated: bool,
/// The Id of the custom [`Emoji`].
///
- /// [`Emoji`]: struct.Emoji.html
+ /// [`Emoji`]: ../guild/struct.Emoji.html
id: EmojiId,
/// The name of the custom emoji. This is primarily used for decoration
/// and distinguishing the emoji client-side.
@@ -431,7 +431,7 @@ impl Display for ReactionType {
/// displayed. Otherwise, if the type is a
/// [unicode][`ReactionType::Unicode`], then the inner unicode is displayed.
///
- /// [`Emoji::fmt`]: struct.Emoji.html#method.fmt
+ /// [`Emoji::fmt`]: ../guild/struct.Emoji.html#method.fmt
/// [`ReactionType::Custom`]: enum.ReactionType.html#variant.Custom
/// [`ReactionType::Unicode`]: enum.ReactionType.html#variant.Unicode
fn fmt(&self, f: &mut Formatter) -> FmtResult {
diff --git a/src/model/error.rs b/src/model/error.rs
index 09aaded..05070a7 100644
--- a/src/model/error.rs
+++ b/src/model/error.rs
@@ -70,10 +70,10 @@ use super::Permissions;
/// # fn main() { }
/// ```
///
-/// [`Error`]: ../enum.Error.html
-/// [`Error::Model`]: ../enum.Error.html#variant.Model
-/// [`GuildId::ban`]: struct.GuildId.html#method.ban
-/// [`model`]: ./index.html
+/// [`Error`]: ../../enum.Error.html
+/// [`Error::Model`]: ../../enum.Error.html#variant.Model
+/// [`GuildId::ban`]: ../id/struct.GuildId.html#method.ban
+/// [`model`]: ../index.html
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Error {
/// When attempting to delete below or above the minimum and maximum allowed
@@ -88,9 +88,9 @@ pub enum Error {
/// An indication that a [guild][`Guild`] could not be found by
/// [Id][`GuildId`] in the [`Cache`].
///
- /// [`Guild`]: ../model/guild/struct.Guild.html
- /// [`GuildId`]: ../model/id/struct.GuildId.html
- /// [`Cache`]: ../cache/struct.Cache.html
+ /// [`Guild`]: ../guild/struct.Guild.html
+ /// [`GuildId`]: ../id/struct.GuildId.html
+ /// [`Cache`]: ../../cache/struct.Cache.html
GuildNotFound,
/// Indicates that there are hierarchy problems restricting an action.
///
@@ -106,23 +106,23 @@ pub enum Error {
/// The provided [`Permission`]s is the set of required permissions
/// required.
///
- /// [`Permission`]: ../model/permissions/struct.Permissions.html
+ /// [`Permission`]: ../permissions/struct.Permissions.html
InvalidPermissions(Permissions),
/// An indicator that the [current user] can not perform an action.
///
- /// [current user]: ../model/user/struct.CurrentUser.html
+ /// [current user]: ../user/struct.CurrentUser.html
InvalidUser,
/// An indicator that an item is missing from the [`Cache`], and the action
/// can not be continued.
///
- /// [`Cache`]: ../cache/struct.Cache.html
+ /// [`Cache`]: ../../cache/struct.Cache.html
ItemMissing,
/// Indicates that a [`Message`]s content was too long and will not
/// successfully send, as the length is over 2000 codepoints, or 4000 bytes.
///
/// The number of bytes larger than the limit is provided.
///
- /// [`Message`]: ../model/channel/struct.Message.html
+ /// [`Message`]: ../channel/struct.Message.html
MessageTooLong(u64),
/// Indicates that the current user is attempting to Direct Message another
/// bot user, which is disallowed by the API.
diff --git a/src/model/event.rs b/src/model/event.rs
index ff4cceb..03905cf 100644
--- a/src/model/event.rs
+++ b/src/model/event.rs
@@ -34,7 +34,7 @@ use std::mem;
/// [`Channel`]: ../channel/enum.Channel.html
/// [`Group`]: ../channel/struct.Group.html
/// [`Guild`]: ../guild/struct.Guild.html
-/// [`PrivateChannel`]: ../struct.PrivateChannel.html
+/// [`PrivateChannel`]: ../channel/struct.PrivateChannel.html
#[derive(Clone, Debug)]
pub struct ChannelCreateEvent {
/// The channel that was created.
@@ -148,8 +148,7 @@ impl CacheUpdate for ChannelDeleteEvent {
cache.categories.remove(&channel_id);
},
- // We ignore these two due to the fact that the delete event for dms/groups
- // will _not_ fire anymore.
+ // We ignore these because the delete event does not fire for these.
Channel::Private(_) | Channel::Group(_) => unreachable!(),
};
@@ -1350,16 +1349,14 @@ pub enum Event {
/// Fires the [`Client::channel_delete`] event.
///
/// [`Channel`]: ../channel/enum.Channel.html
- /// [`Client::channel_delete`]:
- /// ../../client/struct.Client.html#channel_delete
+ /// [`Client::channel_delete`]: ../../client/struct.Client.html#channel_delete
ChannelDelete(ChannelDeleteEvent),
/// The pins for a [`Channel`] have been updated.
///
/// Fires the [`Client::channel_pins_update`] event.
///
/// [`Channel`]: ../channel/enum.Channel.html
- /// [`Client::channel_pins_update`]:
- /// ../../client/struct.Client.html#channel_pins_update
+ /// [`Client::channel_pins_update`]: ../../client/struct.Client.html#channel_pins_update
ChannelPinsUpdate(ChannelPinsUpdateEvent),
/// A [`User`] has been added to a [`Group`].
///
@@ -1422,8 +1419,7 @@ pub enum Event {
///
/// Fires the [`reaction_remove`] event handler.
///
- /// [`reaction_remove`]:
- /// ../../prelude/trait.EventHandler.html#method.reaction_remove
+ /// [`reaction_remove`]: ../../prelude/trait.EventHandler.html#method.reaction_remove
ReactionRemove(ReactionRemoveEvent),
/// A request was issued to remove all [`Reaction`]s from a [`Message`].
///
diff --git a/src/model/gateway.rs b/src/model/gateway.rs
index b73d900..28e3d68 100644
--- a/src/model/gateway.rs
+++ b/src/model/gateway.rs
@@ -131,6 +131,66 @@ impl Game {
}
}
+impl<'a> From<&'a str> for Game {
+ fn from(name: &'a str) -> Self {
+ Game {
+ kind: GameType::Playing,
+ name: name.to_owned(),
+ url: None,
+ }
+ }
+}
+
+impl From<String> for Game {
+ fn from(name: String) -> Self {
+ Game {
+ kind: GameType::Playing,
+ url: None,
+ name,
+ }
+ }
+}
+
+impl<'a> From<(String, GameType)> for Game {
+ fn from((name, kind): (String, GameType)) -> Self {
+ Self {
+ url: None,
+ kind,
+ name,
+ }
+ }
+}
+
+impl<'a> From<(&'a str, &'a str)> for Game {
+ fn from((name, url): (&'a str, &'a str)) -> Self {
+ Self {
+ kind: GameType::Streaming,
+ name: name.to_owned(),
+ url: Some(url.to_owned()),
+ }
+ }
+}
+
+impl From<(String, String)> for Game {
+ fn from((name, url): (String, String)) -> Self {
+ Self {
+ kind: GameType::Streaming,
+ url: Some(url),
+ name,
+ }
+ }
+}
+
+impl From<(String, GameType, String)> for Game {
+ fn from((name, kind, url): (String, GameType, String)) -> Self {
+ Self {
+ url: Some(url),
+ kind,
+ name,
+ }
+ }
+}
+
impl<'de> Deserialize<'de> for Game {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
let mut map = JsonMap::deserialize(deserializer)?;
@@ -201,12 +261,12 @@ pub struct Gateway {
/// Information detailing the current online status of a [`User`].
///
-/// [`User`]: struct.User.html
+/// [`User`]: ../user/struct.User.html
#[derive(Clone, Debug)]
pub struct Presence {
/// The game that a [`User`] is current playing.
///
- /// [`User`]: struct.User.html
+ /// [`User`]: ../user/struct.User.html
pub game: Option<Game>,
/// The date of the last presence update.
pub last_modified: Option<u64>,
@@ -214,7 +274,7 @@ pub struct Presence {
pub nick: Option<String>,
/// The user's online status.
pub status: OnlineStatus,
- /// The Id of the [`User`]. Can be used to calculate the user's creation
+ /// The Id of the [`User`](../user/struct.User.html). Can be used to calculate the user's creation
/// date.
pub user_id: UserId,
/// The associated user instance.
diff --git a/src/model/guild/emoji.rs b/src/model/guild/emoji.rs
index e787e46..51c660f 100644
--- a/src/model/guild/emoji.rs
+++ b/src/model/guild/emoji.rs
@@ -52,7 +52,8 @@ impl Emoji {
///
/// **Note**: Only user accounts may use this method.
///
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ /// [Manage Emojis]:
+ /// ../permissions/struct.Permissions.html#associatedconstant.MANAGE_EMOJIS
///
/// # Examples
///
@@ -91,7 +92,7 @@ impl Emoji {
///
/// **Note**: Only user accounts may use this method.
///
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ /// [Manage Emojis]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_EMOJIS
///
/// # Examples
///
diff --git a/src/model/guild/guild_id.rs b/src/model/guild/guild_id.rs
index 9c3120a..4b2bd21 100644
--- a/src/model/guild/guild_id.rs
+++ b/src/model/guild/guild_id.rs
@@ -43,11 +43,10 @@ impl GuildId {
/// Returns a [`ModelError::DeleteMessageDaysAmount`] if the number of
/// days' worth of messages to delete is over the maximum.
///
- /// [`ModelError::DeleteMessageDaysAmount`]:
- /// enum.ModelError.html#variant.DeleteMessageDaysAmount
- /// [`Guild::ban`]: struct.Guild.html#method.ban
- /// [`User`]: struct.User.html
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ /// [`ModelError::DeleteMessageDaysAmount`]: ../error/enum.Error.html#variant.DeleteMessageDaysAmount
+ /// [`Guild::ban`]: ../guild/struct.Guild.html#method.ban
+ /// [`User`]: ../user/struct.User.html
+ /// [Ban Members]: ../permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
#[inline]
pub fn ban<U, BO>(&self, user: U, ban_options: &BO) -> Result<()>
where U: Into<UserId>, BO: BanOptions {
@@ -72,7 +71,7 @@ impl GuildId {
///
/// Requires the [Ban Members] permission.
///
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ /// [Ban Members]: ../permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
#[inline]
pub fn bans(&self) -> Result<Vec<Ban>> { http::get_bans(self.0) }
@@ -87,7 +86,7 @@ impl GuildId {
/// Gets all of the guild's channels over the REST API.
///
- /// [`Guild`]: struct.Guild.html
+ /// [`Guild`]: ../guild/struct.Guild.html
pub fn channels(&self) -> Result<HashMap<ChannelId, GuildChannel>> {
let mut channels = HashMap::new();
@@ -114,9 +113,9 @@ impl GuildId {
/// let _channel = GuildId(7).create_channel("test", ChannelType::Voice, None);
/// ```
///
- /// [`GuildChannel`]: struct.GuildChannel.html
- /// [`http::create_channel`]: ../http/fn.create_channel.html
- /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
+ /// [`GuildChannel`]: ../channel/struct.GuildChannel.html
+ /// [`http::create_channel`]: ../../http/fn.create_channel.html
+ /// [Manage Channels]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNELS
#[inline]
pub fn create_channel<C>(&self, name: &str, kind: ChannelType, category: C) -> Result<GuildChannel>
where C: Into<Option<ChannelId>> {
@@ -151,10 +150,10 @@ impl GuildId {
/// how to read an image from the filesystem and encode it as base64. Most
/// of the example can be applied similarly for this method.
///
- /// [`EditProfile::avatar`]: ../builder/struct.EditProfile.html#method.avatar
- /// [`Guild::create_emoji`]: struct.Guild.html#method.create_emoji
- /// [`utils::read_image`]: ../utils/fn.read_image.html
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ /// [`EditProfile::avatar`]: ../../builder/struct.EditProfile.html#method.avatar
+ /// [`Guild::create_emoji`]: ../guild/struct.Guild.html#method.create_emoji
+ /// [`utils::read_image`]: ../../utils/fn.read_image.html
+ /// [Manage Emojis]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_EMOJIS
#[inline]
pub fn create_emoji(&self, name: &str, image: &str) -> Result<Emoji> {
let map = json!({
@@ -169,7 +168,7 @@ impl GuildId {
///
/// Requires the [Manage Guild] permission.
///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[inline]
pub fn create_integration<I>(&self, integration_id: I, kind: &str) -> Result<()>
where I: Into<IntegrationId> {
@@ -195,8 +194,8 @@ impl GuildId {
///
/// **Note**: Requires the [Manage Roles] permission.
///
- /// [`Guild::create_role`]: struct.Guild.html#method.create_role
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ /// [`Guild::create_role`]: ../guild/struct.Guild.html#method.create_role
+ /// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[inline]
pub fn create_role<F: FnOnce(EditRole) -> EditRole>(&self, f: F) -> Result<Role> {
let map = utils::vecmap_to_json_map(f(EditRole::default()).0);
@@ -217,7 +216,7 @@ impl GuildId {
///
/// **Note**: Requires the current user to be the owner of the guild.
///
- /// [`Guild::delete`]: struct.Guild.html#method.delete
+ /// [`Guild::delete`]: ../guild/struct.Guild.html#method.delete
#[inline]
pub fn delete(&self) -> Result<PartialGuild> { http::delete_guild(self.0) }
@@ -225,8 +224,8 @@ impl GuildId {
///
/// Requires the [Manage Emojis] permission.
///
- /// [`Emoji`]: struct.Emoji.html
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ /// [`Emoji`]: ../guild/struct.Emoji.html
+ /// [Manage Emojis]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_EMOJIS
#[inline]
pub fn delete_emoji<E: Into<EmojiId>>(&self, emoji_id: E) -> Result<()> {
self._delete_emoji(emoji_id.into())
@@ -240,7 +239,7 @@ impl GuildId {
///
/// Requires the [Manage Guild] permission.
///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[inline]
pub fn delete_integration<I: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
self._delete_integration(integration_id.into())
@@ -257,9 +256,9 @@ impl GuildId {
///
/// Requires the [Manage Roles] permission.
///
- /// [`Role`]: struct.Role.html
- /// [`Role::delete`]: struct.Role.html#method.delete
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ /// [`Role`]: ../guild/struct.Role.html
+ /// [`Role::delete`]: ../guild/struct.Role.html#method.delete
+ /// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[inline]
pub fn delete_role<R: Into<RoleId>>(&self, role_id: R) -> Result<()> {
self._delete_role(role_id.into())
@@ -276,8 +275,8 @@ impl GuildId {
/// **Note**: Requires the current user to have the [Manage Guild]
/// permission.
///
- /// [`Guild::edit`]: struct.Guild.html#method.edit
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [`Guild::edit`]: ../guild/struct.Guild.html#method.edit
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[inline]
pub fn edit<F: FnOnce(EditGuild) -> EditGuild>(&mut self, f: F) -> Result<PartialGuild> {
let map = utils::vecmap_to_json_map(f(EditGuild::default()).0);
@@ -292,9 +291,9 @@ impl GuildId {
///
/// Requires the [Manage Emojis] permission.
///
- /// [`Emoji`]: struct.Emoji.html
- /// [`Emoji::edit`]: struct.Emoji.html#method.edit
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ /// [`Emoji`]: ../guild/struct.Emoji.html
+ /// [`Emoji::edit`]: ../guild/struct.Emoji.html#method.edit
+ /// [Manage Emojis]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_EMOJIS
#[inline]
pub fn edit_emoji<E: Into<EmojiId>>(&self, emoji_id: E, name: &str) -> Result<Emoji> {
self._edit_emoji(emoji_id.into(), name)
@@ -340,7 +339,7 @@ impl GuildId {
///
/// Requires the [Change Nickname] permission.
///
- /// [Change Nickname]: permissions/constant.CHANGE_NICKNAME.html
+ /// [Change Nickname]: ../permissions/struct.Permissions.html#associatedconstant.CHANGE_NICKNAME
#[inline]
pub fn edit_nickname(&self, new_nickname: Option<&str>) -> Result<()> {
http::edit_nickname(self.0, new_nickname)
@@ -360,8 +359,8 @@ impl GuildId {
/// GuildId(7).edit_role(RoleId(8), |r| r.hoist(true));
/// ```
///
- /// [`Role`]: struct.Role.html
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ /// [`Role`]: ../guild/struct.Role.html
+ /// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[inline]
pub fn edit_role<F, R>(&self, role_id: R, f: F) -> Result<Role>
where F: FnOnce(EditRole) -> EditRole, R: Into<RoleId> {
@@ -387,8 +386,8 @@ impl GuildId {
/// GuildId(7).edit_role_position(RoleId(8), 2);
/// ```
///
- /// [`Role`]: struct.Role.html
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ /// [`Role`]: ../guild/struct.Role.html
+ /// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[inline]
pub fn edit_role_position<R>(&self, role_id: R, position: u64) -> Result<Vec<Role>>
where R: Into<RoleId> {
@@ -425,7 +424,7 @@ impl GuildId {
///
/// Requires the [Manage Guild] permission.
///
- /// [Manage Guild]: permissions/struct.MANAGE_GUILD.html
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[inline]
pub fn invites(&self) -> Result<Vec<RichInvite>> { http::get_guild_invites(self.0) }
@@ -433,8 +432,8 @@ impl GuildId {
///
/// Requires the [Kick Members] permission.
///
- /// [`Member`]: struct.Member.html
- /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
+ /// [`Member`]: ../guild/struct.Member.html
+ /// [Kick Members]: ../permissions/struct.Permissions.html#associatedconstant.KICK_MEMBERS
#[inline]
pub fn kick<U: Into<UserId>>(&self, user_id: U) -> Result<()> {
http::kick_member(self.0, user_id.into().0)
@@ -444,16 +443,26 @@ impl GuildId {
#[inline]
pub fn leave(&self) -> Result<()> { http::leave_guild(self.0) }
- /// Gets a user's [`Member`] for the guild by Id.
+ /// Gets a user's [`Member`] for the guild by Id.
+ ///
+ /// If the cache feature is enabled the cache will be checked
+ /// first. If not found it will resort to an http request.
///
- /// [`Guild`]: struct.Guild.html
- /// [`Member`]: struct.Member.html
+ /// [`Guild`]: ../guild/struct.Guild.html
+ /// [`Member`]: ../guild/struct.Member.html
#[inline]
pub fn member<U: Into<UserId>>(&self, user_id: U) -> Result<Member> {
self._member(user_id.into())
}
fn _member(&self, user_id: UserId) -> Result<Member> {
+ #[cfg(feature = "cache")]
+ {
+ if let Some(member) = CACHE.read().member(self.0, user_id) {
+ return Ok(member);
+ }
+ }
+
http::get_member(self.0, user_id.0)
}
@@ -463,7 +472,7 @@ impl GuildId {
/// value is 1000. Optionally pass in `after` to offset the results by a
/// [`User`]'s Id.
///
- /// [`User`]: struct.User.html
+ /// [`User`]: ../user/struct.User.html
#[inline]
pub fn members<U>(&self, limit: Option<u64>, after: Option<U>) -> Result<Vec<Member>>
where U: Into<UserId> {
@@ -478,7 +487,7 @@ impl GuildId {
///
/// Requires the [Move Members] permission.
///
- /// [Move Members]: permissions/constant.MOVE_MEMBERS.html
+ /// [Move Members]: ../permissions/struct.Permissions.html#associatedconstant.MOVE_MEMBERS
#[inline]
pub fn move_member<C, U>(&self, user_id: U, channel_id: C) -> Result<()>
where C: Into<ChannelId>, U: Into<UserId> {
@@ -504,8 +513,8 @@ impl GuildId {
///
/// Requires the [Kick Members] permission.
///
- /// [`Member`]: struct.Member.html
- /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
+ /// [`Member`]: ../guild/struct.Member.html
+ /// [Kick Members]: ../permissions/struct.Permissions.html#associatedconstant.KICK_MEMBERS
pub fn prune_count(&self, days: u16) -> Result<GuildPrune> {
let map = json!({
"days": days,
@@ -546,7 +555,7 @@ impl GuildId {
/// retrieve the total number of shards in use. If you already have the
/// total, consider using [`utils::shard_id`].
///
- /// [`utils::shard_id`]: ../utils/fn.shard_id.html
+ /// [`utils::shard_id`]: ../../utils/fn.shard_id.html
#[cfg(all(feature = "cache", feature = "utils"))]
#[inline]
pub fn shard_id(&self) -> u64 { ::utils::shard_id(self.0, CACHE.read().shard_count) }
@@ -580,7 +589,7 @@ impl GuildId {
///
/// Requires the [Manage Guild] permission.
///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[inline]
pub fn start_integration_sync<I: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
self._start_integration_sync(integration_id.into())
@@ -599,9 +608,9 @@ impl GuildId {
///
/// **Note**: Requires the [Kick Members] permission.
///
- /// [`GuildPrune`]: struct.GuildPrune.html
- /// [`Member`]: struct.Member.html
- /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
+ /// [`GuildPrune`]: ../guild/struct.GuildPrune.html
+ /// [`Member`]: ../guild/struct.Member.html
+ /// [Kick Members]: ../permissions/struct.Permissions.html#associatedconstant.KICK_MEMBERS
#[inline]
pub fn start_prune(&self, days: u16) -> Result<GuildPrune> {
let map = json!({
@@ -615,8 +624,8 @@ impl GuildId {
///
/// Requires the [Ban Members] permission.
///
- /// [`User`]: struct.User.html
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ /// [`User`]: ../user/struct.User.html
+ /// [Ban Members]: ../permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
#[inline]
pub fn unban<U: Into<UserId>>(&self, user_id: U) -> Result<()> {
self._unban(user_id.into())
@@ -630,7 +639,7 @@ impl GuildId {
///
/// **Note**: Requires the [Manage Guild] permission.
///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[inline]
pub fn vanity_url(&self) -> Result<String> {
http::get_guild_vanity_url(self.0)
@@ -640,7 +649,7 @@ impl GuildId {
///
/// **Note**: Requires the [Manage Webhooks] permission.
///
- /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
+ /// [Manage Webhooks]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_WEBHOOKS
#[inline]
pub fn webhooks(&self) -> Result<Vec<Webhook>> { http::get_guild_webhooks(self.0) }
}
diff --git a/src/model/guild/member.rs b/src/model/guild/member.rs
index 8101700..0b31ba5 100644
--- a/src/model/guild/member.rs
+++ b/src/model/guild/member.rs
@@ -67,7 +67,7 @@ pub struct Member {
///
/// Can't be longer than 32 characters.
pub nick: Option<String>,
- /// Vector of Ids of [`Role`]s given to the member.
+ /// Vector of Ids of [`Role`](struct.Role.html)s given to the member.
pub roles: Vec<RoleId>,
/// Attached User struct.
#[serde(deserialize_with = "deserialize_sync_user",
@@ -83,7 +83,7 @@ impl Member {
/// **Note**: Requires the [Manage Roles] permission.
///
/// [`Role`]: struct.Role.html
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ /// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[cfg(feature = "cache")]
#[inline]
pub fn add_role<R: Into<RoleId>>(&mut self, role_id: R) -> Result<()> {
@@ -112,7 +112,7 @@ impl Member {
/// **Note**: Requires the [Manage Roles] permission.
///
/// [`Role`]: struct.Role.html
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ /// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[cfg(feature = "cache")]
pub fn add_roles(&mut self, role_ids: &[RoleId]) -> Result<()> {
self.roles.extend_from_slice(role_ids);
@@ -140,9 +140,8 @@ impl Member {
/// Returns a [`ModelError::GuildNotFound`] if the guild could not be
/// found.
///
- /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound
- ///
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ /// [`ModelError::GuildNotFound`]: ../error/enum.Error.html#variant.GuildNotFound
+ /// [Ban Members]: ../permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
#[cfg(feature = "cache")]
#[inline]
pub fn ban<BO: BanOptions>(&self, ban_options: &BO) -> Result<()> {
@@ -235,8 +234,8 @@ impl Member {
/// See [`EditMember`] for the permission(s) required for separate builder
/// methods, as well as usage of this.
///
- /// [`Guild::edit_member`]: ../model/guild/struct.Guild.html#method.edit_member
- /// [`EditMember`]: ../builder/struct.EditMember.html
+ /// [`Guild::edit_member`]: struct.Guild.html#method.edit_member
+ /// [`EditMember`]: ../../builder/struct.EditMember.html
#[cfg(feature = "cache")]
pub fn edit<F: FnOnce(EditMember) -> EditMember>(&self, f: F) -> Result<()> {
let map = utils::vecmap_to_json_map(f(EditMember::default()).0);
@@ -312,9 +311,9 @@ impl Member {
/// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
/// if the current user does not have permission to perform the kick.
///
- /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
+ /// [`ModelError::GuildNotFound`]: ../error/enum.Error.html#variant.GuildNotFound
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [Kick Members]: ../permissions/struct.Permissions.html#associatedconstant.KICK_MEMBERS
pub fn kick(&self) -> Result<()> {
#[cfg(feature = "cache")]
{
@@ -353,8 +352,8 @@ impl Member {
/// And/or returns [`ModelError::ItemMissing`] if the "default channel" of the guild is not
/// found.
///
- /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound
- /// [`ModelError::ItemMissing`]: enum.ModelError.html#variant.ItemMissing
+ /// [`ModelError::GuildNotFound`]: ../error/enum.Error.html#variant.GuildNotFound
+ /// [`ModelError::ItemMissing`]: ../error/enum.Error.html#variant.ItemMissing
#[cfg(feature = "cache")]
pub fn permissions(&self) -> Result<Permissions> {
let guild = match self.guild_id.find() {
@@ -373,7 +372,7 @@ impl Member {
/// **Note**: Requires the [Manage Roles] permission.
///
/// [`Role`]: struct.Role.html
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ /// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[cfg(feature = "cache")]
#[inline]
pub fn remove_role<R: Into<RoleId>>(&mut self, role_id: R) -> Result<()> {
@@ -401,7 +400,7 @@ impl Member {
/// **Note**: Requires the [Manage Roles] permission.
///
/// [`Role`]: struct.Role.html
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ /// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[cfg(feature = "cache")]
pub fn remove_roles(&mut self, role_ids: &[RoleId]) -> Result<()> {
self.roles.retain(|r| !role_ids.contains(r));
@@ -447,9 +446,9 @@ impl Member {
/// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
/// if the current user does not have permission to perform bans.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [`User`]: struct.User.html
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [`User`]: ../user/struct.User.html
+ /// [Ban Members]: ../permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
#[cfg(feature = "cache")]
pub fn unban(&self) -> Result<()> {
http::remove_ban(self.guild_id.0, self.user.read().id.0)
diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs
index 67b0f0e..b5c6967 100644
--- a/src/model/guild/mod.rs
+++ b/src/model/guild/mod.rs
@@ -101,7 +101,7 @@ pub struct Guild {
/// if the [`member_count`] is greater than the `LARGE_THRESHOLD` set by
/// the library.
///
- /// [`ReadyEvent`]: events/struct.ReadyEvent.html
+ /// [`ReadyEvent`]: ../event/struct.ReadyEvent.html
/// [`member_count`]: #structfield.member_count
#[serde(serialize_with = "serialize_gen_map")]
pub members: HashMap<UserId, Member>,
@@ -109,17 +109,17 @@ pub struct Guild {
/// [`Role`]s or [`User`]s with moderation permissions.
///
/// [`Role`]: struct.Role.html
- /// [`User`]: struct.User.html
+ /// [`User`]: ../user/struct.User.html
pub mfa_level: MfaLevel,
/// The name of the guild.
pub name: String,
/// The Id of the [`User`] who owns the guild.
///
- /// [`User`]: struct.User.html
+ /// [`User`]: ../user/struct.User.html
pub owner_id: UserId,
/// A mapping of [`User`]s' Ids to their current presences.
///
- /// [`User`]: struct.User.html
+ /// [`User`]: ../user/struct.User.html
#[serde(serialize_with = "serialize_gen_map")]
pub presences: HashMap<UserId, Presence>,
/// The region that the voice servers that the guild uses are located in.
@@ -129,10 +129,8 @@ pub struct Guild {
pub roles: HashMap<RoleId, Role>,
/// An identifying hash of the guild's splash icon.
///
- /// If the [`InviteSplash`] feature is enabled, this can be used to generate
+ /// If the [`"InviteSplash"`] feature is enabled, this can be used to generate
/// a URL to a splash image.
- ///
- /// [`InviteSplash`]: enum.Feature.html#variant.InviteSplash
pub splash: Option<String>,
/// The ID of the channel to which system messages are sent.
pub system_channel_id: Option<ChannelId>,
@@ -140,7 +138,7 @@ pub struct Guild {
pub verification_level: VerificationLevel,
/// A mapping of of [`User`]s to their current voice state.
///
- /// [`User`]: struct.User.html
+ /// [`User`]: ../user/struct.User.html
#[serde(serialize_with = "serialize_gen_map")]
pub voice_states: HashMap<UserId, VoiceState>,
}
@@ -224,12 +222,11 @@ impl Guild {
/// Returns a [`ModelError::DeleteMessageDaysAmount`] if the number of
/// days' worth of messages to delete is over the maximum.
///
- /// [`ModelError::DeleteMessageDaysAmount`]:
- /// enum.ModelError.html#variant.DeleteMessageDaysAmount
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [`Guild::ban`]: struct.Guild.html#method.ban
- /// [`User`]: struct.User.html
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ /// [`ModelError::DeleteMessageDaysAmount`]: ../error/enum.Error.html#variant.DeleteMessageDaysAmount
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [`Guild::ban`]: ../guild/struct.Guild.html#method.ban
+ /// [`User`]: ../user/struct.User.html
+ /// [Ban Members]: ../permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
#[inline]
pub fn ban<U: Into<UserId>, BO: BanOptions>(&self, user: U, options: &BO) -> Result<()> {
self._ban(user.into(), options)
@@ -260,8 +257,8 @@ impl Guild {
/// if the current user does not have permission to perform bans.
///
/// [`Ban`]: struct.Ban.html
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [Ban Members]: ../permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
pub fn bans(&self) -> Result<Vec<Ban>> {
#[cfg(feature = "cache")]
{
@@ -314,7 +311,7 @@ impl Guild {
///
/// [`Guild`]: struct.Guild.html
/// [`PartialGuild`]: struct.PartialGuild.html
- /// [`Shard`]: ../gateway/struct.Shard.html
+ /// [`Shard`]: ../../gateway/struct.Shard.html
/// [US West region]: enum.Region.html#variant.UsWest
/// [whitelist]: https://discordapp.com/developers/docs/resources/guild#create-guild
pub fn create(name: &str, region: Region, icon: Option<&str>) -> Result<PartialGuild> {
@@ -346,9 +343,9 @@ impl Guild {
/// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
/// if the current user does not have permission to perform bans.
///
- /// [`Channel`]: struct.Channel.html
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
+ /// [`Channel`]: ../channel/enum.Channel.html
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [Manage Channels]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNELS
pub fn create_channel<C>(&self, name: &str, kind: ChannelType, category: C) -> Result<GuildChannel>
where C: Into<Option<ChannelId>> {
#[cfg(feature = "cache")]
@@ -379,9 +376,9 @@ impl Guild {
/// how to read an image from the filesystem and encode it as base64. Most
/// of the example can be applied similarly for this method.
///
- /// [`EditProfile::avatar`]: ../builder/struct.EditProfile.html#method.avatar
- /// [`utils::read_image`]: ../fn.read_image.html
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ /// [`EditProfile::avatar`]: ../../builder/struct.EditProfile.html#method.avatar
+ /// [`utils::read_image`]: ../../utils/fn.read_image.html
+ /// [Manage Emojis]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_EMOJIS
#[inline]
pub fn create_emoji(&self, name: &str, image: &str) -> Result<Emoji> {
self.id.create_emoji(name, image)
@@ -391,7 +388,7 @@ impl Guild {
///
/// Requires the [Manage Guild] permission.
///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[inline]
pub fn create_integration<I>(&self, integration_id: I, kind: &str) -> Result<()>
where I: Into<IntegrationId> {
@@ -417,9 +414,9 @@ impl Guild {
/// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
/// if the current user does not have permission to perform bans.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [`Role`]: struct.Role.html
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ /// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
pub fn create_role<F>(&self, f: F) -> Result<Role>
where F: FnOnce(EditRole) -> EditRole {
#[cfg(feature = "cache")]
@@ -444,7 +441,7 @@ impl Guild {
/// If the `cache` is enabled, then returns a [`ModelError::InvalidUser`]
/// if the current user is not the guild owner.
///
- /// [`ModelError::InvalidUser`]: enum.ModelError.html#variant.InvalidUser
+ /// [`ModelError::InvalidUser`]: ../error/enum.Error.html#variant.InvalidUser
pub fn delete(&self) -> Result<PartialGuild> {
#[cfg(feature = "cache")]
{
@@ -463,7 +460,7 @@ impl Guild {
/// Requires the [Manage Emojis] permission.
///
/// [`Emoji`]: struct.Emoji.html
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ /// [Manage Emojis]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_EMOJIS
#[inline]
pub fn delete_emoji<E: Into<EmojiId>>(&self, emoji_id: E) -> Result<()> {
self.id.delete_emoji(emoji_id)
@@ -473,7 +470,7 @@ impl Guild {
///
/// Requires the [Manage Guild] permission.
///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[inline]
pub fn delete_integration<I: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
self.id.delete_integration(integration_id)
@@ -488,7 +485,7 @@ impl Guild {
///
/// [`Role`]: struct.Role.html
/// [`Role::delete`]: struct.Role.html#method.delete
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ /// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[inline]
pub fn delete_role<R: Into<RoleId>>(&self, role_id: R) -> Result<()> {
self.id.delete_role(role_id)
@@ -520,8 +517,8 @@ impl Guild {
/// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
/// if the current user does not have permission to perform bans.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
pub fn edit<F>(&mut self, f: F) -> Result<()>
where F: FnOnce(EditGuild) -> EditGuild {
#[cfg(feature = "cache")]
@@ -564,7 +561,7 @@ impl Guild {
///
/// [`Emoji`]: struct.Emoji.html
/// [`Emoji::edit`]: struct.Emoji.html#method.edit
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ /// [Manage Emojis]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_EMOJIS
#[inline]
pub fn edit_emoji<E: Into<EmojiId>>(&self, emoji_id: E, name: &str) -> Result<Emoji> {
self.id.edit_emoji(emoji_id, name)
@@ -601,8 +598,8 @@ impl Guild {
/// if the current user does not have permission to change their own
/// nickname.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [Change Nickname]: permissions/constant.CHANGE_NICKNAME.html
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [Change Nickname]: ../permissions/struct.Permissions.html#associatedconstant.CHANGE_NICKNAME
pub fn edit_nickname(&self, new_nickname: Option<&str>) -> Result<()> {
#[cfg(feature = "cache")]
{
@@ -628,7 +625,7 @@ impl Guild {
/// guild.edit_role(RoleId(7), |r| r.hoist(true));
/// ```
///
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ /// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[inline]
pub fn edit_role<F, R>(&self, role_id: R, f: F) -> Result<Role>
where F: FnOnce(EditRole) -> EditRole, R: Into<RoleId> {
@@ -648,7 +645,7 @@ impl Guild {
/// ```
///
/// [`Role`]: struct.Role.html
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ /// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[inline]
pub fn edit_role_position<R>(&self, role_id: R, position: u64) -> Result<Vec<Role>>
where R: Into<RoleId> {
@@ -755,8 +752,8 @@ impl Guild {
/// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
/// if the current user does not have permission to perform bans.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
pub fn invites(&self) -> Result<Vec<RichInvite>> {
#[cfg(feature = "cache")]
{
@@ -780,7 +777,7 @@ impl Guild {
/// Requires the [Kick Members] permission.
///
/// [`Member`]: struct.Member.html
- /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
+ /// [Kick Members]: ../permissions/struct.Permissions.html#associatedconstant.KICK_MEMBERS
#[inline]
pub fn kick<U: Into<UserId>>(&self, user_id: U) -> Result<()> { self.id.kick(user_id) }
@@ -790,7 +787,7 @@ impl Guild {
/// Gets a user's [`Member`] for the guild by Id.
///
- /// [`Guild`]: struct.Guild.html
+ /// [`Guild`]: ../guild/struct.Guild.html
/// [`Member`]: struct.Member.html
#[inline]
pub fn member<U: Into<UserId>>(&self, user_id: U) -> Result<Member> { self.id.member(user_id) }
@@ -801,7 +798,7 @@ impl Guild {
/// value is 1000. Optionally pass in `after` to offset the results by a
/// [`User`]'s Id.
///
- /// [`User`]: struct.User.html
+ /// [`User`]: ../user/struct.User.html
#[inline]
pub fn members<U>(&self, limit: Option<u64>, after: Option<U>) -> Result<Vec<Member>>
where U: Into<UserId> {
@@ -1174,7 +1171,7 @@ impl Guild {
///
/// Requires the [Move Members] permission.
///
- /// [Move Members]: permissions/constant.MOVE_MEMBERS.html
+ /// [Move Members]: ../permissions/struct.Permissions.html#associatedconstant.MOVE_MEMBERS
#[inline]
pub fn move_member<C, U>(&self, user_id: U, channel_id: C) -> Result<()>
where C: Into<ChannelId>, U: Into<UserId> {
@@ -1194,7 +1191,7 @@ impl Guild {
/// Calculate a [`User`]'s permissions in a given channel in the guild.
///
- /// [`User`]: struct.User.html
+ /// [`User`]: ../user/struct.User.html
#[inline]
pub fn permissions_in<C, U>(&self, channel_id: C, user_id: U) -> Permissions
where C: Into<ChannelId>, U: Into<UserId> {
@@ -1349,10 +1346,10 @@ impl Guild {
/// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
/// if the current user does not have permission to perform bans.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [`GuildPrune`]: struct.GuildPrune.html
/// [`Member`]: struct.Member.html
- /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
+ /// [Kick Members]: ../permissions/struct.Permissions.html#associatedconstant.KICK_MEMBERS
pub fn prune_count(&self, days: u16) -> Result<GuildPrune> {
#[cfg(feature = "cache")]
{
@@ -1385,7 +1382,7 @@ impl Guild {
/// retrieve the total number of shards in use. If you already have the
/// total, consider using [`utils::shard_id`].
///
- /// [`utils::shard_id`]: ../utils/fn.shard_id.html
+ /// [`utils::shard_id`]: ../../utils/fn.shard_id.html
#[cfg(all(feature = "cache", feature = "utils"))]
#[inline]
pub fn shard_id(&self) -> u64 { self.id.shard_id() }
@@ -1425,7 +1422,7 @@ impl Guild {
///
/// Requires the [Manage Guild] permission.
///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[inline]
pub fn start_integration_sync<I: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
self.id.start_integration_sync(integration_id)
@@ -1442,10 +1439,10 @@ impl Guild {
/// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
/// if the current user does not have permission to perform bans.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [`GuildPrune`]: struct.GuildPrune.html
/// [`Member`]: struct.Member.html
- /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
+ /// [Kick Members]: ../permissions/struct.Permissions.html#associatedconstant.KICK_MEMBERS
pub fn start_prune(&self, days: u16) -> Result<GuildPrune> {
#[cfg(feature = "cache")]
{
@@ -1468,9 +1465,9 @@ impl Guild {
/// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
/// if the current user does not have permission to perform bans.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [`User`]: struct.User.html
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [`User`]: ../user/struct.User.html
+ /// [Ban Members]: ../permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
pub fn unban<U: Into<UserId>>(&self, user_id: U) -> Result<()> {
#[cfg(feature = "cache")]
{
@@ -1488,7 +1485,7 @@ impl Guild {
///
/// **Note**: Requires the [Manage Guild] permission.
///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[inline]
pub fn vanity_url(&self) -> Result<String> {
self.id.vanity_url()
@@ -1498,7 +1495,7 @@ impl Guild {
///
/// **Note**: Requires the [Manage Webhooks] permission.
///
- /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
+ /// [Manage Webhooks]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_WEBHOOKS
#[inline]
pub fn webhooks(&self) -> Result<Vec<Webhook>> { self.id.webhooks() }
@@ -2015,3 +2012,109 @@ impl VerificationLevel {
}
}
}
+
+#[cfg(test)]
+mod test {
+ #[cfg(feature = "model")]
+ mod model {
+ use chrono::prelude::*;
+ use model::prelude::*;
+ use std::collections::*;
+ use std::sync::Arc;
+
+ fn gen_user() -> User {
+ User {
+ id: UserId(210),
+ avatar: Some("abc".to_string()),
+ bot: true,
+ discriminator: 1432,
+ name: "test".to_string(),
+ }
+ }
+
+ fn gen_member() -> Member {
+ let dt: DateTime<FixedOffset> = FixedOffset::east(5 * 3600)
+ .ymd(2016, 11, 08)
+ .and_hms(0, 0, 0);
+ let vec1 = Vec::new();
+ let u = Arc::new(RwLock::new(gen_user()));
+
+ Member {
+ deaf: false,
+ guild_id: GuildId(1),
+ joined_at: Some(dt),
+ mute: false,
+ nick: Some("aaaa".to_string()),
+ roles: vec1,
+ user: u,
+ }
+ }
+
+ fn gen() -> Guild {
+ let u = gen_user();
+ let m = gen_member();
+
+ let hm1 = HashMap::new();
+ let hm2 = HashMap::new();
+ let vec1 = Vec::new();
+ let dt: DateTime<FixedOffset> = FixedOffset::east(5 * 3600)
+ .ymd(2016, 11, 08)
+ .and_hms(0, 0, 0);
+ let mut hm3 = HashMap::new();
+ let hm4 = HashMap::new();
+ let hm5 = HashMap::new();
+ let hm6 = HashMap::new();
+
+ hm3.insert(u.id, m);
+
+ let notifications = DefaultMessageNotificationLevel::All;
+
+ Guild {
+ afk_channel_id: Some(ChannelId(0)),
+ afk_timeout: 0,
+ channels: hm1,
+ default_message_notifications: notifications,
+ emojis: hm2,
+ features: vec1,
+ icon: Some("/avatars/210/a_aaa.webp?size=1024".to_string()),
+ id: GuildId(1),
+ joined_at: dt,
+ large: false,
+ member_count: 1,
+ members: hm3,
+ mfa_level: MfaLevel::Elevated,
+ name: "Spaghetti".to_string(),
+ owner_id: UserId(210),
+ presences: hm4,
+ region: "NA".to_string(),
+ roles: hm5,
+ splash: Some("asdf".to_string()),
+ verification_level: VerificationLevel::None,
+ voice_states: hm6,
+ application_id: Some(ApplicationId(0)),
+ explicit_content_filter: ExplicitContentFilter::None,
+ system_channel_id: Some(ChannelId(0)),
+ }
+ }
+
+
+ #[test]
+ fn member_named_username() {
+ let guild = gen();
+ let lhs = guild
+ .member_named("test#1432")
+ .unwrap()
+ .display_name();
+
+ assert_eq!(lhs, gen_member().display_name());
+ }
+
+ #[test]
+ fn member_named_nickname() {
+ let guild = gen();
+ let lhs = guild.member_named("aaaa").unwrap().display_name();
+
+ assert_eq!(lhs, gen_member().display_name());
+ }
+ }
+}
diff --git a/src/model/guild/partial_guild.rs b/src/model/guild/partial_guild.rs
index d324517..7fff7be 100644
--- a/src/model/guild/partial_guild.rs
+++ b/src/model/guild/partial_guild.rs
@@ -55,10 +55,9 @@ impl PartialGuild {
/// Returns a [`ModelError::DeleteMessageDaysAmount`] if the number of
/// days' worth of messages to delete is over the maximum.
///
- /// [`ModelError::DeleteMessageDaysAmount`]:
- /// enum.ModelError.html#variant.DeleteMessageDaysAmount
- /// [`User`]: struct.User.html
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ /// [`ModelError::DeleteMessageDaysAmount`]: ../error/enum.Error.html#variant.DeleteMessageDaysAmount
+ /// [`User`]: ../user/struct.User.html
+ /// [Ban Members]: ../permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
pub fn ban<U: Into<UserId>>(&self, user: U, delete_message_days: u8) -> Result<()> {
if delete_message_days > 7 {
return Err(Error::Model(
@@ -73,7 +72,7 @@ impl PartialGuild {
///
/// Requires the [Ban Members] permission.
///
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ /// [Ban Members]: ../permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
#[inline]
pub fn bans(&self) -> Result<Vec<Ban>> { self.id.bans() }
@@ -99,9 +98,9 @@ impl PartialGuild {
/// guild.create_channel("test", ChannelType::Voice, None);
/// ```
///
- /// [`GuildChannel`]: struct.GuildChannel.html
- /// [`http::create_channel`]: ../http/fn.create_channel.html
- /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
+ /// [`GuildChannel`]: ../channel/struct.GuildChannel.html
+ /// [`http::create_channel`]: ../../http/fn.create_channel.html
+ /// [Manage Channels]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_CHANNELS
#[inline]
pub fn create_channel<C>(&self, name: &str, kind: ChannelType, category: C) -> Result<GuildChannel>
where C: Into<Option<ChannelId>> {
@@ -121,10 +120,10 @@ impl PartialGuild {
/// how to read an image from the filesystem and encode it as base64. Most
/// of the example can be applied similarly for this method.
///
- /// [`EditProfile::avatar`]: ../builder/struct.EditProfile.html#method.avatar
+ /// [`EditProfile::avatar`]: ../../builder/struct.EditProfile.html#method.avatar
/// [`Guild::create_emoji`]: struct.Guild.html#method.create_emoji
- /// [`utils::read_image`]: ../utils/fn.read_image.html
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ /// [`utils::read_image`]: ../../utils/fn.read_image.html
+ /// [Manage Emojis]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_EMOJIS
#[inline]
pub fn create_emoji(&self, name: &str, image: &str) -> Result<Emoji> {
self.id.create_emoji(name, image)
@@ -134,7 +133,7 @@ impl PartialGuild {
///
/// Requires the [Manage Guild] permission.
///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[inline]
pub fn create_integration<I>(&self, integration_id: I, kind: &str) -> Result<()>
where I: Into<IntegrationId> {
@@ -152,9 +151,9 @@ impl PartialGuild {
/// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
/// if the current user does not have permission to perform bans.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [`Guild::create_role`]: struct.Guild.html#method.create_role
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ /// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[inline]
pub fn create_role<F: FnOnce(EditRole) -> EditRole>(&self, f: F) -> Result<Role> {
self.id.create_role(f)
@@ -172,7 +171,7 @@ impl PartialGuild {
/// Requires the [Manage Emojis] permission.
///
/// [`Emoji`]: struct.Emoji.html
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ /// [Manage Emojis]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_EMOJIS
#[inline]
pub fn delete_emoji<E: Into<EmojiId>>(&self, emoji_id: E) -> Result<()> {
self.id.delete_emoji(emoji_id)
@@ -182,7 +181,7 @@ impl PartialGuild {
///
/// Requires the [Manage Guild] permission.
///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[inline]
pub fn delete_integration<I: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
self.id.delete_integration(integration_id)
@@ -197,7 +196,7 @@ impl PartialGuild {
///
/// [`Role`]: struct.Role.html
/// [`Role::delete`]: struct.Role.html#method.delete
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ /// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[inline]
pub fn delete_role<R: Into<RoleId>>(&self, role_id: R) -> Result<()> {
self.id.delete_role(role_id)
@@ -208,7 +207,7 @@ impl PartialGuild {
/// **Note**: Requires the current user to have the [Manage Guild]
/// permission.
///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
pub fn edit<F>(&mut self, f: F) -> Result<()>
where F: FnOnce(EditGuild) -> EditGuild {
match self.id.edit(f) {
@@ -242,7 +241,8 @@ impl PartialGuild {
///
/// [`Emoji`]: struct.Emoji.html
/// [`Emoji::edit`]: struct.Emoji.html#method.edit
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ /// [Manage Emojis]:
+ /// ../permissions/struct.Permissions.html#associatedconstant.MANAGE_EMOJIS
#[inline]
pub fn edit_emoji<E: Into<EmojiId>>(&self, emoji_id: E, name: &str) -> Result<Emoji> {
self.id.edit_emoji(emoji_id, name)
@@ -281,8 +281,8 @@ impl PartialGuild {
/// if the current user does not have permission to change their own
/// nickname.
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [Change Nickname]: permissions/constant.CHANGE_NICKNAME.html
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [Change Nickname]: ../permissions/struct.Permissions.html#associatedconstant.CHANGE_NICKNAME
#[inline]
pub fn edit_nickname(&self, new_nickname: Option<&str>) -> Result<()> {
self.id.edit_nickname(new_nickname)
@@ -299,7 +299,7 @@ impl PartialGuild {
/// Requires the [Kick Members] permission.
///
/// [`Member`]: struct.Member.html
- /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
+ /// [Kick Members]: ../permissions/struct.Permissions.html#associatedconstant.KICK_MEMBERS
#[inline]
pub fn kick<U: Into<UserId>>(&self, user_id: U) -> Result<()> { self.id.kick(user_id) }
@@ -320,7 +320,7 @@ impl PartialGuild {
///
/// Requires the [Manage Guild] permission.
///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[inline]
pub fn invites(&self) -> Result<Vec<RichInvite>> { self.id.invites() }
@@ -340,7 +340,7 @@ impl PartialGuild {
/// value is 1000. Optionally pass in `after` to offset the results by a
/// [`User`]'s Id.
///
- /// [`User`]: struct.User.html
+ /// [`User`]: ../user/struct.User.html
pub fn members<U>(&self, limit: Option<u64>, after: Option<U>) -> Result<Vec<Member>>
where U: Into<UserId> {
self.id.members(limit, after)
@@ -350,7 +350,7 @@ impl PartialGuild {
///
/// Requires the [Move Members] permission.
///
- /// [Move Members]: permissions/constant.MOVE_MEMBERS.html
+ /// [Move Members]: ../permissions/struct.Permissions.html#associatedconstant.MOVE_MEMBERS
#[inline]
pub fn move_member<C, U>(&self, user_id: U, channel_id: C) -> Result<()>
where C: Into<ChannelId>, U: Into<UserId> {
@@ -363,7 +363,7 @@ impl PartialGuild {
/// Requires the [Kick Members] permission.
///
/// [`Member`]: struct.Member.html
- /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
+ /// [Kick Members]: ../permissions/struct.Permissions.html#associatedconstant.KICK_MEMBERS
#[inline]
pub fn prune_count(&self, days: u16) -> Result<GuildPrune> { self.id.prune_count(days) }
@@ -376,7 +376,7 @@ impl PartialGuild {
/// retrieve the total number of shards in use. If you already have the
/// total, consider using [`utils::shard_id`].
///
- /// [`utils::shard_id`]: ../utils/fn.shard_id.html
+ /// [`utils::shard_id`]: ../../utils/fn.shard_id.html
#[cfg(all(feature = "cache", feature = "utils"))]
#[inline]
pub fn shard_id(&self) -> u64 { self.id.shard_id() }
@@ -416,7 +416,7 @@ impl PartialGuild {
///
/// Requires the [Manage Guild] permission.
///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[inline]
pub fn start_integration_sync<I: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
self.id.start_integration_sync(integration_id)
@@ -426,8 +426,8 @@ impl PartialGuild {
///
/// Requires the [Ban Members] permission.
///
- /// [`User`]: struct.User.html
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ /// [`User`]: ../user/struct.User.html
+ /// [Ban Members]: ../permissions/struct.Permissions.html#associatedconstant.BAN_MEMBERS
#[inline]
pub fn unban<U: Into<UserId>>(&self, user_id: U) -> Result<()> { self.id.unban(user_id) }
@@ -435,7 +435,7 @@ impl PartialGuild {
///
/// **Note**: Requires the [Manage Guild] permission.
///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[inline]
pub fn vanity_url(&self) -> Result<String> {
self.id.vanity_url()
@@ -445,7 +445,7 @@ impl PartialGuild {
///
/// **Note**: Requires the [Manage Webhooks] permission.
///
- /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
+ /// [Manage Webhooks]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_WEBHOOKS
#[inline]
pub fn webhooks(&self) -> Result<Vec<Webhook>> { self.id.webhooks() }
diff --git a/src/model/guild/role.rs b/src/model/guild/role.rs
index 53ec478..b66c0a0 100644
--- a/src/model/guild/role.rs
+++ b/src/model/guild/role.rs
@@ -50,7 +50,7 @@ pub struct Role {
///
/// See the [`permissions`] module for more information.
///
- /// [`permissions`]: permissions/index.html
+ /// [`permissions`]: ../permissions/index.html
pub permissions: Permissions,
/// The role's position in the position list. Roles are considered higher in
/// hierarchy if their position is higher.
@@ -65,7 +65,7 @@ impl Role {
///
/// **Note** Requires the [Manage Roles] permission.
///
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ /// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[cfg(feature = "cache")]
#[inline]
pub fn delete(&self) -> Result<()> { http::delete_role(self.find_guild()?.0, self.id.0) }
@@ -87,7 +87,7 @@ impl Role {
/// ```
///
/// [`Role`]: struct.Role.html
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ /// [Manage Roles]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_ROLES
#[cfg(all(feature = "builder", feature = "cache"))]
pub fn edit<F: FnOnce(EditRole) -> EditRole>(&self, f: F) -> Result<Role> {
self.find_guild()
@@ -101,7 +101,7 @@ impl Role {
/// Returns a [`ModelError::GuildNotFound`] if a guild is not in the cache
/// that contains the role.
///
- /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound
+ /// [`ModelError::GuildNotFound`]: ../error/enum.Error.html#variant.GuildNotFound
#[cfg(feature = "cache")]
pub fn find_guild(&self) -> Result<GuildId> {
for guild in CACHE.read().guilds.values() {
diff --git a/src/model/id.rs b/src/model/id.rs
index 046de11..5ec5c75 100644
--- a/src/model/id.rs
+++ b/src/model/id.rs
@@ -106,7 +106,7 @@ pub struct RoleId(pub u64);
#[allow(derive_hash_xor_eq)]
pub struct UserId(pub u64);
-/// An identifier for a [`Webhook`](struct.Webhook.html).
+/// An identifier for a [`Webhook`](../webhook/struct.Webhook.html).
#[derive(Copy, Clone, Default, Debug, Eq, Hash, PartialOrd, Ord, Serialize)]
#[allow(derive_hash_xor_eq)]
pub struct WebhookId(pub u64);
diff --git a/src/model/invite.rs b/src/model/invite.rs
index 2bbd5d9..a6aa756 100644
--- a/src/model/invite.rs
+++ b/src/model/invite.rs
@@ -19,8 +19,8 @@ use {http, utils};
pub struct Invite {
/// The approximate number of [`Member`]s in the related [`Guild`].
///
- /// [`Guild`]: struct.Guild.html
- /// [`Member`]: struct.Member.html
+ /// [`Guild`]: ../guild/struct.Guild.html
+ /// [`Member`]: ../guild/struct.Member.html
pub approximate_member_count: Option<u64>,
/// The approximate number of [`Member`]s with an active session in the
/// related [`Guild`].
@@ -28,14 +28,16 @@ pub struct Invite {
/// An active session is defined as an open, heartbeating WebSocket connection.
/// These include [invisible][`OnlineStatus::Invisible`] members.
///
- /// [`OnlineStatus::Invisible`]: enum.OnlineStatus.html#variant.Invisible
+ /// [`OnlineStatus::Invisible`]: ../user/enum.OnlineStatus.html#variant.Invisible
+ /// [`Guild`]: ../guild/struct.Guild.html
+ /// [`Member`]: ../guild/struct.Member.html
pub approximate_presence_count: Option<u64>,
/// The unique code for the invite.
pub code: String,
/// A representation of the minimal amount of information needed about the
/// [`GuildChannel`] being invited to.
///
- /// [`GuildChannel`]: struct.GuildChannel.html
+ /// [`GuildChannel`]: ../channel/struct.GuildChannel.html
pub channel: InviteChannel,
/// A representation of the minimal amount of information needed about the
/// [`Guild`] being invited to.
@@ -63,11 +65,11 @@ impl Invite {
/// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
/// if the current user does not have the required [permission].
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [`CreateInvite`]: ../builder/struct.CreateInvite.html
- /// [`GuildChannel`]: struct.GuildChannel.html
- /// [Create Invite]: permissions/constant.CREATE_INVITE.html
- /// [permission]: permissions/index.html
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [`CreateInvite`]: ../../builder/struct.CreateInvite.html
+ /// [`GuildChannel`]: ../channel/struct.GuildChannel.html
+ /// [Create Invite]: ../permissions/struct.Permissions.html#associatedconstant.CREATE_INVITE
+ /// [permission]: ../permissions/index.html
pub fn create<C, F>(channel_id: C, f: F) -> Result<RichInvite>
where C: Into<ChannelId>, F: FnOnce(CreateInvite) -> CreateInvite {
Self::_create(channel_id.into(), f)
@@ -98,9 +100,9 @@ impl Invite {
/// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
/// if the current user does not have the required [permission].
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
- /// [permission]: permissions/index.html
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
+ /// [permission]: ../permissions/index.html
pub fn delete(&self) -> Result<Invite> {
#[cfg(feature = "cache")]
{
@@ -190,7 +192,7 @@ impl InviteGuild {
/// retrieve the total number of shards in use. If you already have the
/// total, consider using [`utils::shard_id`].
///
- /// [`utils::shard_id`]: ../utils/fn.shard_id.html
+ /// [`utils::shard_id`]: ../../utils/fn.shard_id.html
#[cfg(all(feature = "cache", feature = "utils"))]
#[inline]
pub fn shard_id(&self) -> u64 { self.id.shard_id() }
@@ -226,7 +228,7 @@ impl InviteGuild {
/// the [`Invite`] struct.
///
/// [`Invite`]: struct.Invite.html
-/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+/// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct RichInvite {
/// A representation of the minimal amount of information needed about the
@@ -281,11 +283,11 @@ impl RichInvite {
/// [`ModelError::InvalidPermissions`] if the current user does not have
/// the required [permission].
///
- /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
+ /// [`ModelError::InvalidPermissions`]: ../error/enum.Error.html#variant.InvalidPermissions
/// [`Invite::delete`]: struct.Invite.html#method.delete
- /// [`http::delete_invite`]: ../http/fn.delete_invite.html
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
- /// [permission]: permissions/index.html
+ /// [`http::delete_invite`]: ../../http/fn.delete_invite.html
+ /// [Manage Guild]: ../permissions/struct.Permissions.html#associatedconstant.MANAGE_GUILD.html
+ /// [permission]: ../permissions/index.html
pub fn delete(&self) -> Result<Invite> {
#[cfg(feature = "cache")]
{
diff --git a/src/model/misc.rs b/src/model/misc.rs
index 3195c55..5c0825a 100644
--- a/src/model/misc.rs
+++ b/src/model/misc.rs
@@ -308,3 +308,87 @@ pub struct Maintenance {
pub start: String,
pub stop: String,
}
+
+#[cfg(test)]
+mod test {
+ use model::prelude::*;
+
+ #[test]
+ fn test_formatters() {
+ assert_eq!(ChannelId(1).to_string(), "1");
+ assert_eq!(EmojiId(2).to_string(), "2");
+ assert_eq!(GuildId(3).to_string(), "3");
+ assert_eq!(RoleId(4).to_string(), "4");
+ assert_eq!(UserId(5).to_string(), "5");
+ }
+
+ #[cfg(feature = "utils")]
+ mod utils {
+ use model::prelude::*;
+ use parking_lot::RwLock;
+ use std::sync::Arc;
+ use utils::Colour;
+
+ #[test]
+ fn test_mention() {
+ let channel = Channel::Guild(Arc::new(RwLock::new(GuildChannel {
+ bitrate: None,
+ category_id: None,
+ guild_id: GuildId(1),
+ kind: ChannelType::Text,
+ id: ChannelId(4),
+ last_message_id: None,
+ last_pin_timestamp: None,
+ name: "a".to_string(),
+ permission_overwrites: vec![],
+ position: 1,
+ topic: None,
+ user_limit: None,
+ nsfw: false,
+ })));
+ let emoji = Emoji {
+ animated: false,
+ id: EmojiId(5),
+ name: "a".to_string(),
+ managed: true,
+ require_colons: true,
+ roles: vec![],
+ };
+ let role = Role {
+ id: RoleId(2),
+ colour: Colour::ROSEWATER,
+ hoist: false,
+ managed: false,
+ mentionable: false,
+ name: "fake role".to_string(),
+ permissions: Permissions::empty(),
+ position: 1,
+ };
+ let user = User {
+ id: UserId(6),
+ avatar: None,
+ bot: false,
+ discriminator: 4132,
+ name: "fake".to_string(),
+ };
+ let member = Member {
+ deaf: false,
+ guild_id: GuildId(2),
+ joined_at: None,
+ mute: false,
+ nick: None,
+ roles: vec![],
+ user: Arc::new(RwLock::new(user.clone())),
+ };
+
+ assert_eq!(ChannelId(1).mention(), "<#1>");
+ assert_eq!(channel.mention(), "<#4>");
+ assert_eq!(emoji.mention(), "<:a:5>");
+ assert_eq!(member.mention(), "<@6>");
+ assert_eq!(role.mention(), "<@&2>");
+ assert_eq!(role.id.mention(), "<@&2>");
+ assert_eq!(user.mention(), "<@6>");
+ assert_eq!(user.id.mention(), "<@6>");
+ }
+ }
+}
diff --git a/src/model/permissions.rs b/src/model/permissions.rs
index 0076b05..784071e 100644
--- a/src/model/permissions.rs
+++ b/src/model/permissions.rs
@@ -35,14 +35,14 @@
//! [`PRESET_GENERAL`]: constant.PRESET_GENERAL.html
//! [`PRESET_TEXT`]: constant.PRESET_TEXT.html
//! [`PRESET_VOICE`]: constant.PRESET_VOICE.html
-//! [Administrator]: constant.ADMINISTRATOR.html
-//! [Ban Members]: constant.BAN_MEMBERS.html
-//! [Kick Members]: constant.KICK_MEMBERS.html
-//! [Manage Channels]: constant.MANAGE_CHANNELS.html
-//! [Manage Guild]: constant.MANAGE_GUILD.html
-//! [Manage Messages]: constant.MANAGE_MESSAGES.html
-//! [Manage Roles]: constant.MANAGE_ROLES.html
-//! [Manage Webhooks]: constant.MANAGE_WEBHOOKS.html
+//! [Administrator]: struct.Permissions.html#associatedconstant.ADMINISTRATOR
+//! [Ban Members]: struct.Permissions.html#associatedconstant.BAN_MEMBERS
+//! [Kick Members]: struct.Permissions.html#associatedconstant.KICK_MEMBERS
+//! [Manage Channels]: struct.Permissions.html#associatedconstant.MANAGE_CHANNELS
+//! [Manage Guild]: struct.Permissions.html#associatedconstant.MANAGE_GUILD
+//! [Manage Messages]: struct.Permissions.html#associatedconstant.MANAGE_MESSAGES
+//! [Manage Roles]: struct.Permissions.html#associatedconstant.MANAGE_ROLES
+//! [Manage Webhooks]: struct.Permissions.html#associatedconstant.MANAGE_WEBHOOKS
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
@@ -77,20 +77,20 @@ use super::utils::U64Visitor;
/// permissions::general().toggle(permissions::SEND_TTS_MESSAGES);
/// ```
///
-/// [Add Reactions]: constant.ADD_REACTIONS.html
-/// [Attach Files]: constant.ATTACH_FILES.html
-/// [Change Nickname]: constant.CHANGE_NICKNAME.html
-/// [Connect]: constant.CONNECT.html
-/// [Create Invite]: constant.CREATE_INVITE.html
-/// [Embed Links]: constant.EMBED_LINKS.html
-/// [Mention Everyone]: constant.MENTION_EVERYONE.html
-/// [Read Message History]: constant.READ_MESSAGE_HISTORY.html
-/// [Read Messages]: constant.READ_MESSAGES.html
-/// [Send Messages]: constant.SEND_MESSAGES.html
-/// [Send TTS Messages]: constant.SEND_TTS_MESSAGES.html
-/// [Speak]: constant.SPEAK.html
-/// [Use External Emojis]: constant.USE_EXTERNAL_EMOJIS.html
-/// [Use VAD]: constant.USE_VAD.html
+/// [Add Reactions]: struct.Permissions.html#associatedconstant.ADD_REACTIONS
+/// [Attach Files]: struct.Permissions.html#associatedconstant.ATTACH_FILES
+/// [Change Nickname]: struct.Permissions.html#associatedconstant.CHANGE_NICKNAME
+/// [Connect]: struct.Permissions.html#associatedconstant.CONNECT
+/// [Create Invite]: struct.Permissions.html#associatedconstant.CREATE_INVITE
+/// [Embed Links]: struct.Permissions.html#associatedconstant.EMBED_LINKS
+/// [Mention Everyone]: struct.Permissions.html#associatedconstant.MENTION_EVERYONE
+/// [Read Message History]: struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
+/// [Read Messages]: struct.Permissions.html#associatedconstant.READ_MESSAGES
+/// [Send Messages]: struct.Permissions.html#associatedconstant.SEND_MESSAGES
+/// [Send TTS Messages]: struct.Permissions.html#associatedconstant.SEND_TTS_MESSAGES
+/// [Speak]: struct.Permissions.html#associatedconstant.SPEAK
+/// [Use External Emojis]: struct.Permissions.html#associatedconstant.USE_EXTERNAL_EMOJIS
+/// [Use VAD]: struct.Permissions.html#associatedconstant.USE_VAD
pub const PRESET_GENERAL: Permissions = Permissions {
bits: 0b0000_0110_0011_0111_1101_1100_0100_0001,
};
@@ -98,7 +98,7 @@ pub const PRESET_GENERAL: Permissions = Permissions {
/// Returns a set of text-only permissions with the original `@everyone`
/// permissions set to true.
///
-/// This includes the text permissions given via [`general`]:
+/// This includes the text permissions that are in [`PRESET_GENERAL`]:
///
/// - [Add Reactions]
/// - [Attach Files]
@@ -112,18 +112,18 @@ pub const PRESET_GENERAL: Permissions = Permissions {
/// - [Send TTS Messages]
/// - [Use External Emojis]
///
-/// [`general`]: fn.general.html
-/// [Add Reactions]: constant.ADD_REACTIONS.html
-/// [Attach Files]: constant.ATTACH_FILES.html
-/// [Change Nickname]: constant.CHANGE_NICKNAME.html
-/// [Create Invite]: constant.CREATE_INVITE.html
-/// [Embed Links]: constant.EMBED_LINKS.html
-/// [Mention Everyone]: constant.MENTION_EVERYONE.html
-/// [Read Message History]: constant.READ_MESSAGE_HISTORY.html
-/// [Read Messages]: constant.READ_MESSAGES.html
-/// [Send Messages]: constant.SEND_MESSAGES.html
-/// [Send TTS Messages]: constant.SEND_TTS_MESSAGES.html
-/// [Use External Emojis]: constant.USE_EXTERNAL_EMOJIS.html
+/// [`PRESET_GENERAL`]: constant.PRESET_GENERAL.html
+/// [Add Reactions]: struct.Permissions.html#associatedconstant.ADD_REACTIONS
+/// [Attach Files]: struct.Permissions.html#associatedconstant.ATTACH_FILES
+/// [Change Nickname]: struct.Permissions.html#associatedconstant.CHANGE_NICKNAME
+/// [Create Invite]: struct.Permissions.html#associatedconstant.CREATE_INVITE
+/// [Embed Links]: struct.Permissions.html#associatedconstant.EMBED_LINKS
+/// [Mention Everyone]: struct.Permissions.html#associatedconstant.MENTION_EVERYONE
+/// [Read Message History]: struct.Permissions.html#associatedconstant.READ_MESSAGE_HISTORY
+/// [Read Messages]: struct.Permissions.html#associatedconstant.READ_MESSAGES
+/// [Send Messages]: struct.Permissions.html#associatedconstant.SEND_MESSAGES
+/// [Send TTS Messages]: struct.Permissions.html#associatedconstant.SEND_TTS_MESSAGES
+/// [Use External Emojis]: struct.Permissions.html#associatedconstant.USE_EXTERNAL_EMOJIS
pub const PRESET_TEXT: Permissions = Permissions {
bits: 0b0000_0000_0000_0111_1111_1100_0100_0000,
};
@@ -131,16 +131,16 @@ pub const PRESET_TEXT: Permissions = Permissions {
/// Returns a set of voice-only permissions with the original `@everyone`
/// permissions set to true.
///
-/// This includes the voice permissions given via [`general`]:
+/// This includes the voice permissions that are in [`PRESET_GENERAL`]:
///
/// - [Connect]
/// - [Speak]
/// - [Use VAD]
///
-/// [`general`]: fn.general.html
-/// [Connect]: constant.CONNECT.html
-/// [Speak]: constant.SPEAK.html
-/// [Use VAD]: constant.USE_VAD.html
+/// [`PRESET_GENERAL`]: constant.PRESET_GENERAL.html
+/// [Connect]: struct.Permissions.html#associatedconstant.CONNECT
+/// [Speak]: struct.Permissions.html#associatedconstant.SPEAK
+/// [Use VAD]: struct.Permissions.html#associatedconstant.USE_VAD
pub const PRESET_VOICE: Permissions = Permissions {
bits: 0b0000_0011_1111_0000_0000_0000_0000_0000,
};
@@ -149,11 +149,11 @@ pub const PRESET_VOICE: Permissions = Permissions {
/// [`PermissionOverwrite`]s, roles globally in a [`Guild`], and to
/// [`GuildChannel`]s.
///
-/// [`Guild`]: ../struct.Guild.html
-/// [`GuildChannel`]: ../struct.GuildChannel.html
-/// [`PermissionOverwrite`]: ../struct.PermissionOverwrite.html
-/// [`Role`]: ../struct.Role.html
-/// [`User`]: ../struct.User.html
+/// [`Guild`]: ../guild/struct.Guild.html
+/// [`GuildChannel`]: ../channel/struct.GuildChannel.html
+/// [`PermissionOverwrite`]: ../channel/struct.PermissionOverwrite.html
+/// [`Role`]: ../guild/struct.Role.html
+/// [`User`]: ../user/struct.User.html
///
#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
pub struct Permissions {
@@ -164,38 +164,40 @@ __impl_bitflags! {
Permissions: u64 {
/// Allows for the creation of [`RichInvite`]s.
///
- /// [`RichInvite`]: ../struct.RichInvite.html
+ /// [`RichInvite`]: ../invite/struct.RichInvite.html
CREATE_INVITE = 0b0000_0000_0000_0000_0000_0000_0000_0001;
/// Allows for the kicking of guild [member]s.
///
- /// [member]: ../struct.Member.html
+ /// [member]: ../guild/struct.Member.html
KICK_MEMBERS = 0b0000_0000_0000_0000_0000_0000_0000_0010;
/// Allows the banning of guild [member]s.
///
- /// [member]: ../struct.Member.html
+ /// [member]: ../guild/struct.Member.html
BAN_MEMBERS = 0b0000_0000_0000_0000_0000_0000_0000_0100;
/// Allows all permissions, bypassing channel [permission overwrite]s.
///
- /// [permission overwrite]: ../struct.PermissionOverwrite.html
+ /// [permission overwrite]: ../channel/struct.PermissionOverwrite.html
ADMINISTRATOR = 0b0000_0000_0000_0000_0000_0000_0000_1000;
/// Allows management and editing of guild [channel]s.
///
- /// [channel]: ../struct.GuildChannel.html
+ /// [channel]: ../channel/struct.GuildChannel.html
MANAGE_CHANNELS = 0b0000_0000_0000_0000_0000_0000_0001_0000;
/// Allows management and editing of the [guild].
///
- /// [guild]: ../struct.Guild.html
+ /// [guild]: ../guild/struct.Guild.html
MANAGE_GUILD = 0b0000_0000_0000_0000_0000_0000_0010_0000;
/// [`Member`]s with this permission can add new [`Reaction`]s to a
/// [`Message`]. Members can still react using reactions already added
/// to messages without this permission.
///
- /// [`Member`]: ../struct.Member.html
- /// [`Message`]: ../struct.Message.html
- /// [`Reaction`]: ../struct.Reaction.html
+ /// [`Member`]: ../guild/struct.Member.html
+ /// [`Message`]: ../channel/struct.Message.html
+ /// [`Reaction`]: ../channel/struct.Reaction.html
ADD_REACTIONS = 0b0000_0000_0000_0000_0000_0000_0100_0000;
- // Allows viewing a guild's audit logs.
+ /// Allows viewing a guild's audit logs.
VIEW_AUDIT_LOG = 0b0000_0000_0000_0000_0000_0000_1000_0000;
+ /// Allows the use of priority speaking in voice channels.
+ PRIORITY_SPEAKER = 0b0000_0000_0000_0000_0000_0001_0000_0000;
/// Allows reading messages in a guild channel. If a user does not have
/// this permission, then they will not be able to see the channel.
READ_MESSAGES = 0b0000_0000_0000_0000_0000_0100_0000_0000;
@@ -241,8 +243,8 @@ __impl_bitflags! {
///
/// If this is disabled, then [`Member`]s must use push-to-talk.
///
- /// [`Member`]: ../struct.Member.html
- /// [voice]: ../enum.ChannelType.html#variant.Voice
+ /// [`Member`]: ../guild/struct.Member.html
+ /// [voice]: ../channel/enum.ChannelType.html#variant.Voice
USE_VAD = 0b0000_0010_0000_0000_0000_0000_0000_0000;
/// Allows members to change their own nickname in the guild.
CHANGE_NICKNAME = 0b0000_0100_0000_0000_0000_0000_0000_0000;
@@ -255,7 +257,7 @@ __impl_bitflags! {
/// Allows management of emojis created without the use of an
/// [`Integration`].
///
- /// [`Integration`]: ../struct.Integration.html
+ /// [`Integration`]: ../guild/struct.Integration.html
MANAGE_EMOJIS = 0b0100_0000_0000_0000_0000_0000_0000_0000;
}
}
@@ -265,175 +267,181 @@ impl Permissions {
/// Shorthand for checking that the set of permissions contains the
/// [Add Reactions] permission.
///
- /// [Add Reactions]: constant.ADD_REACTIONS.html
+ /// [Add Reactions]: #associatedconstant.ADD_REACTIONS
pub fn add_reactions(&self) -> bool { self.contains(Self::ADD_REACTIONS) }
/// Shorthand for checking that the set of permissions contains the
/// [Administrator] permission.
///
- /// [Administrator]: constant.ADMINISTRATOR.html
+ /// [Administrator]: #associatedconstant.ADMINISTRATOR
pub fn administrator(&self) -> bool { self.contains(Self::ADMINISTRATOR) }
/// Shorthand for checking that the set of permissions contains the
/// [Attach Files] permission.
///
- /// [Attach Files]: constant.ATTACH_FILES.html
+ /// [Attach Files]: #associatedconstant.ATTACH_FILES
pub fn attach_files(&self) -> bool { self.contains(Self::ATTACH_FILES) }
/// Shorthand for checking that the set of permissions contains the
/// [Ban Members] permission.
///
- /// [Ban Members]: constant.BAN_MEMBERS.html
+ /// [Ban Members]: #associatedconstant.BAN_MEMBERS
pub fn ban_members(&self) -> bool { self.contains(Self::BAN_MEMBERS) }
/// Shorthand for checking that the set of permissions contains the
/// [Change Nickname] permission.
///
- /// [Change Nickname]: constant.CHANGE_NICKNAME.html
+ /// [Change Nickname]: #associatedconstant.CHANGE_NICKNAME
pub fn change_nickname(&self) -> bool { self.contains(Self::CHANGE_NICKNAME) }
/// Shorthand for checking that the set of permissions contains the
/// [Connect] permission.
///
- /// [Connect]: constant.CONNECT.html
+ /// [Connect]: #associatedconstant.CONNECT
pub fn connect(&self) -> bool { self.contains(Self::CONNECT) }
/// Shorthand for checking that the set of permissions contains the
/// [View Audit Log] permission.
///
- /// [View Audit Log]: constant.VIEW_AUDIT_LOG.html
+ /// [View Audit Log]: #associatedconstant.VIEW_AUDIT_LOG
pub fn view_audit_log(&self) -> bool { self.contains(Self::VIEW_AUDIT_LOG) }
+ /// Shorthand for checking that the set of permission contains the
+ /// [Priority Speaker] permission.
+ ///
+ /// [Priority Speaker]: #associatedconstant.PRIORITY_SPEAKER
+ pub fn priority_speaker(&self) -> bool { self.contains(Self::PRIORITY_SPEAKER) }
+
/// Shorthand for checking that the set of permissions contains the
/// [Create Invite] permission.
///
- /// [Create Invite]: constant.CREATE_INVITE.html
+ /// [Create Invite]: #associatedconstant.CREATE_INVITE
pub fn create_invite(&self) -> bool { self.contains(Self::CREATE_INVITE) }
/// Shorthand for checking that the set of permissions contains the
/// [Deafen Members] permission.
///
- /// [Deafen Members]: constant.DEAFEN_MEMBERS.html
+ /// [Deafen Members]: #associatedconstant.DEAFEN_MEMBERS
pub fn deafen_members(&self) -> bool { self.contains(Self::DEAFEN_MEMBERS) }
/// Shorthand for checking that the set of permissions contains the
/// [Embed Links] permission.
///
- /// [Embed Links]: constant.EMBED_LINKS.html
+ /// [Embed Links]: #associatedconstant.EMBED_LINKS
pub fn embed_links(&self) -> bool { self.contains(Self::EMBED_LINKS) }
/// Shorthand for checking that the set of permissions contains the
/// [Use External Emojis] permission.
///
- /// [Use External Emojis]: constant.USE_EXTERNAL_EMOJIS.html
+ /// [Use External Emojis]: #associatedconstant.USE_EXTERNAL_EMOJIS
pub fn external_emojis(&self) -> bool { self.contains(Self::USE_EXTERNAL_EMOJIS) }
/// Shorthand for checking that the set of permissions contains the
/// [Kick Members] permission.
///
- /// [Kick Members]: constant.KICK_MEMBERS.html
+ /// [Kick Members]: #associatedconstant.KICK_MEMBERS
pub fn kick_members(&self) -> bool { self.contains(Self::KICK_MEMBERS) }
/// Shorthand for checking that the set of permissions contains the
/// [Manage Channels] permission.
///
- /// [Manage Channels]: constant.MANAGE_CHANNELS.html
+ /// [Manage Channels]: #associatedconstant.MANAGE_CHANNELS
pub fn manage_channels(&self) -> bool { self.contains(Self::MANAGE_CHANNELS) }
/// Shorthand for checking that the set of permissions contains the
/// [Manage Emojis] permission.
///
- /// [Manage Emojis]: constant.MANAGE_EMOJIS.html
+ /// [Manage Emojis]: #associatedconstant.MANAGE_EMOJIS
pub fn manage_emojis(&self) -> bool { self.contains(Self::MANAGE_EMOJIS) }
/// Shorthand for checking that the set of permissions contains the
/// [Manage Guild] permission.
///
- /// [Manage Guild]: constant.MANAGE_GUILD.html
+ /// [Manage Guild]: #associatedconstant.MANAGE_GUILD
pub fn manage_guild(&self) -> bool { self.contains(Self::MANAGE_GUILD) }
/// Shorthand for checking that the set of permissions contains the
/// [Manage Messages] permission.
///
- /// [Manage Messages]: constant.MANAGE_MESSAGES.html
+ /// [Manage Messages]: #associatedconstant.MANAGE_MESSAGES
pub fn manage_messages(&self) -> bool { self.contains(Self::MANAGE_MESSAGES) }
/// Shorthand for checking that the set of permissions contains the
/// [Manage Nicknames] permission.
///
- /// [Manage Nicknames]: constant.MANAGE_NICKNAMES.html
+ /// [Manage Nicknames]: #associatedconstant.MANAGE_NICKNAMES
pub fn manage_nicknames(&self) -> bool { self.contains(Self::MANAGE_NICKNAMES) }
/// Shorthand for checking that the set of permissions contains the
/// [Manage Roles] permission.
///
- /// [Manage Roles]: constant.MANAGE_ROLES.html
+ /// [Manage Roles]: #associatedconstant.MANAGE_ROLES
pub fn manage_roles(&self) -> bool { self.contains(Self::MANAGE_ROLES) }
/// Shorthand for checking that the set of permissions contains the
/// [Manage Webhooks] permission.
///
- /// [Manage Webhooks]: constant.MANAGE_WEBHOOKS.html
+ /// [Manage Webhooks]: #associatedconstant.MANAGE_WEBHOOKS
pub fn manage_webhooks(&self) -> bool { self.contains(Self::MANAGE_WEBHOOKS) }
/// Shorthand for checking that the set of permissions contains the
/// [Mention Everyone] permission.
///
- /// [Mention Everyone]: constant.MENTION_EVERYONE.html
+ /// [Mention Everyone]: #associatedconstant.MENTION_EVERYONE
pub fn mention_everyone(&self) -> bool { self.contains(Self::MENTION_EVERYONE) }
/// Shorthand for checking that the set of permissions contains the
/// [Move Members] permission.
///
- /// [Move Members]: constant.MOVE_MEMBERS.html
+ /// [Move Members]: #associatedconstant.MOVE_MEMBERS
pub fn move_members(&self) -> bool { self.contains(Self::MOVE_MEMBERS) }
/// Shorthand for checking that the set of permissions contains the
/// [Mute Members] permission.
///
- /// [Mute Members]: constant.MUTE_MEMBERS.html
+ /// [Mute Members]: #associatedconstant.MUTE_MEMBERS
pub fn mute_members(&self) -> bool { self.contains(Self::MUTE_MEMBERS) }
/// Shorthand for checking that the set of permissions contains the
/// [Read Message History] permission.
///
- /// [Read Message History]: constant.READ_MESSAGE_HISTORY.html
+ /// [Read Message History]: #associatedconstant.READ_MESSAGE_HISTORY
pub fn read_message_history(&self) -> bool { self.contains(Self::READ_MESSAGE_HISTORY) }
/// Shorthand for checking that the set of permissions contains the
/// [Read Messages] permission.
///
- /// [Read Messages]: constant.READ_MESSAGES.html
+ /// [Read Messages]: #associatedconstant.READ_MESSAGES
pub fn read_messages(&self) -> bool { self.contains(Self::READ_MESSAGES) }
/// Shorthand for checking that the set of permissions contains the
/// [Send Messages] permission.
///
- /// [Send Messages]: constant.SEND_MESSAGES.html
+ /// [Send Messages]: #associatedconstant.SEND_MESSAGES
pub fn send_messages(&self) -> bool { self.contains(Self::SEND_MESSAGES) }
/// Shorthand for checking that the set of permissions contains the
/// [Send TTS Messages] permission.
///
- /// [Send TTS Messages]: constant.SEND_TTS_MESSAGES.html
+ /// [Send TTS Messages]: #associatedconstant.SEND_TTS_MESSAGES
pub fn send_tts_messages(&self) -> bool { self.contains(Self::SEND_TTS_MESSAGES) }
/// Shorthand for checking that the set of permissions contains the
/// [Speak] permission.
///
- /// [Speak]: constant.SPEAK.html
+ /// [Speak]: #associatedconstant.SPEAK
pub fn speak(&self) -> bool { self.contains(Self::SPEAK) }
/// Shorthand for checking that the set of permissions contains the
/// [Use External Emojis] permission.
///
- /// [Use External Emojis]: constant.USE_EXTERNAL_EMOJIS.html
+ /// [Use External Emojis]: #associatedconstant.USE_EXTERNAL_EMOJIS
pub fn use_external_emojis(&self) -> bool { self.contains(Self::USE_EXTERNAL_EMOJIS) }
/// Shorthand for checking that the set of permissions contains the
/// [Use VAD] permission.
///
- /// [Use VAD]: constant.USE_VAD.html
+ /// [Use VAD]: #associatedconstant.USE_VAD
pub fn use_vad(&self) -> bool { self.contains(Self::USE_VAD) }
}
diff --git a/src/model/user.rs b/src/model/user.rs
index a378be5..019bec9 100644
--- a/src/model/user.rs
+++ b/src/model/user.rs
@@ -75,7 +75,7 @@ impl CurrentUser {
///
/// This mutates the current user in-place.
///
- /// Refer to `EditProfile`'s documentation for its methods.
+ /// Refer to [`EditProfile`]'s documentation for its methods.
///
/// # Examples
///
@@ -88,6 +88,8 @@ impl CurrentUser {
///
/// CACHE.write().user.edit(|p| p.avatar(Some(&avatar)));
/// ```
+ ///
+ /// [`EditProfile`]: ../../builder/struct.EditProfile.html
pub fn edit<F>(&mut self, f: F) -> Result<()>
where F: FnOnce(EditProfile) -> EditProfile {
let mut map = VecMap::new();
@@ -204,13 +206,13 @@ impl CurrentUser {
/// # Errors
///
/// Returns an
- /// [`HttpError::InvalidRequest(Unauthorized)`][`HttpError::InvalidRequest`]
+ /// [`HttpError::UnsuccessfulRequest(Unauthorized)`][`HttpError::UnsuccessfulRequest`]
/// If the user is not authorized for this end point.
///
/// May return [`Error::Format`] while writing url to the buffer.
///
- /// [`Error::Format`]: ../enum.Error.html#variant.Format
- /// [`HttpError::InvalidRequest`]: ../http/enum.HttpError.html#variant.InvalidRequest
+ /// [`Error::Format`]: ../../enum.Error.html#variant.Format
+ /// [`HttpError::UnsuccessfulRequest`]: ../../http/enum.HttpError.html#variant.UnsuccessfulRequest
pub fn invite_url(&self, permissions: Permissions) -> Result<String> {
let bits = permissions.bits();
let client_id = http::get_current_application_info().map(|v| v.id)?;
@@ -458,7 +460,7 @@ impl User {
/// Returns a [`ModelError::MessagingBot`] if the user being direct messaged
/// is a bot user.
///
- /// [`ModelError::MessagingBot`]: enum.ModelError.html#variant.MessagingBot
+ /// [`ModelError::MessagingBot`]: ../error/enum.Error.html#variant.MessagingBot
/// [`PrivateChannel`]: struct.PrivateChannel.html
/// [`User::dm`]: struct.User.html#method.dm
// A tale with Clippy:
@@ -535,7 +537,7 @@ impl User {
/// Returns a [`ModelError::MessagingBot`] if the user being direct messaged
/// is a bot user.
///
- /// [`ModelError::MessagingBot`]: enum.ModelError.html#variant.MessagingBot
+ /// [`ModelError::MessagingBot`]: ../error/enum.Error.html#variant.MessagingBot
/// [direct_message]: #method.direct_message
#[cfg(feature = "builder")]
#[inline]
@@ -572,11 +574,11 @@ impl User {
/// let _ = message.author.has_role(guild_id, role_id);
/// ```
///
- /// [`Guild`]: struct.Guild.html
- /// [`GuildId`]: struct.GuildId.html
- /// [`PartialGuild`]: struct.PartialGuild.html
- /// [`Role`]: struct.Role.html
- /// [`Cache`]: ../cache/struct.Cache.html
+ /// [`Guild`]: ../guild/struct.Guild.html
+ /// [`GuildId`]: ../id/struct.GuildId.html
+ /// [`PartialGuild`]: ../guild/struct.PartialGuild.html
+ /// [`Role`]: ../guild/struct.Role.html
+ /// [`Cache`]: ../../cache/struct.Cache.html
// no-cache would warn on guild_id.
pub fn has_role<G, R>(&self, guild: G, role: R) -> bool
where G: Into<GuildContainer>, R: Into<RoleId> {
@@ -725,7 +727,7 @@ impl UserId {
/// Creates a direct message channel between the [current user] and the
/// user. This can also retrieve the channel if one already exists.
///
- /// [current user]: struct.CurrentUser.html
+ /// [current user]: ../user/struct.CurrentUser.html
pub fn create_dm_channel(&self) -> Result<PrivateChannel> {
let map = json!({
"recipient_id": self.0,
@@ -738,9 +740,10 @@ impl UserId {
#[cfg(feature = "cache")]
pub fn find(&self) -> Option<Arc<RwLock<User>>> { CACHE.read().user(*self) }
- /// Gets a user by its Id over the REST API.
+ /// Gets a user by its Id from either the cache or the REST API.
///
- /// **Note**: The current user must be a bot user.
+ /// Searches the cache for the user first, if the cache is enabled. If the
+ /// user was not found, then the user is searched via the REST API.
#[inline]
pub fn get(&self) -> Result<User> {
#[cfg(feature = "cache")]
@@ -843,3 +846,71 @@ fn tag(name: &str, discriminator: u16) -> String {
tag
}
+
+#[cfg(test)]
+mod test {
+ #[cfg(feature = "model")]
+ mod model {
+ use model::id::UserId;
+ use model::user::User;
+
+ fn gen() -> User {
+ User {
+ id: UserId(210),
+ avatar: Some("abc".to_string()),
+ bot: true,
+ discriminator: 1432,
+ name: "test".to_string(),
+ }
+ }
+
+ #[test]
+ fn test_core() {
+ let mut user = gen();
+
+ assert!(
+ user.avatar_url()
+ .unwrap()
+ .ends_with("/avatars/210/abc.webp?size=1024")
+ );
+ assert!(
+ user.static_avatar_url()
+ .unwrap()
+ .ends_with("/avatars/210/abc.webp?size=1024")
+ );
+
+ user.avatar = Some("a_aaa".to_string());
+ assert!(
+ user.avatar_url()
+ .unwrap()
+ .ends_with("/avatars/210/a_aaa.gif?size=1024")
+ );
+ assert!(
+ user.static_avatar_url()
+ .unwrap()
+ .ends_with("/avatars/210/a_aaa.webp?size=1024")
+ );
+
+ user.avatar = None;
+ assert!(user.avatar_url().is_none());
+
+ assert_eq!(user.tag(), "test#1432");
+ }
+
+ #[test]
+ fn default_avatars() {
+ let mut user = gen();
+
+ user.discriminator = 0;
+ assert!(user.default_avatar_url().ends_with("0.png"));
+ user.discriminator = 1;
+ assert!(user.default_avatar_url().ends_with("1.png"));
+ user.discriminator = 2;
+ assert!(user.default_avatar_url().ends_with("2.png"));
+ user.discriminator = 3;
+ assert!(user.default_avatar_url().ends_with("3.png"));
+ user.discriminator = 4;
+ assert!(user.default_avatar_url().ends_with("4.png"));
+ }
+ }
+}
diff --git a/src/model/webhook.rs b/src/model/webhook.rs
index 68d6012..fca70fe 100644
--- a/src/model/webhook.rs
+++ b/src/model/webhook.rs
@@ -33,7 +33,7 @@ pub struct Webhook {
///
/// This can be modified via [`ExecuteWebhook::avatar`].
///
- /// [`ExecuteWebhook::avatar`]: ../builder/struct.ExecuteWebhook.html#method.avatar
+ /// [`ExecuteWebhook::avatar`]: ../../builder/struct.ExecuteWebhook.html#method.avatar
pub avatar: Option<String>,
/// The Id of the channel that owns the webhook.
pub channel_id: ChannelId,
@@ -43,7 +43,7 @@ pub struct Webhook {
///
/// This can be modified via [`ExecuteWebhook::username`].
///
- /// [`ExecuteWebhook::username`]: ../builder/struct.ExecuteWebhook.html#method.username
+ /// [`ExecuteWebhook::username`]: ../../builder/struct.ExecuteWebhook.html#method.username
pub name: Option<String>,
/// The webhook's secure token.
pub token: String,
@@ -60,7 +60,7 @@ impl Webhook {
/// As this calls the [`http::delete_webhook_with_token`] function,
/// authentication is not required.
///
- /// [`http::delete_webhook_with_token`]: ../http/fn.delete_webhook_with_token.html
+ /// [`http::delete_webhook_with_token`]: ../../http/fn.delete_webhook_with_token.html
#[inline]
pub fn delete(&self) -> Result<()> { http::delete_webhook_with_token(self.id.0, &self.token) }
@@ -108,8 +108,8 @@ impl Webhook {
/// let _ = webhook.edit(None, Some(&image)).expect("Error editing");
/// ```
///
- /// [`http::edit_webhook`]: ../http/fn.edit_webhook.html
- /// [`http::edit_webhook_with_token`]: ../http/fn.edit_webhook_with_token.html
+ /// [`http::edit_webhook`]: ../../http/fn.edit_webhook.html
+ /// [`http::edit_webhook_with_token`]: ../../http/fn.edit_webhook_with_token.html
pub fn edit(&mut self, name: Option<&str>, avatar: Option<&str>) -> Result<()> {
if name.is_none() && avatar.is_none() {
return Ok(());
@@ -205,7 +205,7 @@ impl Webhook {
/// As this calls the [`http::get_webhook_with_token`] function,
/// authentication is not required.
///
- /// [`http::get_webhook_with_token`]: ../http/fn.get_webhook_with_token.html
+ /// [`http::get_webhook_with_token`]: ../../http/fn.get_webhook_with_token.html
pub fn refresh(&mut self) -> Result<()> {
match http::get_webhook_with_token(self.id.0, &self.token) {
Ok(replacement) => {
@@ -224,7 +224,7 @@ impl WebhookId {
///
/// **Note**: Requires the [Manage Webhooks] permission.
///
- /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
+ /// [Manage Webhooks]: ../../model/permissions/struct.Permissions.html#associatedconstant.MANAGE_WEBHOOKS
#[inline]
pub fn get(self) -> Result<Webhook> { http::get_webhook(self.0) }
}
diff --git a/src/utils/colour.rs b/src/utils/colour.rs
index c901e9b..a678a32 100644
--- a/src/utils/colour.rs
+++ b/src/utils/colour.rs
@@ -190,6 +190,22 @@ impl Colour {
/// [`g`]: #method.g
/// [`b`]: #method.b
pub fn tuple(&self) -> (u8, u8, u8) { (self.r(), self.g(), self.b()) }
+
+ /// Returns a hexadecimal string of this Colour.
+ ///
+ /// This is equivalent to passing the integer value through
+ /// `std::fmt::UpperHex` with 0 padding and 6 width
+ ///
+ /// # Examples
+ ///
+ /// ```rust
+ /// use serenity::utils::Colour;
+ ///
+ /// assert_eq!(Colour::new(6573123).hex(), "644C43");
+ /// ```
+ pub fn hex(&self) -> String {
+ format!("{:06X}", self.0)
+ }
}
impl From<i32> for Colour {
@@ -309,3 +325,55 @@ impl Default for Colour {
/// Creates a default value for a `Colour`, setting the inner value to `0`.
fn default() -> Colour { Colour(0) }
}
+
+#[cfg(test)]
+mod test {
+ use super::Colour;
+ use std::u32;
+
+ #[test]
+ fn new() {
+ assert_eq!(Colour::new(1).0, 1);
+ assert_eq!(Colour::new(u32::MIN).0, u32::MIN);
+ assert_eq!(Colour::new(u32::MAX).0, u32::MAX);
+ }
+
+ #[test]
+ fn from_rgb() {
+ assert_eq!(Colour::from_rgb(255, 0, 0).0, 0xFF0000);
+ assert_eq!(Colour::from_rgb(0, 255, 0).0, 0x00FF00);
+ assert_eq!(Colour::from_rgb(0, 0, 255).0, 0x0000FF);
+ }
+
+ #[test]
+ fn r() {
+ assert_eq!(Colour::new(0x336123).r(), 0x33);
+ }
+
+ #[test]
+ fn g() {
+ assert_eq!(Colour::new(0x336123).g(), 0x61);
+ }
+
+ #[test]
+ fn b() {
+ assert_eq!(Colour::new(0x336123).b(), 0x23);
+ }
+
+ #[test]
+ fn tuple() {
+ assert_eq!(Colour::new(0x336123).tuple(), (0x33, 0x61, 0x23));
+ }
+
+ #[test]
+ fn default() {
+ assert_eq!(Colour::default().0, 0);
+ }
+
+ #[test]
+ fn from() {
+ assert_eq!(Colour::from(7i32).0, 7);
+ assert_eq!(Colour::from(7u32).0, 7);
+ assert_eq!(Colour::from(7u64).0, 7);
+ }
+}
diff --git a/src/utils/message_builder.rs b/src/utils/message_builder.rs
index 7bbfddf..c609c28 100644
--- a/src/utils/message_builder.rs
+++ b/src/utils/message_builder.rs
@@ -988,3 +988,81 @@ fn normalize(text: &str) -> String {
.replace("@everyone", "@\u{200B}everyone")
.replace("@here", "@\u{200B}here")
}
+
+#[cfg(test)]
+mod test {
+ use model::prelude::*;
+ use super::{
+ ContentModifier::*,
+ MessageBuilder,
+ };
+
+ #[test]
+ fn code_blocks() {
+ let content = MessageBuilder::new()
+ .push_codeblock("test", Some("rb"))
+ .build();
+ assert_eq!(content, "```rb\ntest\n```");
+ }
+
+ #[test]
+ fn safe_content() {
+ let content = MessageBuilder::new()
+ .push_safe("@everyone discord.gg/discord-api")
+ .build();
+ assert_ne!(content, "@everyone discord.gg/discord-api");
+ }
+
+ #[test]
+ fn no_free_formatting() {
+ let content = MessageBuilder::new().push_bold_safe("test**test").build();
+ assert_ne!(content, "**test**test**");
+ }
+
+ #[test]
+ fn mentions() {
+ let content_emoji = MessageBuilder::new()
+ .emoji(&Emoji {
+ animated: false,
+ id: EmojiId(32),
+ name: "Rohrkatze".to_string(),
+ managed: false,
+ require_colons: true,
+ roles: vec![],
+ })
+ .build();
+ let content_mentions = MessageBuilder::new()
+ .channel(1)
+ .mention(&UserId(2))
+ .role(3)
+ .user(4)
+ .build();
+ assert_eq!(content_mentions, "<#1><@2><@&3><@4>");
+ assert_eq!(content_emoji, "<:Rohrkatze:32>");
+ }
+
+ #[test]
+ fn content() {
+ let content = Bold + Italic + Code + "Fun!";
+
+ assert_eq!(content.to_string(), "***`Fun!`***");
+ }
+
+ #[test]
+ fn message_content() {
+ let message_content = MessageBuilder::new()
+ .push(Bold + Italic + Code + "Fun!")
+ .build();
+
+ assert_eq!(message_content, "***`Fun!`***");
+ }
+
+ #[test]
+ fn message_content_safe() {
+ let message_content = MessageBuilder::new()
+ .push_safe(Bold + Italic + "test**test")
+ .build();
+
+ assert_eq!(message_content, "***test\\*\\*test***");
+ }
+}
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index e20dd22..347e19a 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -131,7 +131,7 @@ pub fn is_nsfw(name: &str) -> bool {
/// assert_eq!(utils::parse_invite(url), "0cDvIgU2voY8RSYL");
/// ```
///
-/// [`RichInvite`]: ../model/guild/struct.RichInvite.html
+/// [`RichInvite`]: ../model/invite/struct.RichInvite.html
pub fn parse_invite(code: &str) -> &str {
if code.starts_with("https://discord.gg/") {
&code[19..]
@@ -500,3 +500,54 @@ pub fn with_cache_mut<T, F>(mut f: F) -> T
let mut cache = CACHE.write();
f(&mut cache)
}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_invite_parser() {
+ assert_eq!(parse_invite("https://discord.gg/abc"), "abc");
+ assert_eq!(parse_invite("http://discord.gg/abc"), "abc");
+ assert_eq!(parse_invite("discord.gg/abc"), "abc");
+ }
+
+ #[test]
+ fn test_username_parser() {
+ assert_eq!(parse_username("<@12345>").unwrap(), 12_345);
+ assert_eq!(parse_username("<@!12345>").unwrap(), 12_345);
+ }
+
+ #[test]
+ fn role_parser() {
+ assert_eq!(parse_role("<@&12345>").unwrap(), 12_345);
+ }
+
+ #[test]
+ fn test_channel_parser() {
+ assert_eq!(parse_channel("<#12345>").unwrap(), 12_345);
+ }
+
+ #[test]
+ fn test_emoji_parser() {
+ let emoji = parse_emoji("<:name:12345>").unwrap();
+ assert_eq!(emoji.name, "name");
+ assert_eq!(emoji.id, 12_345);
+ }
+
+ #[test]
+ fn test_quote_parser() {
+ let parsed = parse_quotes("a \"b c\" d\"e f\" g");
+ assert_eq!(parsed, ["a", "b c", "d", "e f", "g"]);
+ }
+
+ #[test]
+ fn test_is_nsfw() {
+ assert!(!is_nsfw("general"));
+ assert!(is_nsfw("nsfw"));
+ assert!(is_nsfw("nsfw-test"));
+ assert!(!is_nsfw("nsfw-"));
+ assert!(!is_nsfw("général"));
+ assert!(is_nsfw("nsfw-général"));
+ }
+}
diff --git a/src/voice/audio.rs b/src/voice/audio.rs
index 2e15590..c556c8b 100644
--- a/src/voice/audio.rs
+++ b/src/voice/audio.rs
@@ -6,7 +6,6 @@ use std::{
pub const HEADER_LEN: usize = 12;
pub const SAMPLE_RATE: u32 = 48_000;
-pub static SILENT_FRAME: [u8; 3] = [0xf8, 0xff, 0xfe];
/// A readable audio source.
pub trait AudioSource: Send {
diff --git a/src/voice/connection.rs b/src/voice/connection.rs
index 54d9116..d1472d5 100644
--- a/src/voice/connection.rs
+++ b/src/voice/connection.rs
@@ -45,7 +45,7 @@ use std::{
},
time::Duration
};
-use super::audio::{AudioReceiver, AudioType, LockedAudio, HEADER_LEN, SAMPLE_RATE, SILENT_FRAME};
+use super::audio::{AudioReceiver, AudioType, HEADER_LEN, SAMPLE_RATE, LockedAudio};
use super::connection_info::ConnectionInfo;
use super::{payload, VoiceError, CRYPTO_MODE};
use websocket::{
@@ -200,8 +200,7 @@ impl Connection {
keepalive_timer: Timer::new(temp_heartbeat),
udp,
sequence: 0,
- // We need to send some frames to receive any audio.
- silence_frames: 100,
+ silence_frames: 0,
soft_clip,
speaking: false,
ssrc: hello.ssrc,
@@ -393,7 +392,7 @@ impl Connection {
self.silence_frames -= 1;
// Explicit "Silence" frame.
- opus_frame.extend_from_slice(&SILENT_FRAME);
+ opus_frame.extend_from_slice(&[0xf8, 0xff, 0xfe]);
} else {
// Per official guidelines, send 5x silence BEFORE we stop speaking.
self.set_speaking(false)?;
diff --git a/tests/resources/guild_-1_role_position.json b/tests/resources/guild_-1_role_position.json
new file mode 100644
index 0000000..30898d1
--- /dev/null
+++ b/tests/resources/guild_-1_role_position.json
@@ -0,0 +1,271 @@
+{
+ "voice_states": [],
+ "verification_level": 0,
+ "explicit_content_filter": 0,
+ "unavailable": false,
+ "splash": null,
+ "roles": [
+ {
+ "position": -1,
+ "permissions": 37215297,
+ "name": "@everyone",
+ "mentionable": false,
+ "managed": false,
+ "id": "1",
+ "hoist": false,
+ "color": 0
+ },
+ {
+ "position": 1,
+ "permissions": 66583679,
+ "name": "role",
+ "mentionable": false,
+ "managed": false,
+ "id": "1",
+ "hoist": true,
+ "color": 7419530
+ },
+ {
+ "position": 2,
+ "permissions": 536345727,
+ "name": "role 2",
+ "mentionable": false,
+ "managed": false,
+ "id": "17",
+ "hoist": true,
+ "color": 2123412
+ },
+ {
+ "position": 3,
+ "permissions": 66583679,
+ "name": "role 3",
+ "mentionable": false,
+ "managed": false,
+ "id": "166",
+ "hoist": true,
+ "color": 3447003
+ },
+ {
+ "position": 1,
+ "permissions": 37215297,
+ "name": "aaaaaa",
+ "mentionable": true,
+ "managed": false,
+ "id": "88",
+ "hoist": false,
+ "color": 15277667
+ },
+ {
+ "position": 1,
+ "permissions": 35840,
+ "name": "aaaabsadfasda",
+ "mentionable": false,
+ "managed": true,
+ "id": "643534543",
+ "hoist": false,
+ "color": 0
+ }
+ ],
+ "region": "us-central",
+ "presences": [
+ {
+ "user": {
+ "id": "2342342"
+ },
+ "status": "online",
+ "game": null
+ },
+ {
+ "user": {
+ "id": "1233432"
+ },
+ "status": "online",
+ "game": null
+ },
+ {
+ "user": {
+ "id": "35353534"
+ },
+ "status": "online",
+ "game": {
+ "url": "",
+ "type": 0,
+ "name": "aaaaaa"
+ }
+ },
+ {
+ "user": {
+ "id": "12314324"
+ },
+ "status": "online",
+ "game": null
+ }
+ ],
+ "owner_id": "7",
+ "name": "guild name",
+ "mfa_level": 0,
+ "members": [
+ {
+ "user": {
+ "username": "aaa",
+ "id": "92781184873947136",
+ "discriminator": "6291",
+ "avatar": "asdasdadada"
+ },
+ "roles": [
+ "164155714355462146"
+ ],
+ "nick": "asdasdadas",
+ "mute": false,
+ "joined_at": "2017-01-29T15:35:17.136000+00:00",
+ "deaf": false
+ },
+ {
+ "user": {
+ "username": "aaaaaa",
+ "id": "161972119494852608",
+ "discriminator": "7653",
+ "avatar": "ffffff"
+ },
+ "roles": [
+ "2342432423432"
+ ],
+ "mute": false,
+ "joined_at": "2017-01-29T15:35:17.136000+00:00",
+ "deaf": false
+ },
+ {
+ "user": {
+ "username": "aaaaa",
+ "id": "167333834952540160",
+ "discriminator": "0857",
+ "bot": true,
+ "avatar": "ffffff"
+ },
+ "roles": [
+ "34534543543"
+ ],
+ "mute": false,
+ "joined_at": "2017-01-29T15:35:17.136000+00:00",
+ "deaf": false
+ },
+ {
+ "user": {
+ "username": "aaaaaaa",
+ "id": "171403455745884160",
+ "discriminator": "0075",
+ "avatar": "ffffff"
+ },
+ "roles": [
+ "56465464"
+ ],
+ "mute": false,
+ "joined_at": "2017-01-29T15:35:17.136000+00:00",
+ "deaf": false
+ },
+ {
+ "user": {
+ "username": "asdasdsadas",
+ "id": "12312312",
+ "discriminator": "7181",
+ "bot": true,
+ "avatar": "ffffff"
+ },
+ "roles": [
+ "12313212321"
+ ],
+ "nick": null,
+ "mute": false,
+ "joined_at": "2017-01-29T15:35:17.136000+00:00",
+ "deaf": false
+ },
+ {
+ "user": {
+ "username": "aaaaa",
+ "id": "1231231231",
+ "discriminator": "2138",
+ "bot": true,
+ "avatar": "fake"
+ },
+ "roles": [
+ "1231231312"
+ ],
+ "nick": null,
+ "mute": false,
+ "joined_at": "2017-01-29T15:35:17.136000+00:00",
+ "deaf": false
+ }
+ ],
+ "member_count": 6,
+ "large": false,
+ "joined_at": "2017-01-29T15:35:17.136000+00:00",
+ "id": "12321321312321",
+ "icon": "fake icon",
+ "features": [],
+ "emojis": [],
+ "default_message_notifications": 0,
+ "channels": [
+ {
+ "type": 0,
+ "topic": "",
+ "position": 0,
+ "permission_overwrites": [
+ {
+ "type": "role",
+ "id": "123131231321",
+ "deny": 0,
+ "allow": 0
+ }
+ ],
+ "name": "asdadsa",
+ "last_message_id": "5676576575",
+ "id": "3453543543"
+ },
+ {
+ "user_limit": 0,
+ "type": 2,
+ "position": 0,
+ "permission_overwrites": [],
+ "name": "adssadasda",
+ "id": "56464564645",
+ "bitrate": 63841
+ },
+ {
+ "user_limit": 0,
+ "type": 2,
+ "position": 1,
+ "permission_overwrites": [
+ {
+ "type": "role",
+ "id": "23423432423",
+ "deny": 2097152,
+ "allow": 0
+ }
+ ],
+ "name": "AFK",
+ "id": "23432434242",
+ "bitrate": 64000
+ },
+ {
+ "type": 0,
+ "topic": null,
+ "position": 1,
+ "permission_overwrites": [],
+ "name": "asdasdasdsa",
+ "last_message_id": "234324324242",
+ "id": "2342343243242"
+ },
+ {
+ "user_limit": 0,
+ "type": 2,
+ "position": 2,
+ "permission_overwrites": [],
+ "name": "asdadsa",
+ "id": "32134242342",
+ "bitrate": 96000
+ }
+ ],
+ "afk_timeout": 900,
+ "afk_channel_id": "23432423423",
+ "system_channel_id": null
+}
diff --git a/tests/resources/role_-1_position.json b/tests/resources/role_-1_position.json
new file mode 100644
index 0000000..7dca625
--- /dev/null
+++ b/tests/resources/role_-1_position.json
@@ -0,0 +1,10 @@
+{
+ "position": -1,
+ "permissions": 37215297,
+ "name": "@everyone",
+ "mentionable": false,
+ "managed": false,
+ "id": "444",
+ "hoist": false,
+ "color": 0
+}
diff --git a/tests/test_args.rs b/tests/test_args.rs
deleted file mode 100644
index 9868b1b..0000000
--- a/tests/test_args.rs
+++ /dev/null
@@ -1,432 +0,0 @@
-extern crate serenity;
-#[macro_use] extern crate matches;
-
-use serenity::framework::standard::{Args, ArgError};
-
-#[test]
-fn single_with_empty_message() {
- let mut args = Args::new("", &["".to_string()]);
- assert_matches!(args.single::<String>().unwrap_err(), ArgError::Eos);
-
- let mut args = Args::new("", &[",".to_string()]);
- assert_matches!(args.single::<String>().unwrap_err(), ArgError::Eos);
-}
-
-#[test]
-fn single_n_with_empty_message() {
- let args = Args::new("", &["".to_string()]);
- assert_matches!(args.single_n::<String>().unwrap_err(), ArgError::Eos);
-
- let args = Args::new("", &[",".to_string()]);
- assert_matches!(args.single_n::<String>().unwrap_err(), ArgError::Eos);
-}
-
-#[test]
-fn single_quoted_with_empty_message() {
- let mut args = Args::new("", &["".to_string()]);
- assert_matches!(args.single_quoted::<String>().unwrap_err(), ArgError::Eos);
-
- let mut args = Args::new("", &[",".to_string()]);
- assert_matches!(args.single_quoted::<String>().unwrap_err(), ArgError::Eos);
-}
-
-#[test]
-fn multiple_with_empty_message() {
- let args = Args::new("", &["".to_string()]);
- assert_matches!(args.multiple::<String>().unwrap_err(), ArgError::Eos);
-
- let args = Args::new("", &[",".to_string()]);
- assert_matches!(args.multiple::<String>().unwrap_err(), ArgError::Eos);
-}
-
-#[test]
-fn multiple_quoted_with_empty_message() {
- let args = Args::new("", &["".to_string()]);
- assert_matches!(args.multiple_quoted::<String>().unwrap_err(), ArgError::Eos);
-
- let args = Args::new("", &[",".to_string()]);
- assert_matches!(args.multiple_quoted::<String>().unwrap_err(), ArgError::Eos);
-}
-
-#[test]
-fn skip_with_empty_message() {
- let mut args = Args::new("", &["".to_string()]);
- assert_matches!(args.skip(), None);
-
- let mut args = Args::new("", &[",".to_string()]);
- assert_matches!(args.skip(), None);
-}
-
-#[test]
-fn skip_for_with_empty_message() {
- let mut args = Args::new("", &["".to_string()]);
- assert_matches!(args.skip_for(0), None);
-
- let mut args = Args::new("", &["".to_string()]);
- assert_matches!(args.skip_for(5), None);
-
- let mut args = Args::new("", &[",".to_string()]);
- assert_matches!(args.skip_for(0), None);
-
- let mut args = Args::new("", &[",".to_string()]);
- assert_matches!(args.skip_for(5), None);
-}
-
-#[test]
-fn single_i32_with_2_bytes_long_delimiter() {
- let mut args = Args::new("1, 2", &[", ".to_string()]);
-
- assert_eq!(args.single::<i32>().unwrap(), 1);
- assert_eq!(args.single::<i32>().unwrap(), 2);
-}
-
-#[test]
-fn single_i32_with_1_byte_long_delimiter_i32() {
- let mut args = Args::new("1,2", &[",".to_string()]);
-
- assert_eq!(args.single::<i32>().unwrap(), 1);
- assert_eq!(args.single::<i32>().unwrap(), 2);
-}
-
-#[test]
-fn single_i32_with_wrong_char_after_first_arg() {
- let mut args = Args::new("1, 2", &[",".to_string()]);
-
- assert_eq!(args.single::<i32>().unwrap(), 1);
- assert!(args.single::<i32>().is_err());
-}
-
-#[test]
-fn single_i32_with_one_character_being_3_bytes_long() {
- let mut args = Args::new("1★2", &["★".to_string()]);
-
- assert_eq!(args.single::<i32>().unwrap(), 1);
- assert_eq!(args.single::<i32>().unwrap(), 2);
-}
-
-#[test]
-fn single_i32_with_untrimmed_whitespaces() {
- let mut args = Args::new(" 1, 2 ", &[",".to_string()]);
-
- assert!(args.single::<i32>().is_err());
-}
-
-#[test]
-fn single_i32_n() {
- let args = Args::new("1,2", &[",".to_string()]);
-
- assert_eq!(args.single_n::<i32>().unwrap(), 1);
- assert_eq!(args.single_n::<i32>().unwrap(), 1);
-}
-
-#[test]
-fn single_quoted_chaining() {
- let mut args = Args::new(r#""1, 2" "2" """#, &[" ".to_string()]);
-
- assert_eq!(args.single_quoted::<String>().unwrap(), "1, 2");
- assert_eq!(args.single_quoted::<String>().unwrap(), "2");
- assert_eq!(args.single_quoted::<String>().unwrap(), "");
-}
-
-#[test]
-fn single_quoted_and_single_chaining() {
- let mut args = Args::new(r#""1, 2" "2" "3" 4"#, &[" ".to_string()]);
-
- assert_eq!(args.single_quoted::<String>().unwrap(), "1, 2");
- assert!(args.single_n::<i32>().is_err());
- assert_eq!(args.single::<String>().unwrap(), "\"2\"");
- assert_eq!(args.single_quoted::<i32>().unwrap(), 3);
- assert_eq!(args.single::<i32>().unwrap(), 4);
-}
-
-#[test]
-fn full_on_args() {
- let test_text = "Some text to ensure `full()` works.";
- let args = Args::new(test_text, &[" ".to_string()]);
-
- assert_eq!(args.full(), test_text);
-}
-
-#[test]
-fn multiple_quoted_strings_one_delimiter() {
- let args = Args::new(r#""1, 2" "a" "3" 4 "5"#, &[" ".to_string()]);
-
- assert_eq!(args.multiple_quoted::<String>().unwrap(), ["1, 2", "a", "3", "4", "\"5"]);
-}
-
-#[test]
-fn multiple_quoted_strings_with_multiple_delimiter() {
- let args = Args::new(r#""1, 2" "a","3"4 "5"#, &[" ".to_string(), ",".to_string()]);
-
- assert_eq!(args.multiple_quoted::<String>().unwrap(), ["1, 2", "a", "3", "4", "\"5"]);
-}
-
-#[test]
-fn multiple_quoted_strings_with_multiple_delimiters() {
- let args = Args::new(r#""1, 2" "a","3" """#, &[" ".to_string(), ",".to_string()]);
-
- assert_eq!(args.multiple_quoted::<String>().unwrap(), ["1, 2", "a", "3", ""]);
-}
-
-#[test]
-fn multiple_quoted_i32() {
- let args = Args::new(r#""1" "2" 3"#, &[" ".to_string()]);
-
- assert_eq!(args.multiple_quoted::<i32>().unwrap(), [1, 2, 3]);
-}
-
-#[test]
-fn multiple_quoted_quote_appears_without_delimiter_in_front() {
- let args = Args::new(r#"hello, my name is cake" 2"#, &[",".to_string(), " ".to_string()]);
-
- assert_eq!(args.multiple_quoted::<String>().unwrap(), ["hello", "my", "name", "is", "cake\"", "2"]);
-}
-
-#[test]
-fn multiple_quoted_single_quote() {
- let args = Args::new(r#"hello "2 b"#, &[",".to_string(), " ".to_string()]);
-
- assert_eq!(args.multiple_quoted::<String>().unwrap(), ["hello", "\"2 b"]);
-}
-
-#[test]
-fn multiple_quoted_one_quote_pair() {
- let args = Args::new(r#"hello "2 b""#, &[",".to_string(), " ".to_string()]);
-
- assert_eq!(args.multiple_quoted::<String>().unwrap(), ["hello", "2 b"]);
-}
-
-
-#[test]
-fn delimiter_before_multiple_quoted() {
- let args = Args::new(r#","hello, my name is cake" "2""#, &[",".to_string(), " ".to_string()]);
-
- assert_eq!(args.multiple_quoted::<String>().unwrap(), ["hello, my name is cake", "2"]);
-}
-
-#[test]
-fn no_quote() {
- let args = Args::new("hello, my name is cake", &[",".to_string(), " ".to_string()]);
-
- assert_eq!(args.single_quoted_n::<String>().unwrap(), "hello");
-}
-
-#[test]
-fn single_quoted_n() {
- let args = Args::new(r#""hello, my name is cake","test"#, &[",".to_string()]);
-
- assert_eq!(args.single_quoted_n::<String>().unwrap(), "hello, my name is cake");
- assert_eq!(args.single_quoted_n::<String>().unwrap(), "hello, my name is cake");
-}
-
-#[test]
-fn multiple_quoted_starting_with_wrong_delimiter_in_first_quote() {
- let args = Args::new(r#""hello, my name is cake" "2""#, &[",".to_string(), " ".to_string()]);
-
- assert_eq!(args.multiple_quoted::<String>().unwrap(), ["hello, my name is cake", "2"]);
-}
-
-#[test]
-fn multiple_quoted_with_one_correct_and_one_invalid_quote() {
- let args = Args::new(r#""hello, my name is cake" "2""#, &[",".to_string(), " ".to_string()]);
-
- assert_eq!(args.multiple_quoted::<String>().unwrap(), ["hello, my name is cake", "2"]);
-}
-
-#[test]
-fn find_i32_one_one_byte_delimiter() {
- let mut args = Args::new("hello,my name is cake 2", &[" ".to_string()]);
-
- assert_eq!(args.find::<i32>().unwrap(), 2);
-}
-
-#[test]
-fn find_i32_one_three_byte_delimiter() {
- let mut args = Args::new("hello,my name is cakeé2", &["é".to_string()]);
-
- assert_eq!(args.find::<i32>().unwrap(), 2);
-}
-
-#[test]
-fn find_i32_multiple_delimiter_but_i32_not_last() {
- let mut args = Args::new("hello,my name is 2 cake", &[" ".to_string(), ",".to_string()]);
-
- assert_eq!(args.find::<i32>().unwrap(), 2);
-}
-
-#[test]
-fn find_i32_multiple_delimiter() {
- let mut args = Args::new("hello,my name is cake 2", &[" ".to_string(), ",".to_string()]);
-
- assert_eq!(args.find::<i32>().unwrap(), 2);
-}
-
-#[test]
-fn find_n_i32() {
- let mut args = Args::new("a 2", &[" ".to_string()]);
-
- assert_eq!(args.find_n::<i32>().unwrap(), 2);
- assert_eq!(args.find_n::<i32>().unwrap(), 2);
-}
-
-#[test]
-fn skip() {
- let mut args = Args::new("1 2", &[" ".to_string()]);
-
- assert_eq!(args.skip().unwrap(), "1");
- assert_eq!(args.remaining(), 1);
- assert_eq!(args.single::<String>().unwrap(), "2");
-}
-
-#[test]
-fn skip_for() {
- let mut args = Args::new("1 2 neko 100", &[" ".to_string()]);
-
- assert_eq!(args.skip_for(2).unwrap(), ["1", "2"]);
- assert_eq!(args.remaining(), 2);
- assert_eq!(args.single::<String>().unwrap(), "neko");
- assert_eq!(args.single::<String>().unwrap(), "100");
-}
-
-#[test]
-fn len_with_one_delimiter() {
- let args = Args::new("1 2 neko 100", &[" ".to_string()]);
-
- assert_eq!(args.len(), 4);
- assert_eq!(args.remaining(), 4);
-}
-
-#[test]
-fn len_multiple_quoted() {
- let args = Args::new(r#""hello, my name is cake" "2""#, &[" ".to_string()]);
-
- assert_eq!(args.len(), 2);
-}
-
-#[test]
-fn remaining_len_before_and_after_single() {
- let mut args = Args::new("1 2", &[" ".to_string()]);
-
- assert_eq!(args.remaining(), 2);
- assert_eq!(args.single::<i32>().unwrap(), 1);
- assert_eq!(args.remaining(), 1);
- assert_eq!(args.single::<i32>().unwrap(), 2);
- assert_eq!(args.remaining(), 0);
-}
-
-#[test]
-fn remaining_len_before_and_after_single_quoted() {
- let mut args = Args::new(r#""1" "2" "3""#, &[" ".to_string()]);
-
- assert_eq!(args.remaining(), 3);
- assert_eq!(args.single_quoted::<i32>().unwrap(), 1);
- assert_eq!(args.remaining(), 2);
- assert_eq!(args.single_quoted::<i32>().unwrap(), 2);
- assert_eq!(args.remaining(), 1);
- assert_eq!(args.single_quoted::<i32>().unwrap(), 3);
- assert_eq!(args.remaining(), 0);
-}
-
-#[test]
-fn remaining_len_before_and_after_skip() {
- let mut args = Args::new("1 2", &[" ".to_string()]);
-
- assert_eq!(args.remaining(), 2);
- assert_eq!(args.skip().unwrap(), "1");
- assert_eq!(args.remaining(), 1);
- assert_eq!(args.skip().unwrap(), "2");
- assert_eq!(args.remaining(), 0);
-}
-
-#[test]
-fn remaining_len_before_and_after_skip_empty_string() {
- let mut args = Args::new("", &[" ".to_string()]);
-
- assert_eq!(args.remaining(), 0);
- assert_eq!(args.skip(), None);
- assert_eq!(args.remaining(), 0);
-}
-
-#[test]
-fn remaining_len_before_and_after_skip_for() {
- let mut args = Args::new("1 2", &[" ".to_string()]);
-
- assert_eq!(args.remaining(), 2);
- assert_eq!(args.skip_for(2), Some(vec!["1".to_string(), "2".to_string()]));
- assert_eq!(args.skip_for(2), None);
- assert_eq!(args.remaining(), 0);
-}
-
-#[test]
-fn remaining_len_before_and_after_find() {
- let mut args = Args::new("a 2 6", &[" ".to_string()]);
-
- assert_eq!(args.remaining(), 3);
- assert_eq!(args.find::<i32>().unwrap(), 2);
- assert_eq!(args.remaining(), 2);
- assert_eq!(args.find::<i32>().unwrap(), 6);
- assert_eq!(args.remaining(), 1);
- assert_eq!(args.find::<String>().unwrap(), "a");
- assert_eq!(args.remaining(), 0);
- assert_matches!(args.find::<String>().unwrap_err(), ArgError::Eos);
- assert_eq!(args.remaining(), 0);
-}
-
-#[test]
-fn remaining_len_before_and_after_find_n() {
- let mut args = Args::new("a 2 6", &[" ".to_string()]);
-
- assert_eq!(args.remaining(), 3);
- assert_eq!(args.find_n::<i32>().unwrap(), 2);
- assert_eq!(args.remaining(), 3);
-}
-
-
-#[test]
-fn multiple_strings_with_one_delimiter() {
- let args = Args::new("hello, my name is cake 2", &[" ".to_string()]);
-
- assert_eq!(args.multiple::<String>().unwrap(), ["hello,", "my", "name", "is", "cake", "2"]);
-}
-
-#[test]
-fn multiple_i32_with_one_delimiter() {
- let args = Args::new("1 2 3", &[" ".to_string()]);
-
- assert_eq!(args.multiple::<i32>().unwrap(), [1, 2, 3]);
-}
-
-#[test]
-fn multiple_i32_with_one_delimiter_and_parse_error() {
- let args = Args::new("1 2 3 abc", &[" ".to_string()]);
-
- assert_matches!(args.multiple::<i32>().unwrap_err(), ArgError::Parse(_));
-}
-
-#[test]
-fn multiple_i32_with_three_delimiters() {
- let args = Args::new("1 2 3", &[" ".to_string(), ",".to_string()]);
-
- assert_eq!(args.multiple::<i32>().unwrap(), [1, 2, 3]);
-}
-
-#[test]
-fn single_after_failed_single() {
- let mut args = Args::new("b 2", &[" ".to_string()]);
-
- assert_matches!(args.single::<i32>().unwrap_err(), ArgError::Parse(_));
- // Test that `single` short-circuts on an error and leaves the source as is.
- assert_eq!(args.remaining(), 2);
- assert_eq!(args.single::<String>().unwrap(), "b");
- assert_eq!(args.single::<String>().unwrap(), "2");
-}
-
-#[test]
-fn remaining_len_after_failed_single_quoted() {
- let mut args = Args::new("b a", &[" ".to_string()]);
-
- assert_eq!(args.remaining(), 2);
- // Same goes for `single_quoted` and the alike.
- assert_matches!(args.single_quoted::<i32>().unwrap_err(), ArgError::Parse(_));
- assert_eq!(args.remaining(), 2);
-}
diff --git a/tests/test_cache.rs b/tests/test_cache.rs
deleted file mode 100644
index 833af04..0000000
--- a/tests/test_cache.rs
+++ /dev/null
@@ -1,174 +0,0 @@
-#![cfg(feature = "cache")]
-
-extern crate chrono;
-extern crate serde_json;
-extern crate serenity;
-
-use chrono::DateTime;
-use serde_json::{Number, Value};
-use serenity::{
- cache::{Cache, CacheUpdate, Settings},
- model::prelude::*,
- prelude::RwLock,
-};
-use std::{
- collections::HashMap,
- sync::Arc,
-};
-
-#[test]
-fn test_cache_messages() {
- let mut settings = Settings::new();
- settings.max_messages(2);
- let mut cache = Cache::new_with_settings(settings);
-
- // Test inserting one message into a channel's message cache.
- let datetime = DateTime::parse_from_str(
- "1983 Apr 13 12:09:14.274 +0000",
- "%Y %b %d %H:%M:%S%.3f %z",
- ).unwrap();
- let mut event = MessageCreateEvent {
- message: Message {
- id: MessageId(3),
- attachments: vec![],
- author: User {
- id: UserId(2),
- avatar: None,
- bot: false,
- discriminator: 1,
- name: "user 1".to_owned(),
- },
- channel_id: ChannelId(2),
- guild_id: Some(GuildId(1)),
- content: String::new(),
- edited_timestamp: None,
- embeds: vec![],
- kind: MessageType::Regular,
- member: None,
- mention_everyone: false,
- mention_roles: vec![],
- mentions: vec![],
- nonce: Value::Number(Number::from(1)),
- pinned: false,
- reactions: vec![],
- timestamp: datetime.clone(),
- tts: false,
- webhook_id: None,
- },
- };
- // Check that the channel cache doesn't exist.
- assert!(!cache.messages.contains_key(&event.message.channel_id));
- // Add first message, none because message ID 2 doesn't already exist.
- assert!(event.update(&mut cache).is_none());
- // None, it only returns the oldest message if the cache was already full.
- assert!(event.update(&mut cache).is_none());
- // Assert there's only 1 message in the channel's message cache.
- assert_eq!(cache.messages.get(&event.message.channel_id).unwrap().len(), 1);
-
- // Add a second message, assert that channel message cache length is 2.
- event.message.id = MessageId(4);
- assert!(event.update(&mut cache).is_none());
- assert_eq!(cache.messages.get(&event.message.channel_id).unwrap().len(), 2);
-
- // Add a third message, the first should now be removed.
- event.message.id = MessageId(5);
- assert!(event.update(&mut cache).is_some());
-
- {
- let channel = cache.messages.get(&event.message.channel_id).unwrap();
-
- assert_eq!(channel.len(), 2);
- // Check that the first message is now removed.
- assert!(!channel.contains_key(&MessageId(3)));
- }
-
- let guild_channel = GuildChannel {
- id: event.message.channel_id,
- bitrate: None,
- category_id: None,
- guild_id: event.message.guild_id.unwrap(),
- kind: ChannelType::Text,
- last_message_id: None,
- last_pin_timestamp: None,
- name: String::new(),
- permission_overwrites: vec![],
- position: 0,
- topic: None,
- user_limit: None,
- nsfw: false,
- };
-
- // Add a channel delete event to the cache, the cached messages for that
- // channel should now be gone.
- let mut delete = ChannelDeleteEvent {
- channel: Channel::Guild(Arc::new(RwLock::new(guild_channel.clone()))),
- };
- assert!(cache.update(&mut delete).is_none());
- assert!(!cache.messages.contains_key(&delete.channel.id()));
-
- // Test deletion of a guild channel's message cache when a GuildDeleteEvent
- // is received.
- let mut guild_create = {
- let mut channels = HashMap::new();
- channels.insert(ChannelId(2), Arc::new(RwLock::new(guild_channel.clone())));
-
- GuildCreateEvent {
- guild: Guild {
- id: GuildId(1),
- afk_channel_id: None,
- afk_timeout: 0,
- application_id: None,
- default_message_notifications: DefaultMessageNotificationLevel::All,
- emojis: HashMap::new(),
- explicit_content_filter: ExplicitContentFilter::None,
- features: vec![],
- icon: None,
- joined_at: datetime,
- large: false,
- member_count: 0,
- members: HashMap::new(),
- mfa_level: MfaLevel::None,
- name: String::new(),
- owner_id: UserId(3),
- presences: HashMap::new(),
- region: String::new(),
- roles: HashMap::new(),
- splash: None,
- system_channel_id: None,
- verification_level: VerificationLevel::Low,
- voice_states: HashMap::new(),
- channels,
- },
- }
- };
- assert!(cache.update(&mut guild_create).is_none());
- assert!(cache.update(&mut event).is_none());
-
- let mut guild_delete = GuildDeleteEvent {
- guild: PartialGuild {
- id: GuildId(1),
- afk_channel_id: None,
- afk_timeout: 0,
- default_message_notifications: DefaultMessageNotificationLevel::All,
- embed_channel_id: None,
- embed_enabled: false,
- emojis: HashMap::new(),
- features: vec![],
- icon: None,
- mfa_level: MfaLevel::None,
- name: String::new(),
- owner_id: UserId(3),
- region: String::new(),
- roles: HashMap::new(),
- splash: None,
- verification_level: VerificationLevel::Low,
- },
- };
-
- // The guild existed in the cache, so the cache's guild is returned by the
- // update.
- assert!(cache.update(&mut guild_delete).is_some());
-
- // Assert that the channel's message cache no longer exists.
- assert!(!cache.messages.contains_key(&ChannelId(2)));
-}
diff --git a/tests/test_channels.rs b/tests/test_channels.rs
deleted file mode 100644
index 92e31fc..0000000
--- a/tests/test_channels.rs
+++ /dev/null
@@ -1,91 +0,0 @@
-#![cfg(feature = "model")]
-
-extern crate parking_lot;
-extern crate serenity;
-
-#[cfg(feature = "utils")]
-mod utils {
- use parking_lot::RwLock;
- use serenity::model::prelude::*;
- use std::collections::HashMap;
- use std::sync::Arc;
-
- fn group() -> Group {
- Group {
- channel_id: ChannelId(1),
- icon: None,
- last_message_id: None,
- last_pin_timestamp: None,
- name: None,
- owner_id: UserId(2),
- recipients: HashMap::new(),
- }
- }
-
- fn guild_channel() -> GuildChannel {
- GuildChannel {
- id: ChannelId(1),
- bitrate: None,
- category_id: None,
- guild_id: GuildId(2),
- kind: ChannelType::Text,
- last_message_id: None,
- last_pin_timestamp: None,
- name: "nsfw-stuff".to_string(),
- permission_overwrites: vec![],
- position: 0,
- topic: None,
- user_limit: None,
- nsfw: false,
- }
- }
-
- fn private_channel() -> PrivateChannel {
- PrivateChannel {
- id: ChannelId(1),
- last_message_id: None,
- last_pin_timestamp: None,
- kind: ChannelType::Private,
- recipient: Arc::new(RwLock::new(User {
- id: UserId(2),
- avatar: None,
- bot: false,
- discriminator: 1,
- name: "ab".to_string(),
- })),
- }
- }
-
- #[test]
- fn nsfw_checks() {
- let mut channel = guild_channel();
- assert!(channel.is_nsfw());
- channel.kind = ChannelType::Voice;
- assert!(!channel.is_nsfw());
-
- channel.kind = ChannelType::Text;
- channel.name = "nsfw-".to_string();
- assert!(!channel.is_nsfw());
-
- channel.name = "nsfw".to_string();
- assert!(channel.is_nsfw());
- channel.kind = ChannelType::Voice;
- assert!(!channel.is_nsfw());
- channel.kind = ChannelType::Text;
-
- channel.name = "nsf".to_string();
- channel.nsfw = true;
- assert!(channel.is_nsfw());
- channel.nsfw = false;
- assert!(!channel.is_nsfw());
-
- let channel = Channel::Guild(Arc::new(RwLock::new(channel)));
- assert!(!channel.is_nsfw());
-
- let group = group();
- assert!(!group.is_nsfw());
-
- let private_channel = private_channel();
- assert!(!private_channel.is_nsfw());
- }
-}
diff --git a/tests/test_colour.rs b/tests/test_colour.rs
deleted file mode 100644
index 2b36562..0000000
--- a/tests/test_colour.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))]
-#![cfg(feature = "utils")]
-
-extern crate serenity;
-
-use serenity::utils::Colour;
-use std::u32;
-
-#[test]
-fn new() {
- assert_eq!(Colour::new(1).0, 1);
- assert_eq!(Colour::new(u32::MIN).0, u32::MIN);
- assert_eq!(Colour::new(u32::MAX).0, u32::MAX);
-}
-
-#[test]
-fn from_rgb() {
- assert_eq!(Colour::from_rgb(255, 0, 0).0, 0xFF0000);
- assert_eq!(Colour::from_rgb(0, 255, 0).0, 0x00FF00);
- assert_eq!(Colour::from_rgb(0, 0, 255).0, 0x0000FF);
-}
-
-#[test]
-fn r() {
- assert_eq!(Colour::new(0x336123).r(), 0x33);
-}
-
-#[test]
-fn g() {
- assert_eq!(Colour::new(0x336123).g(), 0x61);
-}
-
-#[test]
-fn b() {
- assert_eq!(Colour::new(0x336123).b(), 0x23);
-}
-
-#[test]
-fn tuple() {
- assert_eq!(Colour::new(0x336123).tuple(), (0x33, 0x61, 0x23));
-}
-
-#[test]
-fn default() {
- assert_eq!(Colour::default().0, 0);
-}
-
-#[test]
-fn from() {
- assert_eq!(Colour::from(7i32).0, 7);
- assert_eq!(Colour::from(7u32).0, 7);
- assert_eq!(Colour::from(7u64).0, 7);
-}
diff --git a/tests/test_create_embed.rs b/tests/test_create_embed.rs
deleted file mode 100644
index 5bdce5f..0000000
--- a/tests/test_create_embed.rs
+++ /dev/null
@@ -1,92 +0,0 @@
-#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))]
-#![cfg(all(feature = "builder", feature = "utils"))]
-
-#[macro_use]
-extern crate serde_json;
-extern crate serenity;
-
-use serde_json::Value;
-use serenity::model::channel::{Embed, EmbedField, EmbedFooter, EmbedImage, EmbedVideo};
-use serenity::builder::CreateEmbed;
-use serenity::utils::{self, Colour};
-
-#[test]
-fn test_from_embed() {
- let embed = Embed {
- author: None,
- colour: Colour::new(0xFF0011),
- description: Some("This is a test description".to_string()),
- fields: vec![
- EmbedField {
- inline: false,
- name: "a".to_string(),
- value: "b".to_string(),
- },
- EmbedField {
- inline: true,
- name: "c".to_string(),
- value: "z".to_string(),
- },
- ],
- footer: Some(EmbedFooter {
- icon_url: Some("https://i.imgur.com/XfWpfCV.gif".to_string()),
- proxy_icon_url: None,
- text: "This is a hakase footer".to_string(),
- }),
- image: Some(EmbedImage {
- height: 213,
- proxy_url: "a".to_string(),
- url: "https://i.imgur.com/XfWpfCV.gif".to_string(),
- width: 224,
- }),
- kind: "rich".to_string(),
- provider: None,
- thumbnail: None,
- timestamp: None,
- title: Some("hakase".to_string()),
- url: Some("https://i.imgur.com/XfWpfCV.gif".to_string()),
- video: Some(EmbedVideo {
- height: 213,
- url: "https://i.imgur.com/XfWpfCV.mp4".to_string(),
- width: 224,
- }),
- };
-
- let builder = CreateEmbed::from(embed)
- .colour(0xFF0011)
- .description("This is a hakase description")
- .image("https://i.imgur.com/XfWpfCV.gif")
- .title("still a hakase")
- .url("https://i.imgur.com/XfWpfCV.gif");
-
- let built = Value::Object(utils::vecmap_to_json_map(builder.0));
-
- let obj = json!({
- "color": 0xFF0011,
- "description": "This is a hakase description",
- "title": "still a hakase",
- "type": "rich",
- "url": "https://i.imgur.com/XfWpfCV.gif",
- "fields": [
- {
- "inline": false,
- "name": "a",
- "value": "b",
- },
- {
- "inline": true,
- "name": "c",
- "value": "z",
- },
- ],
- "image": {
- "url": "https://i.imgur.com/XfWpfCV.gif",
- },
- "footer": {
- "text": "This is a hakase footer",
- "icon_url": "https://i.imgur.com/XfWpfCV.gif",
- }
- });
-
- assert_eq!(built, obj);
-}
diff --git a/tests/test_decode_role.rs b/tests/test_decode_role.rs
deleted file mode 100644
index 61e0e13..0000000
--- a/tests/test_decode_role.rs
+++ /dev/null
@@ -1,302 +0,0 @@
-extern crate serde_json;
-extern crate serenity;
-
-use serde_json::Value;
-use serenity::model::guild::{Guild, Role};
-
-#[test]
-fn decode_negative_one_role_position() {
- let json = r#"{
- "position": -1,
- "permissions": 37215297,
- "name": "@everyone",
- "mentionable": false,
- "managed": false,
- "id": "444",
- "hoist": false,
- "color": 0
- }"#;
-
- let value: Value = serde_json::from_str(json).unwrap();
-
- serde_json::from_value::<Role>(value).unwrap();
-}
-
-#[test]
-fn decode_guild_with_n1_role_position() {
- let json = r#"{
- "voice_states": [],
- "verification_level": 0,
- "explicit_content_filter": 0,
- "unavailable": false,
- "splash": null,
- "roles": [
- {
- "position": -1,
- "permissions": 37215297,
- "name": "@everyone",
- "mentionable": false,
- "managed": false,
- "id": "1",
- "hoist": false,
- "color": 0
- },
- {
- "position": 1,
- "permissions": 66583679,
- "name": "role",
- "mentionable": false,
- "managed": false,
- "id": "1",
- "hoist": true,
- "color": 7419530
- },
- {
- "position": 2,
- "permissions": 536345727,
- "name": "role 2",
- "mentionable": false,
- "managed": false,
- "id": "17",
- "hoist": true,
- "color": 2123412
- },
- {
- "position": 3,
- "permissions": 66583679,
- "name": "role 3",
- "mentionable": false,
- "managed": false,
- "id": "166",
- "hoist": true,
- "color": 3447003
- },
- {
- "position": 1,
- "permissions": 37215297,
- "name": "aaaaaa",
- "mentionable": true,
- "managed": false,
- "id": "88",
- "hoist": false,
- "color": 15277667
- },
- {
- "position": 1,
- "permissions": 35840,
- "name": "aaaabsadfasda",
- "mentionable": false,
- "managed": true,
- "id": "643534543",
- "hoist": false,
- "color": 0
- }
- ],
- "region": "us-central",
- "presences": [
- {
- "user": {
- "id": "2342342"
- },
- "status": "online",
- "game": null
- },
- {
- "user": {
- "id": "1233432"
- },
- "status": "online",
- "game": null
- },
- {
- "user": {
- "id": "35353534"
- },
- "status": "online",
- "game": {
- "url": "",
- "type": 0,
- "name": "aaaaaa"
- }
- },
- {
- "user": {
- "id": "12314324"
- },
- "status": "online",
- "game": null
- }
- ],
- "owner_id": "7",
- "name": "guild name",
- "mfa_level": 0,
- "members": [
- {
- "user": {
- "username": "aaa",
- "id": "92781184873947136",
- "discriminator": "6291",
- "avatar": "asdasdadada"
- },
- "roles": [
- "164155714355462146"
- ],
- "nick": "asdasdadas",
- "mute": false,
- "joined_at": "2017-01-29T15:35:17.136000+00:00",
- "deaf": false
- },
- {
- "user": {
- "username": "aaaaaa",
- "id": "161972119494852608",
- "discriminator": "7653",
- "avatar": "ffffff"
- },
- "roles": [
- "2342432423432"
- ],
- "mute": false,
- "joined_at": "2017-01-29T15:35:17.136000+00:00",
- "deaf": false
- },
- {
- "user": {
- "username": "aaaaa",
- "id": "167333834952540160",
- "discriminator": "0857",
- "bot": true,
- "avatar": "ffffff"
- },
- "roles": [
- "34534543543"
- ],
- "mute": false,
- "joined_at": "2017-01-29T15:35:17.136000+00:00",
- "deaf": false
- },
- {
- "user": {
- "username": "aaaaaaa",
- "id": "171403455745884160",
- "discriminator": "0075",
- "avatar": "ffffff"
- },
- "roles": [
- "56465464"
- ],
- "mute": false,
- "joined_at": "2017-01-29T15:35:17.136000+00:00",
- "deaf": false
- },
- {
- "user": {
- "username": "asdasdsadas",
- "id": "12312312",
- "discriminator": "7181",
- "bot": true,
- "avatar": "ffffff"
- },
- "roles": [
- "12313212321"
- ],
- "nick": null,
- "mute": false,
- "joined_at": "2017-01-29T15:35:17.136000+00:00",
- "deaf": false
- },
- {
- "user": {
- "username": "aaaaa",
- "id": "1231231231",
- "discriminator": "2138",
- "bot": true,
- "avatar": "fake"
- },
- "roles": [
- "1231231312"
- ],
- "nick": null,
- "mute": false,
- "joined_at": "2017-01-29T15:35:17.136000+00:00",
- "deaf": false
- }
- ],
- "member_count": 6,
- "large": false,
- "joined_at": "2017-01-29T15:35:17.136000+00:00",
- "id": "12321321312321",
- "icon": "fake icon",
- "features": [],
- "emojis": [],
- "default_message_notifications": 0,
- "channels": [
- {
- "type": 0,
- "topic": "",
- "position": 0,
- "permission_overwrites": [
- {
- "type": "role",
- "id": "123131231321",
- "deny": 0,
- "allow": 0
- }
- ],
- "name": "asdadsa",
- "last_message_id": "5676576575",
- "id": "3453543543"
- },
- {
- "user_limit": 0,
- "type": 2,
- "position": 0,
- "permission_overwrites": [],
- "name": "adssadasda",
- "id": "56464564645",
- "bitrate": 63841
- },
- {
- "user_limit": 0,
- "type": 2,
- "position": 1,
- "permission_overwrites": [
- {
- "type": "role",
- "id": "23423432423",
- "deny": 2097152,
- "allow": 0
- }
- ],
- "name": "AFK",
- "id": "23432434242",
- "bitrate": 64000
- },
- {
- "type": 0,
- "topic": null,
- "position": 1,
- "permission_overwrites": [],
- "name": "asdasdasdsa",
- "last_message_id": "234324324242",
- "id": "2342343243242"
- },
- {
- "user_limit": 0,
- "type": 2,
- "position": 2,
- "permission_overwrites": [],
- "name": "asdadsa",
- "id": "32134242342",
- "bitrate": 96000
- }
- ],
- "afk_timeout": 900,
- "afk_channel_id": "23432423423",
- "system_channel_id": null
- }"#;
-
- let value: Value = serde_json::from_str(json).unwrap();
-
- serde_json::from_value::<Guild>(value).unwrap();
-}
diff --git a/tests/test_deser.rs b/tests/test_deser.rs
index 70b6f1f..72c955d 100644
--- a/tests/test_deser.rs
+++ b/tests/test_deser.rs
@@ -8,11 +8,12 @@ use serenity::model::prelude::*;
use std::fs::File;
macro_rules! p {
- ($s:ident, $filename:expr) => {
+ ($s:ident, $filename:expr) => {{
let f = File::open(concat!("./tests/resources/", $filename, ".json")).unwrap();
let v = serde_json::from_reader::<File, Value>(f).unwrap();
- let _ = $s::deserialize(v).unwrap();
- };
+
+ $s::deserialize(v).unwrap()
+ }};
}
#[test]
@@ -193,3 +194,25 @@ fn guild_features_deser() {
fn guild_system_channel_id_missing() {
p!(Guild, "guild_system_channel_id_missing");
}
+
+#[test]
+fn decode_negative_one_role_position() {
+ p!(Role, "role_-1_position");
+}
+
+#[test]
+fn decode_guild_with_n1_role_position() {
+ p!(Guild, "guild_-1_role_position");
+}
+
+#[test]
+fn decode_footer_deser() {
+ let mut message = p!(Message, "message_footer_1");
+
+ assert_eq!(
+ message.embeds.remove(0).footer.unwrap().text,
+ "2005-09-26 - 2013-09-26"
+ );
+
+ p!(Message, "message_footer_2");
+}
diff --git a/tests/test_formatters.rs b/tests/test_formatters.rs
deleted file mode 100644
index ccdfba1..0000000
--- a/tests/test_formatters.rs
+++ /dev/null
@@ -1,80 +0,0 @@
-extern crate parking_lot;
-extern crate serenity;
-
-use serenity::model::prelude::*;
-
-#[test]
-fn test_formatters() {
- assert_eq!(ChannelId(1).to_string(), "1");
- assert_eq!(EmojiId(2).to_string(), "2");
- assert_eq!(GuildId(3).to_string(), "3");
- assert_eq!(RoleId(4).to_string(), "4");
- assert_eq!(UserId(5).to_string(), "5");
-}
-
-#[cfg(feature = "utils")]
-#[test]
-fn test_mention() {
- use parking_lot::RwLock;
- use serenity::utils::Colour;
- use std::sync::Arc;
-
- let channel = Channel::Guild(Arc::new(RwLock::new(GuildChannel {
- bitrate: None,
- category_id: None,
- guild_id: GuildId(1),
- kind: ChannelType::Text,
- id: ChannelId(4),
- last_message_id: None,
- last_pin_timestamp: None,
- name: "a".to_string(),
- permission_overwrites: vec![],
- position: 1,
- topic: None,
- user_limit: None,
- nsfw: false,
- })));
- let emoji = Emoji {
- animated: false,
- id: EmojiId(5),
- name: "a".to_string(),
- managed: true,
- require_colons: true,
- roles: vec![],
- };
- let role = Role {
- id: RoleId(2),
- colour: Colour::ROSEWATER,
- hoist: false,
- managed: false,
- mentionable: false,
- name: "fake role".to_string(),
- permissions: Permissions::empty(),
- position: 1,
- };
- let user = User {
- id: UserId(6),
- avatar: None,
- bot: false,
- discriminator: 4132,
- name: "fake".to_string(),
- };
- let member = Member {
- deaf: false,
- guild_id: GuildId(2),
- joined_at: None,
- mute: false,
- nick: None,
- roles: vec![],
- user: Arc::new(RwLock::new(user.clone())),
- };
-
- assert_eq!(ChannelId(1).mention(), "<#1>");
- assert_eq!(channel.mention(), "<#4>");
- assert_eq!(emoji.mention(), "<:a:5>");
- assert_eq!(member.mention(), "<@6>");
- assert_eq!(role.mention(), "<@&2>");
- assert_eq!(role.id.mention(), "<@&2>");
- assert_eq!(user.mention(), "<@6>");
- assert_eq!(user.id.mention(), "<@6>");
-}
diff --git a/tests/test_guild.rs b/tests/test_guild.rs
deleted file mode 100644
index 1045381..0000000
--- a/tests/test_guild.rs
+++ /dev/null
@@ -1,103 +0,0 @@
-#![cfg(feature = "model")]
-
-extern crate chrono;
-extern crate serenity;
-
-use chrono::prelude::*;
-use serenity::model::prelude::*;
-use serenity::prelude::*;
-use std::collections::*;
-use std::sync::Arc;
-
-fn gen_user() -> User {
- User {
- id: UserId(210),
- avatar: Some("abc".to_string()),
- bot: true,
- discriminator: 1432,
- name: "test".to_string(),
- }
-}
-
-fn gen_member() -> Member {
- let dt: DateTime<FixedOffset> = FixedOffset::east(5 * 3600)
- .ymd(2016, 11, 08)
- .and_hms(0, 0, 0);
- let vec1 = Vec::new();
- let u = Arc::new(RwLock::new(gen_user()));
-
- Member {
- deaf: false,
- guild_id: GuildId(1),
- joined_at: Some(dt),
- mute: false,
- nick: Some("aaaa".to_string()),
- roles: vec1,
- user: u,
- }
-}
-
-fn gen() -> Guild {
- let u = gen_user();
- let m = gen_member();
-
- let hm1 = HashMap::new();
- let hm2 = HashMap::new();
- let vec1 = Vec::new();
- let dt: DateTime<FixedOffset> = FixedOffset::east(5 * 3600)
- .ymd(2016, 11, 08)
- .and_hms(0, 0, 0);
- let mut hm3 = HashMap::new();
- let hm4 = HashMap::new();
- let hm5 = HashMap::new();
- let hm6 = HashMap::new();
-
- hm3.insert(u.id, m);
-
- Guild {
- afk_channel_id: Some(ChannelId(0)),
- afk_timeout: 0,
- channels: hm1,
- default_message_notifications: DefaultMessageNotificationLevel::All,
- emojis: hm2,
- features: vec1,
- icon: Some("/avatars/210/a_aaa.webp?size=1024".to_string()),
- id: GuildId(1),
- joined_at: dt,
- large: false,
- member_count: 1,
- members: hm3,
- mfa_level: MfaLevel::Elevated,
- name: "Spaghetti".to_string(),
- owner_id: UserId(210),
- presences: hm4,
- region: "NA".to_string(),
- roles: hm5,
- splash: Some("asdf".to_string()),
- verification_level: VerificationLevel::None,
- voice_states: hm6,
- application_id: Some(ApplicationId(0)),
- explicit_content_filter: ExplicitContentFilter::None,
- system_channel_id: Some(ChannelId(0)),
- }
-}
-
-
-#[test]
-fn member_named_username() {
- let guild = gen();
- let lhs = guild
- .member_named("test#1432")
- .unwrap()
- .display_name();
-
- assert_eq!(lhs, gen_member().display_name());
-}
-
-#[test]
-fn member_named_nickname() {
- let guild = gen();
- let lhs = guild.member_named("aaaa").unwrap().display_name();
-
- assert_eq!(lhs, gen_member().display_name());
-}
diff --git a/tests/test_http.rs b/tests/test_http.rs
deleted file mode 100644
index 1389ef6..0000000
--- a/tests/test_http.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-#![cfg(feature = "http")]
-
-extern crate serenity;
-
-use serenity::http::AttachmentType;
-use std::path::Path;
-
-#[test]
-fn test_attachment_type() {
- assert!(match AttachmentType::from(Path::new("./dogs/corgis/kona.png")) {
- AttachmentType::Path(_) => true,
- _ => false,
- });
- assert!(match AttachmentType::from("./cats/copycat.png") {
- AttachmentType::Path(_) => true,
- _ => false,
- });
-}
diff --git a/tests/test_message.rs b/tests/test_message.rs
deleted file mode 100644
index 71afcf5..0000000
--- a/tests/test_message.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-extern crate serde;
-extern crate serde_json;
-extern crate serenity;
-
-use serde::de::Deserialize;
-use serde_json::Value;
-use serenity::model::channel::Message;
-use std::fs::File;
-
-macro_rules! p {
- ($s:ident, $filename:expr) => ({
- let f = File::open(concat!("./tests/resources/", $filename, ".json")).unwrap();
- let v = serde_json::from_reader::<File, Value>(f).unwrap();
-
- $s::deserialize(v).unwrap()
- })
-}
-
-#[test]
-fn test_footer_deser() {
- let mut message = p!(Message, "message_footer_1");
-
- assert_eq!(
- message.embeds.remove(0).footer.unwrap().text,
- "2005-09-26 - 2013-09-26"
- );
-
- p!(Message, "message_footer_2");
-}
diff --git a/tests/test_msg_builder.rs b/tests/test_msg_builder.rs
deleted file mode 100644
index c291bf5..0000000
--- a/tests/test_msg_builder.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-#![cfg(feature = "utils")]
-
-extern crate serenity;
-
-use serenity::utils::MessageBuilder;
-use serenity::utils::ContentModifier::*;
-use serenity::model::guild::Emoji;
-use serenity::model::id::{EmojiId, UserId};
-
-#[test]
-fn code_blocks() {
- let content = MessageBuilder::new()
- .push_codeblock("test", Some("rb"))
- .build();
- assert_eq!(content, "```rb\ntest\n```");
-}
-
-#[test]
-fn safe_content() {
- let content = MessageBuilder::new()
- .push_safe("@everyone discord.gg/discord-api")
- .build();
- assert_ne!(content, "@everyone discord.gg/discord-api");
-}
-
-#[test]
-fn no_free_formatting() {
- let content = MessageBuilder::new().push_bold_safe("test**test").build();
- assert_ne!(content, "**test**test**");
-}
-
-#[test]
-fn mentions() {
- let content_emoji = MessageBuilder::new()
- .emoji(&Emoji {
- animated: false,
- id: EmojiId(32),
- name: "Rohrkatze".to_string(),
- managed: false,
- require_colons: true,
- roles: vec![],
- })
- .build();
- let content_mentions = MessageBuilder::new()
- .channel(1)
- .mention(&UserId(2))
- .role(3)
- .user(4)
- .build();
- assert_eq!(content_mentions, "<#1><@2><@&3><@4>");
- assert_eq!(content_emoji, "<:Rohrkatze:32>");
-}
-
-#[test]
-fn content() {
- let content = Bold + Italic + Code + "Fun!";
-
- assert_eq!(content.to_string(), "***`Fun!`***");
-}
-
-#[test]
-fn message_content() {
- let message_content = MessageBuilder::new()
- .push(Bold + Italic + Code + "Fun!")
- .build();
-
- assert_eq!(message_content, "***`Fun!`***");
-}
-
-#[test]
-fn message_content_safe() {
- let message_content = MessageBuilder::new()
- .push_safe(Bold + Italic + "test**test")
- .build();
-
- assert_eq!(message_content, "***test\\*\\*test***");
-}
diff --git a/tests/test_parsers.rs b/tests/test_parsers.rs
deleted file mode 100644
index eddad53..0000000
--- a/tests/test_parsers.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-#![cfg(feature = "utils")]
-
-extern crate serenity;
-
-use serenity::utils::*;
-
-#[test]
-fn invite_parser() {
- assert_eq!(parse_invite("https://discord.gg/abc"), "abc");
- assert_eq!(parse_invite("http://discord.gg/abc"), "abc");
- assert_eq!(parse_invite("discord.gg/abc"), "abc");
-}
-
-#[test]
-fn username_parser() {
- assert_eq!(parse_username("<@12345>").unwrap(), 12_345);
- assert_eq!(parse_username("<@!12345>").unwrap(), 12_345);
-}
-
-#[test]
-fn role_parser() {
- assert_eq!(parse_role("<@&12345>").unwrap(), 12_345);
-}
-
-#[test]
-fn channel_parser() {
- assert_eq!(parse_channel("<#12345>").unwrap(), 12_345);
-}
-
-#[test]
-fn emoji_parser() {
- let emoji = parse_emoji("<:name:12345>").unwrap();
- assert_eq!(emoji.name, "name");
- assert_eq!(emoji.id, 12_345);
-}
-
-#[test]
-fn quote_parser() {
- let parsed = parse_quotes("a \"b c\" d\"e f\" g");
- assert_eq!(parsed, ["a", "b c", "d", "e f", "g"]);
-}
diff --git a/tests/test_user.rs b/tests/test_user.rs
deleted file mode 100644
index 267b578..0000000
--- a/tests/test_user.rs
+++ /dev/null
@@ -1,66 +0,0 @@
-extern crate serenity;
-
-#[cfg(feature = "model")]
-mod model {
- use serenity::model::id::UserId;
- use serenity::model::user::User;
-
- fn gen() -> User {
- User {
- id: UserId(210),
- avatar: Some("abc".to_string()),
- bot: true,
- discriminator: 1432,
- name: "test".to_string(),
- }
- }
-
- #[test]
- fn test_core() {
- let mut user = gen();
-
- assert!(
- user.avatar_url()
- .unwrap()
- .ends_with("/avatars/210/abc.webp?size=1024",)
- );
- assert!(
- user.static_avatar_url()
- .unwrap()
- .ends_with("/avatars/210/abc.webp?size=1024",)
- );
-
- user.avatar = Some("a_aaa".to_string());
- assert!(
- user.avatar_url()
- .unwrap()
- .ends_with("/avatars/210/a_aaa.gif?size=1024",)
- );
- assert!(
- user.static_avatar_url()
- .unwrap()
- .ends_with("/avatars/210/a_aaa.webp?size=1024",)
- );
-
- user.avatar = None;
- assert!(user.avatar_url().is_none());
-
- assert_eq!(user.tag(), "test#1432");
- }
-
- #[test]
- fn default_avatars() {
- let mut user = gen();
-
- user.discriminator = 0;
- assert!(user.default_avatar_url().ends_with("0.png"));
- user.discriminator = 1;
- assert!(user.default_avatar_url().ends_with("1.png"));
- user.discriminator = 2;
- assert!(user.default_avatar_url().ends_with("2.png"));
- user.discriminator = 3;
- assert!(user.default_avatar_url().ends_with("3.png"));
- user.discriminator = 4;
- assert!(user.default_avatar_url().ends_with("4.png"));
- }
-}
diff --git a/tests/test_utils.rs b/tests/test_utils.rs
deleted file mode 100644
index d5dac6d..0000000
--- a/tests/test_utils.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-#![cfg(feature = "utils")]
-
-extern crate serenity;
-
-use serenity::utils::*;
-
-#[test]
-fn test_is_nsfw() {
- assert!(!is_nsfw("general"));
- assert!(is_nsfw("nsfw"));
- assert!(is_nsfw("nsfw-test"));
- assert!(!is_nsfw("nsfw-"));
- assert!(!is_nsfw("général"));
- assert!(is_nsfw("nsfw-général"));
-}