use std::{any::Any, fmt::Display};
use crate::types::{Container, Embed, InteractionResponse, MessageFlags};
use super::HandlerError;
pub trait IntoResponse: Sized {
fn into_response(self) -> InteractionResponse;
fn try_into_response(self) -> Result<InteractionResponse, HandlerError> {
Ok(self.into_response())
}
}
impl IntoResponse for InteractionResponse {
fn into_response(self) -> InteractionResponse {
self
}
}
impl IntoResponse for String {
fn into_response(self) -> InteractionResponse {
InteractionResponse::respond_with_message(self)
}
}
impl IntoResponse for &str {
fn into_response(self) -> InteractionResponse {
InteractionResponse::respond_with_message(self.to_string())
}
}
impl IntoResponse for Embed {
fn into_response(self) -> InteractionResponse {
InteractionResponse::respond_with_embed(self)
}
}
impl IntoResponse for Container {
fn into_response(self) -> InteractionResponse {
InteractionResponse::respond_with_components(vec![self.into()])
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ephemeral<T>(pub T);
impl<T: IntoResponse> IntoResponse for Ephemeral<T> {
fn into_response(self) -> InteractionResponse {
self.try_into_response()
.unwrap_or_else(|error| error.response())
}
fn try_into_response(self) -> Result<InteractionResponse, HandlerError> {
let mut response = self.0.try_into_response()?;
match &mut response {
InteractionResponse::ChannelMessageWithSource(data)
| InteractionResponse::UpdateMessage(data) => {
*data.flags.get_or_insert(MessageFlags::empty()) |= MessageFlags::Ephemeral;
}
InteractionResponse::DeferredChannelMessageWithSource(flags) => {
*flags.get_or_insert(MessageFlags::empty()) |= MessageFlags::Ephemeral;
}
_ => {}
}
Ok(response)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Update<T>(pub T);
impl<T: IntoResponse> IntoResponse for Update<T> {
fn into_response(self) -> InteractionResponse {
self.try_into_response()
.unwrap_or_else(|error| error.response())
}
fn try_into_response(self) -> Result<InteractionResponse, HandlerError> {
Ok(match self.0.try_into_response()? {
InteractionResponse::ChannelMessageWithSource(mut data) => {
data.flags = data
.flags
.map(|flags| flags - MessageFlags::Ephemeral)
.filter(|flags| !flags.is_empty());
InteractionResponse::UpdateMessage(data)
}
InteractionResponse::DeferredChannelMessageWithSource(_) => {
InteractionResponse::DeferredUpdateMessage
}
other => other,
})
}
}
pub trait CommandOutput {
fn into_result(self) -> Result<InteractionResponse, HandlerError>;
}
impl<T: IntoResponse> CommandOutput for T {
fn into_result(self) -> Result<InteractionResponse, HandlerError> {
self.try_into_response()
}
}
impl<T: IntoResponse, E: Display + 'static> CommandOutput for Result<T, E> {
fn into_result(self) -> Result<InteractionResponse, HandlerError> {
self.map_err(into_handler_error)?.try_into_response()
}
}
pub(crate) fn check_components_v2(response: &InteractionResponse) -> Result<(), HandlerError> {
let Some(data) = response.message_data() else {
return Ok(());
};
if !data.is_components_v2() {
return Ok(());
}
let field = if data
.content
.as_ref()
.is_some_and(|content| !content.is_empty())
{
"content"
} else if data
.embeds
.as_ref()
.is_some_and(|embeds| !embeds.is_empty())
{
"embeds"
} else if data.poll.is_some() {
"a poll"
} else {
return Ok(());
};
Err(HandlerError::new(format_args!(
"a Components V2 message cannot have {field}; use text displays instead"
)))
}
pub(crate) fn into_handler_error<E: Display + 'static>(error: E) -> HandlerError {
let mut error = Some(error);
match (&mut error as &mut dyn Any).downcast_mut::<Option<HandlerError>>() {
Some(handler_error) => handler_error.take().expect("taken once"),
None => HandlerError::new(error.expect("taken once")),
}
}