use std::fmt::Debug; use std::sync::Arc; use async_trait::async_trait; use twilight_model::channel::message::Component as TwilightComponent; use twilight_model::channel::message::component::{ ActionRow as TwilightActionRow, Button as TwilightButton, ButtonStyle, }; use crate::framework::interactions::InteractionError; use crate::framework::interactions::context::InteractionContext; type ComponentWithID = (Option, Arc>); pub struct ActionRow { components: Vec>, } impl ActionRow { pub fn new(components: &[ComponentWithID]) -> Self { Self { components: components.to_vec(), } } pub fn add(&mut self, id: Option, component: Arc>) { self.components.push((id, component)); } pub fn is_empty(&self) -> bool { self.components.is_empty() } pub fn build(self) -> TwilightComponent { TwilightComponent::ActionRow(TwilightActionRow { components: self .components .into_iter() .map(|(id, c)| c.build(id)) .collect(), }) } } #[async_trait] pub trait Component: Send + Sync + Debug { fn custom_id(&self) -> Option { Some(String::from("component")) } fn build(&self, id: Option) -> TwilightComponent; async fn execute(&self, ctx: InteractionContext) -> Result<(), InteractionError>; } #[async_trait] impl Component for T where T: Button, S: Send + Sync + 'static, E: Send + Sync + 'static, { fn custom_id(&self) -> Option { Button::id(self) } fn build(&self, id: Option) -> TwilightComponent { TwilightComponent::Button(TwilightButton { custom_id: id, disabled: self.disabled(), emoji: None, label: self.label().map(|i| i.to_string()), style: if self.url().is_none() { self.style() } else { ButtonStyle::Link }, url: self.url().map(|i| i.to_string()), sku_id: None, }) } async fn execute(&self, ctx: InteractionContext) -> Result<(), InteractionError> { self.on_press(ctx).await } } #[async_trait] pub trait Button: Send + Sync + Debug where S: Send + Sync + 'static, E: Send + Sync + 'static, { fn id(&self) -> Option { if self.url().is_none() { Some(String::from("Button")) } else { None } } fn style(&self) -> ButtonStyle { ButtonStyle::Secondary } fn label(&self) -> Option { None } fn url(&self) -> Option { None } fn disabled(&self) -> bool { false } async fn on_press(&self, ctx: InteractionContext) -> Result<(), InteractionError> { ctx.defer().await } }