twilight_http/request/mod.rs
1//! Typed request builders, [multipart form support], a [manual request builder]
2//! for low-level request construction, and [audit log reason] support.
3//!
4//! # Request Builders
5//!
6//! Requests are created in the form of builders. These can be `await`ed to
7//! receive a [`Response`]. Every route of Discord's API has its own builder:
8//! creating a message is performed via the [`CreateMessage`] builder; updating
9//! a guild is done via [`UpdateGuild`]; and so on. All typed request builders
10//! are instantiated via the primary [`Client`]. When the library doesn't yet
11//! support a new feature or fine-grained support is required, requests can be
12//! manually built via [`RequestBuilder`].
13//!
14//! # Audit Log Reasons
15//!
16//! Audit log reasons can be added to supported requests via the
17//! [`AuditLogReason`] trait:
18//!
19//! ```no_run
20//! # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
21//! # use twilight_model::id::Id;
22//! #
23//! # let guild_id = Id::new(1);
24//! # let user_id = Id::new(2);
25//! use twilight_http::{client::Client, request::AuditLogReason};
26//!
27//! let client = Client::new(std::env::var("DISCORD_TOKEN")?);
28//! client
29//! .delete_ban(guild_id, user_id)
30//! .reason("ban expired")
31//! .await?;
32//! # Ok(()) }
33//! ```
34//!
35//! [`Client`]: crate::client::Client
36//! [`CreateMessage`]: channel::message::CreateMessage
37//! [`Response`]: crate::Response
38//! [`UpdateGuild`]: guild::UpdateGuild
39//! [audit log reason]: AuditLogReason
40//! [manual request builder]: RequestBuilder
41//! [multipart form support]: Form
42
43pub mod application;
44pub mod attachment;
45pub mod channel;
46pub mod guild;
47pub mod poll;
48pub mod scheduled_event;
49pub mod sticker;
50pub mod template;
51pub mod user;
52
53mod audit_reason;
54mod base;
55mod get_current_authorization_information;
56mod get_gateway;
57mod get_gateway_authed;
58mod get_user_application;
59mod get_voice_regions;
60mod multipart;
61mod try_into_request;
62mod update_user_application;
63
64pub use self::{
65 audit_reason::AuditLogReason,
66 base::{Request, RequestBuilder},
67 get_current_authorization_information::GetCurrentAuthorizationInformation,
68 get_gateway::GetGateway,
69 get_gateway_authed::GetGatewayAuthed,
70 get_user_application::GetUserApplicationInfo,
71 get_voice_regions::GetVoiceRegions,
72 multipart::Form,
73 try_into_request::TryIntoRequest,
74 update_user_application::UpdateCurrentUserApplication,
75};
76pub use twilight_http_ratelimiting::request::Method;
77
78use crate::error::{Error, ErrorType};
79use http::header::{HeaderName, HeaderValue};
80use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
81use serde::Serialize;
82use std::iter;
83
84/// Name of the audit log reason header.
85const REASON_HEADER_NAME: &str = "x-audit-log-reason";
86
87/// Type that either serializes to null or a value.
88///
89/// This is particularly useful when combined with an `Option` by allowing three
90/// states via `Option<Nullable<T>>`: undefined, null, and T.
91///
92/// When the request value is `None` it can skip serialization, while if
93/// `Nullable` is provided with `None` within it then it will serialize as
94/// null. This mechanism is primarily used in patch requests.
95#[derive(Serialize)]
96struct Nullable<T>(Option<T>);
97
98fn audit_header(reason: &str) -> Result<impl Iterator<Item = (HeaderName, HeaderValue)>, Error> {
99 let header_name = HeaderName::from_static(REASON_HEADER_NAME);
100 let encoded_reason = utf8_percent_encode(reason, NON_ALPHANUMERIC).to_string();
101 let header_value = HeaderValue::from_str(&encoded_reason).map_err(|e| Error {
102 kind: ErrorType::CreatingHeader {
103 name: encoded_reason,
104 },
105 source: Some(Box::new(e)),
106 })?;
107
108 Ok(iter::once((header_name, header_value)))
109}