From 7e120070b275023fecbf0baaf150d284e125ede3 Mon Sep 17 00:00:00 2001 From: Eduard Smet Date: Wed, 28 Jan 2026 03:29:17 +0100 Subject: [PATCH 1/2] feat(http): Add support for working with Form buffers --- twilight-http/src/error.rs | 11 +++++++++++ twilight-http/src/request/base.rs | 25 ++++++++++++++++++++----- twilight-http/src/request/multipart.rs | 10 ++++++++++ 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/twilight-http/src/error.rs b/twilight-http/src/error.rs index e2a859ae68..0d7abb5e60 100644 --- a/twilight-http/src/error.rs +++ b/twilight-http/src/error.rs @@ -1,5 +1,6 @@ use crate::{api_error::ApiError, json::JsonError, response::StatusCode}; use std::{ + array::TryFromSliceError, error::Error as StdError, fmt::{Debug, Display, Formatter, Result as FmtResult}, str, @@ -30,6 +31,13 @@ impl Error { (self.kind, self.source) } + pub(super) fn multipart(source: TryFromSliceError) -> Self { + Self { + kind: ErrorType::Multipart, + source: Some(Box::new(source)), + } + } + pub(super) fn json(source: JsonError) -> Self { Self { kind: ErrorType::Json, @@ -55,6 +63,7 @@ impl Display for Error { f.write_str(" failed") } + ErrorType::Multipart => f.write_str("buffer could not get turned into a valid form"), ErrorType::Json => f.write_str("given value couldn't be serialized"), ErrorType::Parsing { body, .. } => { f.write_str("response body couldn't be deserialized: ")?; @@ -100,6 +109,7 @@ pub enum ErrorType { CreatingHeader { name: String, }, + Multipart, Json, Parsing { body: Vec, @@ -184,6 +194,7 @@ impl Debug for ErrorType { .debug_struct("CreatingHeader") .field("name", name) .finish(), + Self::Multipart => f.write_str("Multipart"), Self::Json => f.write_str("Json"), Self::Parsing { body } => { let mut debug = f.debug_struct("Parsing"); diff --git a/twilight-http/src/request/base.rs b/twilight-http/src/request/base.rs index 990ac143b4..daa2ff6e88 100644 --- a/twilight-http/src/request/base.rs +++ b/twilight-http/src/request/base.rs @@ -90,14 +90,20 @@ impl RequestBuilder { self } - /// Set the multipart form. - #[allow(clippy::missing_const_for_fn)] - pub fn form(mut self, form: Form) -> Self { + /// Set the the multipart form from an existing buffer. + /// + /// # Errors + /// + /// Returns an [`ErrorType::Multipart`] error type buffer input could not get turned into a + /// valid form. + /// + /// [`ErrorType::Multipart`]: crate::error::ErrorType::Multipart + pub fn multipart(mut self, form_buffer: Vec) -> Result { if let Ok(request) = self.0.as_mut() { - request.form = Some(form); + request.form = Some(Form::from_buffer(form_buffer).map_err(Error::multipart)?); } - self + Ok(self) } /// Set the headers to add. @@ -121,6 +127,15 @@ impl RequestBuilder { self } + /// Set the multipart form. + pub fn form(mut self, form: Form) -> Self { + if let Ok(request) = self.0.as_mut() { + request.form = Some(form); + } + + self + } + /// Whether to use the client's authorization token in the request, if one /// is set. /// diff --git a/twilight-http/src/request/multipart.rs b/twilight-http/src/request/multipart.rs index f2192ad664..d3e97054ec 100644 --- a/twilight-http/src/request/multipart.rs +++ b/twilight-http/src/request/multipart.rs @@ -1,3 +1,5 @@ +use std::array::TryFromSliceError; + #[derive(Clone, Debug)] #[must_use = "has no effect if not built into a Form"] pub struct Form { @@ -19,6 +21,14 @@ impl Form { Self::default() } + pub(crate) fn from_buffer(mut buffer: Vec) -> Result { + let boundary: [u8; 15] = buffer[(buffer.len() - 17)..(buffer.len() - 2)].try_into()?; + + buffer.truncate(buffer.len() - 2); + + Ok(Self { boundary, buffer }) + } + /// Consume the form, returning the buffer's contents. pub fn build(mut self) -> Vec { self.buffer.extend(Self::BOUNDARY_TERMINATOR); From 7e23bb4b523dfa0b95b30c019cf799727bb21050 Mon Sep 17 00:00:00 2001 From: Eduard Smet Date: Fri, 5 Jun 2026 18:34:32 +0200 Subject: [PATCH 2/2] feat(http): Simplify support for working with Form buffers --- twilight-http/src/error.rs | 11 ----------- twilight-http/src/request/base.rs | 15 ++++----------- twilight-http/src/request/multipart.rs | 14 ++++++-------- 3 files changed, 10 insertions(+), 30 deletions(-) diff --git a/twilight-http/src/error.rs b/twilight-http/src/error.rs index 0d7abb5e60..e2a859ae68 100644 --- a/twilight-http/src/error.rs +++ b/twilight-http/src/error.rs @@ -1,6 +1,5 @@ use crate::{api_error::ApiError, json::JsonError, response::StatusCode}; use std::{ - array::TryFromSliceError, error::Error as StdError, fmt::{Debug, Display, Formatter, Result as FmtResult}, str, @@ -31,13 +30,6 @@ impl Error { (self.kind, self.source) } - pub(super) fn multipart(source: TryFromSliceError) -> Self { - Self { - kind: ErrorType::Multipart, - source: Some(Box::new(source)), - } - } - pub(super) fn json(source: JsonError) -> Self { Self { kind: ErrorType::Json, @@ -63,7 +55,6 @@ impl Display for Error { f.write_str(" failed") } - ErrorType::Multipart => f.write_str("buffer could not get turned into a valid form"), ErrorType::Json => f.write_str("given value couldn't be serialized"), ErrorType::Parsing { body, .. } => { f.write_str("response body couldn't be deserialized: ")?; @@ -109,7 +100,6 @@ pub enum ErrorType { CreatingHeader { name: String, }, - Multipart, Json, Parsing { body: Vec, @@ -194,7 +184,6 @@ impl Debug for ErrorType { .debug_struct("CreatingHeader") .field("name", name) .finish(), - Self::Multipart => f.write_str("Multipart"), Self::Json => f.write_str("Json"), Self::Parsing { body } => { let mut debug = f.debug_struct("Parsing"); diff --git a/twilight-http/src/request/base.rs b/twilight-http/src/request/base.rs index daa2ff6e88..fd6039146d 100644 --- a/twilight-http/src/request/base.rs +++ b/twilight-http/src/request/base.rs @@ -90,20 +90,13 @@ impl RequestBuilder { self } - /// Set the the multipart form from an existing buffer. - /// - /// # Errors - /// - /// Returns an [`ErrorType::Multipart`] error type buffer input could not get turned into a - /// valid form. - /// - /// [`ErrorType::Multipart`]: crate::error::ErrorType::Multipart - pub fn multipart(mut self, form_buffer: Vec) -> Result { + /// Set the the multipart form from an existing boundary and buffer. + pub fn multipart(mut self, boundary: [u8; 15], buffer: Vec) -> Self { if let Ok(request) = self.0.as_mut() { - request.form = Some(Form::from_buffer(form_buffer).map_err(Error::multipart)?); + request.form = Some(Form::from_parts(boundary, buffer)); } - Ok(self) + self } /// Set the headers to add. diff --git a/twilight-http/src/request/multipart.rs b/twilight-http/src/request/multipart.rs index d3e97054ec..b53d6fba7b 100644 --- a/twilight-http/src/request/multipart.rs +++ b/twilight-http/src/request/multipart.rs @@ -1,5 +1,3 @@ -use std::array::TryFromSliceError; - #[derive(Clone, Debug)] #[must_use = "has no effect if not built into a Form"] pub struct Form { @@ -21,12 +19,8 @@ impl Form { Self::default() } - pub(crate) fn from_buffer(mut buffer: Vec) -> Result { - let boundary: [u8; 15] = buffer[(buffer.len() - 17)..(buffer.len() - 2)].try_into()?; - - buffer.truncate(buffer.len() - 2); - - Ok(Self { boundary, buffer }) + pub(crate) const fn from_parts(boundary: [u8; 15], buffer: Vec) -> Self { + Self { boundary, buffer } } /// Consume the form, returning the buffer's contents. @@ -116,6 +110,10 @@ impl Form { self } + + pub fn into_parts(self) -> ([u8; 15], Vec) { + (self.boundary, self.buffer) + } } impl Default for Form {