mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2026-04-03 21:40:44 +02:00
102 lines
3.5 KiB
Rust
102 lines
3.5 KiB
Rust
use crate::{*, time::*, lang::*};
|
|
use ::std::{thread::JoinHandle, time::Duration};
|
|
#[cfg(feature = "term")] use ::crossterm::event::poll;
|
|
|
|
#[derive(Clone)] pub struct Exit(Arc<AtomicBool>);
|
|
|
|
impl Exit {
|
|
pub fn run <T> (run: impl Fn(Self)->Usually<T>) -> Usually<T> {
|
|
run(Self(Arc::new(AtomicBool::new(false))))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)] pub struct Thread {
|
|
/// Exit flag.
|
|
pub exit: Arc<AtomicBool>,
|
|
/// Performance counter.
|
|
pub perf: Arc<PerfModel>,
|
|
/// Use this to wait for the thread to finish.
|
|
pub join: JoinHandle<()>,
|
|
}
|
|
|
|
impl Thread {
|
|
/// Spawn a TUI thread that runs `callt least one, then repeats until `exit`.
|
|
pub fn new <F> (exit: Arc<AtomicBool>, call: F) -> Result<Self, std::io::Error>
|
|
where F: Fn(&PerfModel)->() + Send + Sync + 'static
|
|
{
|
|
let perf = Arc::new(PerfModel::default());
|
|
Ok(Self {
|
|
exit: exit.clone(),
|
|
perf: perf.clone(),
|
|
join: std::thread::Builder::new().name("tengri tui output".into()).spawn(move || {
|
|
while !exit.fetch_and(true, Relaxed) {
|
|
let _ = perf.cycle(&call);
|
|
}
|
|
})?.into()
|
|
})
|
|
}
|
|
|
|
/// Spawn a thread that runs `call` least one, then repeats
|
|
/// until `exit`, sleeping for `time` msec after every iteration.
|
|
pub fn new_sleep <F> (
|
|
exit: Arc<AtomicBool>, time: Duration, call: F
|
|
) -> Result<Self, std::io::Error>
|
|
where F: Fn(&PerfModel)->() + Send + Sync + 'static
|
|
{
|
|
Self::new(exit, move |perf| { let _ = call(perf); std::thread::sleep(time); })
|
|
}
|
|
|
|
/// Spawn a thread that uses [crossterm::event::poll]
|
|
/// to run `call` every `time` msec.
|
|
#[cfg(feature = "term")]pub fn new_poll <F> (
|
|
exit: Arc<AtomicBool>, time: Duration, call: F
|
|
) -> Result<Self, std::io::Error>
|
|
where F: Fn(&PerfModel)->() + Send + Sync + 'static
|
|
{
|
|
Self::new(exit, move |perf| { if poll(time).is_ok() { let _ = call(perf); } })
|
|
}
|
|
|
|
pub fn join (self) -> Result<(), Box<dyn std::any::Any + Send>> {
|
|
self.join.join()
|
|
}
|
|
}
|
|
|
|
/// Define an enum containing commands, and implement [Command] trait for over given `State`.
|
|
#[macro_export] macro_rules! def_command (
|
|
($Command:ident: |$state:ident: $State:ty| {
|
|
// FIXME: support attrs (docstrings)
|
|
$($Variant:ident$({$($arg:ident:$Arg:ty),+ $(,)?})?=>$body:expr),* $(,)?
|
|
})=>{
|
|
#[derive(Debug)] pub enum $Command {
|
|
// FIXME: support attrs (docstrings)
|
|
$($Variant $({ $($arg: $Arg),* })?),*
|
|
}
|
|
impl ::tengri::Command<$State> for $Command {
|
|
fn execute (&self, $state: &mut $State) -> Perhaps<Self> {
|
|
match self {
|
|
$(Self::$Variant $({ $($arg),* })? => $body,)*
|
|
_ => unimplemented!("Command<{}>: {self:?}", stringify!($State)),
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
/// Implement [Handle] for given `State` and `handler`.
|
|
#[macro_export] macro_rules! handle {
|
|
(|$self:ident:$State:ty,$input:ident|$handler:expr) => {
|
|
impl<E: Engine> ::tengri::Handle<E> for $State {
|
|
fn handle (&mut $self, $input: &E) -> Perhaps<E::Handled> {
|
|
$handler
|
|
}
|
|
}
|
|
};
|
|
($E:ty: |$self:ident:$State:ty,$input:ident|$handler:expr) => {
|
|
impl ::tengri::Handle<$E> for $State {
|
|
fn handle (&mut $self, $input: &$E) ->
|
|
Perhaps<<$E as ::tengri::Input>::Handled>
|
|
{
|
|
$handler
|
|
}
|
|
}
|
|
}
|
|
}
|