use std::{
cell::Cell,
fmt::{Display, Write},
};
use crate::types::{
Attachment, Component, InteractionResponse, MessageComponentData, ModalCallbackData,
PartialChannel, PartialEmoji, Role, Snowflake, User,
};
use super::{
CommandOutput, ComponentCtx, HandlerError, IntoResponse, ModalChoice, ModalCtx, Update,
custom_id::CustomIdError,
};
#[derive(Debug, Clone, Default)]
#[must_use]
pub struct View {
components: Vec<Component>,
error: Option<CustomIdError>,
}
impl View {
pub fn new(components: Vec<Component>) -> Self {
Self {
components,
error: None,
}
}
pub fn components(&self) -> &[Component] {
&self.components
}
pub fn into_components(self) -> Result<Vec<Component>, CustomIdError> {
match self.error {
Some(error) => Err(error),
None => Ok(self.components),
}
}
}
impl From<Vec<Component>> for View {
fn from(components: Vec<Component>) -> Self {
Self::new(components)
}
}
impl IntoResponse for View {
fn into_response(self) -> InteractionResponse {
self.try_into_response()
.unwrap_or_else(|error| error.response())
}
fn try_into_response(self) -> Result<InteractionResponse, HandlerError> {
self.into_components()
.map(InteractionResponse::respond_with_components)
.map_err(HandlerError::new)
}
}
#[derive(Debug, Clone)]
#[must_use]
pub struct ModalView {
data: ModalCallbackData,
error: Option<CustomIdError>,
}
impl ModalView {
pub fn data(&self) -> &ModalCallbackData {
&self.data
}
pub fn into_data(self) -> Result<ModalCallbackData, CustomIdError> {
match self.error {
Some(error) => Err(error),
None => Ok(self.data),
}
}
}
impl IntoResponse for ModalView {
fn into_response(self) -> InteractionResponse {
self.try_into_response()
.unwrap_or_else(|error| error.response())
}
fn try_into_response(self) -> Result<InteractionResponse, HandlerError> {
self.into_data()
.map(InteractionResponse::Modal)
.map_err(HandlerError::new)
}
}
#[doc(hidden)]
#[derive(Debug, Default)]
pub struct Children {
components: Vec<Component>,
text: Option<String>,
error: Option<CustomIdError>,
}
impl Children {
pub fn text(&mut self, text: &str) {
self.text.get_or_insert_with(String::new).push_str(text);
}
pub fn component(&mut self, component: Component) {
self.flush();
self.components.push(component);
}
pub fn error(&mut self, error: Option<CustomIdError>) {
if self.error.is_none() {
self.error = error;
}
}
pub fn id(&mut self, custom_id: Result<String, CustomIdError>) -> String {
custom_id.unwrap_or_else(|error| {
self.error(Some(error));
String::new()
})
}
pub fn nested(&mut self, mut children: Children) -> Vec<Component> {
children.flush();
self.error(children.error);
children.components
}
pub fn take_text(&mut self) -> String {
self.text.take().unwrap_or_default()
}
fn flush(&mut self) {
if let Some(text) = self.text.take() {
self.components.push(Component::text(text));
}
}
pub fn view(mut self) -> View {
self.flush();
View {
components: self.components,
error: self.error,
}
}
pub fn modal(mut self, mut data: ModalCallbackData) -> ModalView {
self.flush();
data.components = Some(self.components);
ModalView {
data,
error: self.error,
}
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be placed in `rsx!` markup",
note = "use text (&str, String, numbers), a Component, a View, a #[view]'s Props, or an Option, Vec, or iterator of them; collect other iterators into a Vec"
)]
pub trait IntoChildren {
fn push_into(self, children: &mut Children);
}
impl IntoChildren for View {
fn push_into(self, children: &mut Children) {
children.error(self.error);
for component in self.components {
children.component(component);
}
}
}
impl IntoChildren for Component {
fn push_into(self, children: &mut Children) {
children.component(self);
}
}
macro_rules! into_children_component {
($($ty:ty),*) => {$(
impl IntoChildren for $ty {
fn push_into(self, children: &mut Children) {
children.component(self.into());
}
}
)*};
}
into_children_component!(
crate::types::ActionRow,
crate::types::ButtonComponent,
crate::types::Container,
crate::types::Section,
crate::types::TextDisplay,
crate::types::Thumbnail,
crate::types::MediaGallery,
crate::types::FileComponent,
crate::types::Separator,
crate::types::Label
);
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be placed in text",
note = "use &str, String, a number, or an Option of one; call `.to_string()` on other Display types"
)]
pub trait IntoText {
fn push_text(self, text: &mut String);
}
impl IntoText for &str {
fn push_text(self, text: &mut String) {
text.push_str(self);
}
}
impl IntoText for String {
fn push_text(self, text: &mut String) {
text.push_str(&self);
}
}
impl IntoText for &String {
fn push_text(self, text: &mut String) {
text.push_str(self);
}
}
impl<T: IntoText> IntoText for Option<T> {
fn push_text(self, text: &mut String) {
if let Some(value) = self {
value.push_text(text);
}
}
}
macro_rules! text {
($($ty:ty),*) => {$(
impl IntoText for $ty {
fn push_text(self, text: &mut String) {
let _ = write!(text, "{self}");
}
}
)*};
}
text!(
char,
u8,
u16,
u32,
u64,
u128,
usize,
i8,
i16,
i32,
i64,
i128,
isize,
f32,
f64,
Snowflake,
std::fmt::Arguments<'_>
);
macro_rules! into_children_text {
($($ty:ty),*) => {$(
impl IntoChildren for $ty {
fn push_into(self, children: &mut Children) {
let text = children.text.get_or_insert_with(String::new);
IntoText::push_text(self, text);
}
}
)*};
}
into_children_text!(
&str,
String,
&String,
char,
u8,
u16,
u32,
u64,
u128,
usize,
i8,
i16,
i32,
i64,
i128,
isize,
f32,
f64,
Snowflake,
std::fmt::Arguments<'_>
);
impl<T: IntoChildren> IntoChildren for Option<T> {
fn push_into(self, children: &mut Children) {
if let Some(child) = self {
child.push_into(children);
}
}
}
impl<T: IntoChildren> IntoChildren for Vec<T> {
fn push_into(self, children: &mut Children) {
for child in self {
child.push_into(children);
}
}
}
impl<T: IntoChildren, const N: usize> IntoChildren for [T; N] {
fn push_into(self, children: &mut Children) {
for child in self {
child.push_into(children);
}
}
}
macro_rules! into_children_iter {
($($ty:ident<$($param:ident),*>),*) => {$(
impl<$($param),*> IntoChildren for ::core::iter::$ty<$($param),*>
where
::core::iter::$ty<$($param),*>: Iterator<Item: IntoChildren>,
{
fn push_into(self, children: &mut Children) {
for child in self {
child.push_into(children);
}
}
}
)*};
}
into_children_iter!(
Map<I, F>,
Filter<I, P>,
FilterMap<I, F>,
Chain<A, B>,
Take<I>,
Skip<I>,
Rev<I>,
Once<T>
);
impl<I: Iterator, U: IntoIterator<Item: IntoChildren>, F: FnMut(I::Item) -> U> IntoChildren
for ::core::iter::FlatMap<I, U, F>
{
fn push_into(self, children: &mut Children) {
for child in self {
child.push_into(children);
}
}
}
impl<I: Iterator<Item: IntoIterator<Item: IntoChildren>>> IntoChildren
for ::core::iter::Flatten<I>
{
fn push_into(self, children: &mut Children) {
for child in self {
child.push_into(children);
}
}
}
impl<T: IntoChildren> IntoChildren for std::vec::IntoIter<T> {
fn push_into(self, children: &mut Children) {
for child in self {
child.push_into(children);
}
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a valid value for this attribute",
note = "the attribute expects `{T}`"
)]
pub trait IntoAttr<T> {
fn into_attr(self) -> T;
}
impl<T> IntoAttr<T> for T {
fn into_attr(self) -> T {
self
}
}
impl<T> IntoAttr<Option<T>> for T {
fn into_attr(self) -> Option<T> {
Some(self)
}
}
impl IntoAttr<String> for &str {
fn into_attr(self) -> String {
self.to_string()
}
}
impl IntoAttr<Option<String>> for &str {
fn into_attr(self) -> Option<String> {
Some(self.to_string())
}
}
impl IntoAttr<String> for &String {
fn into_attr(self) -> String {
self.clone()
}
}
impl IntoAttr<Option<String>> for &String {
fn into_attr(self) -> Option<String> {
Some(self.clone())
}
}
impl IntoAttr<Option<PartialEmoji>> for &str {
fn into_attr(self) -> Option<PartialEmoji> {
Some(PartialEmoji {
id: None,
name: Some(self.to_string()),
animated: None,
})
}
}
#[diagnostic::on_unimplemented(message = "`{Self}` is not a message View")]
pub trait Rerender {
fn rerender(self) -> Result<InteractionResponse, HandlerError>;
}
impl Rerender for View {
fn rerender(self) -> Result<InteractionResponse, HandlerError> {
Update(self).try_into_response()
}
}
impl<T: Rerender, E: Display + 'static> Rerender for Result<T, E> {
fn rerender(self) -> Result<InteractionResponse, HandlerError> {
self.map_err(super::response::into_handler_error)?
.rerender()
}
}
#[doc(hidden)]
pub struct HandlerOutput<T>(Cell<Option<T>>);
impl<T> HandlerOutput<T> {
pub fn new(output: T) -> Self {
Self(Cell::new(Some(output)))
}
fn take(&self) -> T {
self.0.take().expect("handler output answered once")
}
}
#[doc(hidden)]
pub trait ViaRerender {
fn answer(&self) -> Result<InteractionResponse, HandlerError>;
}
impl<T: Rerender> ViaRerender for &HandlerOutput<T> {
fn answer(&self) -> Result<InteractionResponse, HandlerError> {
self.take().rerender()
}
}
#[doc(hidden)]
pub trait ViaOutput {
fn answer(&self) -> Result<InteractionResponse, HandlerError>;
}
impl<T: CommandOutput> ViaOutput for HandlerOutput<T> {
fn answer(&self) -> Result<InteractionResponse, HandlerError> {
self.take().into_result()
}
}
#[derive(Debug, Clone, Copy)]
pub struct ViewInfo {
pub prefix: &'static str,
pub handled: bool,
pub views: &'static [ViewInfo],
}
pub trait ViewDefinition {
const INFO: ViewInfo;
}
pub trait ViewHandler<S>: ViewDefinition {
fn component(
_ctx: ComponentCtx<'_, S>,
_fields: Option<&str>,
) -> impl Future<Output = Option<Result<InteractionResponse, HandlerError>>> + Send {
async { None }
}
fn modal(
_ctx: ModalCtx<'_, S>,
_fields: Option<&str>,
) -> impl Future<Output = Option<Result<InteractionResponse, HandlerError>>> + Send {
async { None }
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot hold select values",
note = "use String, Snowflake, User, Role, PartialChannel, Attachment, or a #[derive(Choice)] enum, optionally in Option or Vec"
)]
pub trait SelectValue: Sized {
const MANY: bool = false;
fn extract(data: &MessageComponentData) -> Result<Self, String>;
}
fn selected(data: &MessageComponentData) -> &[String] {
data.values.as_deref().unwrap_or_default()
}
macro_rules! select_value {
($ty:ty, |$value:ident, $data:ident| $parse:expr) => {
impl SelectValue for $ty {
fn extract(data: &MessageComponentData) -> Result<Self, String> {
let parse =
|$value: &str, $data: &MessageComponentData| -> Result<$ty, String> { $parse };
match selected(data).first() {
Some(value) => parse(value, data),
None => Err("nothing selected".to_string()),
}
}
}
impl SelectValue for Option<$ty> {
fn extract(data: &MessageComponentData) -> Result<Self, String> {
match selected(data).first() {
Some(_) => <$ty as SelectValue>::extract(data).map(Some),
None => Ok(None),
}
}
}
impl SelectValue for Vec<$ty> {
const MANY: bool = true;
fn extract(data: &MessageComponentData) -> Result<Self, String> {
let parse =
|$value: &str, $data: &MessageComponentData| -> Result<$ty, String> { $parse };
selected(data)
.iter()
.map(|value| parse(value, data))
.collect()
}
}
};
}
select_value!(String, |value, _data| Ok(value.to_string()));
select_value!(Snowflake, |value, _data| value
.parse()
.map_err(|_| format!("`{value}` is not an id")));
select_value!(User, |value, data| resolve(value, data, |r| r
.users
.as_ref()));
select_value!(Role, |value, data| resolve(value, data, |r| r
.roles
.as_ref()));
select_value!(PartialChannel, |value, data| resolve(value, data, |r| r
.channels
.as_ref()));
select_value!(Attachment, |value, data| resolve(value, data, |r| r
.attachments
.as_ref()));
fn resolve<T: Clone>(
value: &str,
data: &MessageComponentData,
get: impl Fn(&crate::types::ResolvedData) -> Option<&crate::types::SnowflakeMap<T>>,
) -> Result<T, String> {
let id: Snowflake = value
.parse()
.map_err(|_| format!("`{value}` is not an id"))?;
data.resolved
.as_ref()
.and_then(get)
.and_then(|map| map.get(&id))
.cloned()
.ok_or_else(|| format!("{id} is not in the resolved data"))
}
fn choice<C: ModalChoice>(value: &str) -> Result<C, String> {
C::from_modal_value(value).ok_or_else(|| format!("`{value}` is not a choice"))
}
impl<C: ModalChoice> SelectValue for C {
fn extract(data: &MessageComponentData) -> Result<Self, String> {
match selected(data).first() {
Some(value) => choice(value),
None => Err("nothing selected".to_string()),
}
}
}
impl<C: ModalChoice> SelectValue for Option<C> {
fn extract(data: &MessageComponentData) -> Result<Self, String> {
selected(data)
.first()
.map(|value| choice(value))
.transpose()
}
}
impl<C: ModalChoice> SelectValue for Vec<C> {
const MANY: bool = true;
fn extract(data: &MessageComponentData) -> Result<Self, String> {
selected(data).iter().map(|value| choice(value)).collect()
}
}
#[doc(hidden)]
pub fn select_value<T: SelectValue>(data: &MessageComponentData) -> Result<T, HandlerError> {
T::extract(data).map_err(|e| HandlerError::invalid_option(&data.custom_id, e))
}
#[doc(hidden)]
pub fn modal_value<T: super::ModalValue>(
data: &crate::types::ModalSubmitData,
name: &str,
) -> Result<T, HandlerError> {
T::extract(data.find(name), data).map_err(|e| HandlerError::invalid_option(name, e))
}
#[doc(hidden)]
pub fn choice_options<C: ModalChoice>() -> &'static [(&'static str, &'static str)] {
C::OPTIONS
}
#[doc(hidden)]
pub const fn required<T: super::ModalValue>() -> Option<bool> {
if T::REQUIRED { None } else { Some(false) }
}