Files
gitnow/crates/gitnow/src/components.rs
kjuulh 348e448ce9
All checks were successful
continuous-integration/drone/push Build is passing
feat: use termwiz as backend as that enables a ptty, which can be cleaned up nicely
2024-09-23 22:16:19 +02:00

120 lines
2.3 KiB
Rust

use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
pub mod inline_command;
pub mod spinner;
#[derive(Debug, PartialEq)]
pub enum Msg {
Quit,
Tick,
Success,
Failure(String),
}
pub struct Command {
func: Box<CommandFunc>,
}
impl Command {
pub fn new<T: FnOnce(&Dispatch) -> Option<Msg> + 'static>(f: T) -> Self {
Self { func: Box::new(f) }
}
pub fn execute(self, dispatch: &Dispatch) -> Option<Msg> {
(self.func)(dispatch)
}
}
pub trait IntoCommand {
fn into_command(self) -> Command;
}
impl IntoCommand for () {
fn into_command(self) -> Command {
Command::new(|_| None)
}
}
impl IntoCommand for Command {
fn into_command(self) -> Command {
self
}
}
impl IntoCommand for Msg {
fn into_command(self) -> Command {
Command::new(|_| Some(self))
}
}
type CommandFunc = dyn FnOnce(&Dispatch) -> Option<Msg>;
pub fn create_dispatch() -> (Dispatch, Receiver) {
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
(Dispatch { sender: tx }, Receiver { receiver: rx })
}
#[derive(Clone)]
pub struct Dispatch {
sender: UnboundedSender<Msg>,
}
impl Dispatch {
pub fn send(&self, msg: Msg) {
if let Err(e) = self.sender.send(msg) {
tracing::warn!("failed to send event: {}", e);
}
}
}
pub struct Receiver {
receiver: UnboundedReceiver<Msg>,
}
impl Receiver {
pub async fn next(&mut self) -> Option<Msg> {
self.receiver.recv().await
}
}
#[derive(Default)]
pub struct BatchCommand {
commands: Vec<Command>,
}
impl BatchCommand {
pub fn with(&mut self, cmd: impl IntoCommand) -> &mut Self {
self.commands.push(cmd.into_command());
self
}
}
impl IntoCommand for Vec<Command> {
fn into_command(self) -> Command {
BatchCommand::from(self).into_command()
}
}
impl From<Vec<Command>> for BatchCommand {
fn from(value: Vec<Command>) -> Self {
BatchCommand { commands: value }
}
}
impl IntoCommand for BatchCommand {
fn into_command(self) -> Command {
Command::new(|dispatch| {
for command in self.commands {
let msg = command.execute(dispatch);
if let Some(msg) = msg {
dispatch.send(msg);
}
}
None
})
}
}