composure 0.2.4

Discord bots over HTTP Interactions, no gateway: slash commands, components, modals and JSX-like views, on Cloudflare Workers (WASM) or any server
Documentation
//! Component and Modal Handlers, routed by `custom_id` prefix.

use serde::de::DeserializeOwned;

use crate::types::InteractionResponse;

use super::{ComponentCtx, HandlerError, ModalCtx, custom_id};

/// The `custom_id` prefix a Component Handler answers. Implemented by `#[component]`.
pub trait ComponentDefinition {
    /// The `custom_id` prefix: what comes before the first `:`
    const PREFIX: &'static str;
}

/// Answers clicks and selections on components whose `custom_id` starts with its prefix.
/// Implemented by `#[component]`; listed in an `#[app]`.
pub trait ComponentHandler<S>: ComponentDefinition {
    /// `fields` is the `custom_id` after `prefix:`, `None` when there is no `:`
    fn invoke(
        ctx: ComponentCtx<'_, S>,
        fields: Option<&str>,
    ) -> impl Future<Output = Result<InteractionResponse, HandlerError>> + Send;
}

/// The `custom_id` prefix a Modal Handler answers. Implemented by `#[modal]`.
pub trait ModalDefinition {
    /// The `custom_id` prefix: what comes before the first `:`
    const PREFIX: &'static str;
}

/// Answers submits of modals whose `custom_id` starts with its prefix. Implemented by
/// `#[modal]`; listed in an `#[app]`.
pub trait ModalHandler<S>: ModalDefinition {
    /// Decodes the state and the form, runs the handler; `fields` is the `custom_id` after
    /// `prefix:`
    fn invoke(
        ctx: ModalCtx<'_, S>,
        fields: Option<&str>,
    ) -> impl Future<Output = Result<InteractionResponse, HandlerError>> + Send;
}

/// Decodes a handler's `custom_id` state; used by `#[component]` and `#[modal]`
#[doc(hidden)]
pub fn decode_state<T: DeserializeOwned>(
    prefix: &str,
    fields: Option<&str>,
) -> Result<T, HandlerError> {
    custom_id::decode(fields).map_err(|e| HandlerError::new(format!("`{prefix}` {e}")))
}