tuyere 0.1.0

A powerful TUI framework based on The Elm Architecture 🔮
Documentation
//! Commands for side effects.

use std::future::Future;
use std::pin::Pin;

/// A command that can be returned from `update`.
///
/// Commands represent side effects that should happen after
/// updating the model.
pub struct Command<M> {
    pub(crate) kind: CommandKind<M>,
}

pub(crate) enum CommandKind<M> {
    /// No operation.
    None,
    /// Quit the application.
    Quit,
    /// A batch of commands.
    Batch(Vec<Command<M>>),
    /// An async task that produces a message.
    #[allow(dead_code)]
    Task(Pin<Box<dyn Future<Output = M> + Send + 'static>>),
    /// A simple message to send.
    Message(M),
}

impl<M> Command<M> {
    /// Create a command that does nothing.
    #[must_use]
    pub fn none() -> Self {
        Self {
            kind: CommandKind::None,
        }
    }

    /// Create a command that quits the application.
    #[must_use]
    pub fn quit() -> Self {
        Self {
            kind: CommandKind::Quit,
        }
    }

    /// Create a command that sends a message.
    #[must_use]
    pub fn message(msg: M) -> Self {
        Self {
            kind: CommandKind::Message(msg),
        }
    }

    /// Batch multiple commands together.
    #[must_use]
    pub fn batch(commands: Vec<Command<M>>) -> Self {
        Self {
            kind: CommandKind::Batch(commands),
        }
    }

    /// Create a command from an async task.
    ///
    /// The task will be spawned and its result will be sent as a message.
    pub fn perform<F, Fut>(task: F) -> Self
    where
        F: FnOnce() -> Fut,
        Fut: Future<Output = M> + Send + 'static,
        M: Send + 'static,
    {
        Self {
            kind: CommandKind::Task(Box::pin(task())),
        }
    }

    /// Check if this is a quit command.
    #[must_use]
    pub fn is_quit(&self) -> bool {
        matches!(self.kind, CommandKind::Quit)
    }

    /// Check if this is a no-op command.
    #[must_use]
    pub fn is_none(&self) -> bool {
        matches!(self.kind, CommandKind::None)
    }
}

impl<M> Default for Command<M> {
    fn default() -> Self {
        Self::none()
    }
}

/// Alias for Command (matches bubbletea naming).
pub type Cmd<M> = Command<M>;