mirror of
https://github.com/fafhrd91/actix-web
synced 2024-11-27 17:52:56 +01:00
impl downcast_ref for MessageBody (#1287)
Co-authored-by: Yuki Okushi <huyuumi.dev@gmail.com>
This commit is contained in:
parent
b3f1071aaf
commit
245f96868a
@ -37,8 +37,12 @@ pub trait MessageBody {
|
|||||||
fn size(&self) -> BodySize;
|
fn size(&self) -> BodySize;
|
||||||
|
|
||||||
fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Bytes, Error>>>;
|
fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Bytes, Error>>>;
|
||||||
|
|
||||||
|
downcast_get_type_id!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downcast!(MessageBody);
|
||||||
|
|
||||||
impl MessageBody for () {
|
impl MessageBody for () {
|
||||||
fn size(&self) -> BodySize {
|
fn size(&self) -> BodySize {
|
||||||
BodySize::Empty
|
BodySize::Empty
|
||||||
@ -416,7 +420,10 @@ where
|
|||||||
S: Stream<Item = Result<Bytes, Error>>,
|
S: Stream<Item = Result<Bytes, Error>>,
|
||||||
{
|
{
|
||||||
pub fn new(size: u64, stream: S) -> Self {
|
pub fn new(size: u64, stream: S) -> Self {
|
||||||
SizedStream { size, stream: Box::pin(stream) }
|
SizedStream {
|
||||||
|
size,
|
||||||
|
stream: Box::pin(stream),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -643,4 +650,18 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_body_casting() {
|
||||||
|
let mut body = String::from("hello cast");
|
||||||
|
let resp_body: &mut dyn MessageBody = &mut body;
|
||||||
|
let body = resp_body.downcast_ref::<String>().unwrap();
|
||||||
|
assert_eq!(body, "hello cast");
|
||||||
|
let body = &mut resp_body.downcast_mut::<String>().unwrap();
|
||||||
|
body.push_str("!");
|
||||||
|
let body = resp_body.downcast_ref::<String>().unwrap();
|
||||||
|
assert_eq!(body, "hello cast!");
|
||||||
|
let not_body = resp_body.downcast_ref::<()>();
|
||||||
|
assert!(not_body.is_none());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
//! Error and Result module
|
//! Error and Result module
|
||||||
use std::any::TypeId;
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
@ -60,12 +59,6 @@ impl Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A struct with a private constructor, for use with
|
|
||||||
/// `__private_get_type_id__`. Its single field is private,
|
|
||||||
/// ensuring that it can only be constructed from this module
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub struct PrivateHelper(());
|
|
||||||
|
|
||||||
/// Error that can be converted to `Response`
|
/// Error that can be converted to `Response`
|
||||||
pub trait ResponseError: fmt::Debug + fmt::Display {
|
pub trait ResponseError: fmt::Debug + fmt::Display {
|
||||||
/// Response's status code
|
/// Response's status code
|
||||||
@ -89,43 +82,10 @@ pub trait ResponseError: fmt::Debug + fmt::Display {
|
|||||||
resp.set_body(Body::from(buf))
|
resp.set_body(Body::from(buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A helper method to get the type ID of the type
|
downcast_get_type_id!();
|
||||||
/// this trait is implemented on.
|
|
||||||
/// This method is unsafe to *implement*, since `downcast_ref` relies
|
|
||||||
/// on the returned `TypeId` to perform a cast.
|
|
||||||
///
|
|
||||||
/// Unfortunately, Rust has no notion of a trait method that is
|
|
||||||
/// unsafe to implement (marking it as `unsafe` makes it unsafe
|
|
||||||
/// to *call*). As a workaround, we require this method
|
|
||||||
/// to return a private type along with the `TypeId`. This
|
|
||||||
/// private type (`PrivateHelper`) has a private constructor,
|
|
||||||
/// making it impossible for safe code to construct outside of
|
|
||||||
/// this module. This ensures that safe code cannot violate
|
|
||||||
/// type-safety by implementing this method.
|
|
||||||
#[doc(hidden)]
|
|
||||||
fn __private_get_type_id__(&self) -> (TypeId, PrivateHelper)
|
|
||||||
where
|
|
||||||
Self: 'static,
|
|
||||||
{
|
|
||||||
(TypeId::of::<Self>(), PrivateHelper(()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl dyn ResponseError + 'static {
|
downcast!(ResponseError);
|
||||||
/// Downcasts a response error to a specific type.
|
|
||||||
pub fn downcast_ref<T: ResponseError + 'static>(&self) -> Option<&T> {
|
|
||||||
if self.__private_get_type_id__().0 == TypeId::of::<T>() {
|
|
||||||
// Safety: external crates cannot override the default
|
|
||||||
// implementation of `__private_get_type_id__`, since
|
|
||||||
// it requires returning a private type. We can therefore
|
|
||||||
// rely on the returned `TypeId`, which ensures that this
|
|
||||||
// case is correct.
|
|
||||||
unsafe { Some(&*(self as *const dyn ResponseError as *const T)) }
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
|
||||||
pub mod body;
|
pub mod body;
|
||||||
mod builder;
|
mod builder;
|
||||||
pub mod client;
|
pub mod client;
|
||||||
|
95
actix-http/src/macros.rs
Normal file
95
actix-http/src/macros.rs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#[macro_export]
|
||||||
|
macro_rules! downcast_get_type_id {
|
||||||
|
() => {
|
||||||
|
/// A helper method to get the type ID of the type
|
||||||
|
/// this trait is implemented on.
|
||||||
|
/// This method is unsafe to *implement*, since `downcast_ref` relies
|
||||||
|
/// on the returned `TypeId` to perform a cast.
|
||||||
|
///
|
||||||
|
/// Unfortunately, Rust has no notion of a trait method that is
|
||||||
|
/// unsafe to implement (marking it as `unsafe` makes it unsafe
|
||||||
|
/// to *call*). As a workaround, we require this method
|
||||||
|
/// to return a private type along with the `TypeId`. This
|
||||||
|
/// private type (`PrivateHelper`) has a private constructor,
|
||||||
|
/// making it impossible for safe code to construct outside of
|
||||||
|
/// this module. This ensures that safe code cannot violate
|
||||||
|
/// type-safety by implementing this method.
|
||||||
|
#[doc(hidden)]
|
||||||
|
fn __private_get_type_id__(&self) -> (std::any::TypeId, PrivateHelper)
|
||||||
|
where
|
||||||
|
Self: 'static,
|
||||||
|
{
|
||||||
|
(std::any::TypeId::of::<Self>(), PrivateHelper(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Generate implementation for dyn $name
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! downcast {
|
||||||
|
($name:ident) => {
|
||||||
|
/// A struct with a private constructor, for use with
|
||||||
|
/// `__private_get_type_id__`. Its single field is private,
|
||||||
|
/// ensuring that it can only be constructed from this module
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct PrivateHelper(());
|
||||||
|
|
||||||
|
impl dyn $name + 'static {
|
||||||
|
/// Downcasts generic body to a specific type.
|
||||||
|
pub fn downcast_ref<T: $name + 'static>(&self) -> Option<&T> {
|
||||||
|
if self.__private_get_type_id__().0 == std::any::TypeId::of::<T>() {
|
||||||
|
// Safety: external crates cannot override the default
|
||||||
|
// implementation of `__private_get_type_id__`, since
|
||||||
|
// it requires returning a private type. We can therefore
|
||||||
|
// rely on the returned `TypeId`, which ensures that this
|
||||||
|
// case is correct.
|
||||||
|
unsafe { Some(&*(self as *const dyn $name as *const T)) }
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Downcasts a generic body to a mutable specific type.
|
||||||
|
pub fn downcast_mut<T: $name + 'static>(&mut self) -> Option<&mut T> {
|
||||||
|
if self.__private_get_type_id__().0 == std::any::TypeId::of::<T>() {
|
||||||
|
// Safety: external crates cannot override the default
|
||||||
|
// implementation of `__private_get_type_id__`, since
|
||||||
|
// it requires returning a private type. We can therefore
|
||||||
|
// rely on the returned `TypeId`, which ensures that this
|
||||||
|
// case is correct.
|
||||||
|
unsafe {
|
||||||
|
Some(&mut *(self as *const dyn $name as *const T as *mut T))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
trait MB {
|
||||||
|
downcast_get_type_id!();
|
||||||
|
}
|
||||||
|
|
||||||
|
downcast!(MB);
|
||||||
|
|
||||||
|
impl MB for String {}
|
||||||
|
impl MB for () {}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_any_casting() {
|
||||||
|
let mut body = String::from("hello cast");
|
||||||
|
let resp_body: &mut dyn MB = &mut body;
|
||||||
|
let body = resp_body.downcast_ref::<String>().unwrap();
|
||||||
|
assert_eq!(body, "hello cast");
|
||||||
|
let body = &mut resp_body.downcast_mut::<String>().unwrap();
|
||||||
|
body.push_str("!");
|
||||||
|
let body = resp_body.downcast_ref::<String>().unwrap();
|
||||||
|
assert_eq!(body, "hello cast!");
|
||||||
|
let not_body = resp_body.downcast_ref::<()>();
|
||||||
|
assert!(not_body.is_none());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user