lunar-lib 0.10.0

Common utilities for lunar applications
Documentation
use std::fmt;

use crossbeam::channel::{Receiver, Sender};

pub struct Executor<T> {
    rx: Receiver<Box<dyn FnOnce(&mut T) + Send>>,
}

type SyncFn<T> = Box<dyn FnOnce(&mut T) + Send>;

impl<T> std::ops::Deref for Executor<T> {
    type Target = Receiver<SyncFn<T>>;

    fn deref(&self) -> &Self::Target {
        &self.rx
    }
}

pub struct Runner<T> {
    tx: Sender<SyncFn<T>>,
}

impl<T> Clone for Runner<T> {
    fn clone(&self) -> Self {
        Self {
            tx: self.tx.clone(),
        }
    }
}

pub fn channel<T>() -> (Runner<T>, Executor<T>) {
    let (tx, rx) = crossbeam::channel::unbounded();
    (Runner { tx }, Executor { rx })
}

#[derive(Debug)]
pub struct DispatchError;

impl fmt::Display for DispatchError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("The channel is closed")
    }
}

impl std::error::Error for DispatchError {}

impl<T> Runner<T> {
    pub fn dispatch_command<R: Send + 'static>(
        &self,
        f: impl FnOnce(&mut T) -> R + Send + 'static,
    ) -> Result<R, DispatchError> {
        let (cb, rx) = oneshot::channel();
        self.tx
            .send(Box::new(move |s| {
                let _ = cb.send(f(s));
            }))
            .map_err(|_| DispatchError)?;
        rx.recv().map_err(|_| DispatchError)
    }

    pub fn dispatch_event(
        &self,
        f: impl FnOnce(&mut T) + Send + 'static,
    ) -> Result<(), DispatchError> {
        self.tx.send(Box::new(f)).map_err(|_| DispatchError)
    }
}

#[macro_export]
macro_rules! make_runner_command_ext {
    ($parent:ty => $vis:vis trait $ext:ident {
        $(
            fn $fn:ident(&mut $self:ident $(,$field:ident : $ty:ty)*) -> $ret:ty $body:block

        )*
    }) => {
        $vis trait $ext {
            $(
                fn $fn(&self, $($field : $ty),*) -> Result<$ret, $crate::runners::DispatchError>;
            )*
        }

        impl $parent {
            $(
                $vis fn $fn(&mut $self, $($field : $ty),*) -> $ret {
                    $body
                }
            )*
        }

        impl $ext for $crate::runners::Runner<$parent> {
            $(
                fn $fn(&self, $($field : $ty),*) -> Result<$ret, $crate::runners::DispatchError> {
                    self.dispatch_command(move |s| s.$fn($($field),*))
                }
            )*
        }
    };
}

#[macro_export]
macro_rules! make_runner_event_ext {
    ($parent:ty => $vis:vis trait $ext:ident {
        $(
            fn $fn:ident(&mut $self:ident $(,$field:ident : $ty:ty)*) $body:block
        )*
    }) => {
        $vis trait $ext {
            $(
                fn $fn(&self, $($field : $ty),*) -> Result<(), $crate::runners::DispatchError>;
            )*
        }

        impl $parent {
            $(
                $vis fn $fn(&mut $self, $($field : $ty),*) {
                    $body
                }
            )*
        }

        impl $ext for $crate::runners::Runner<$parent> {
            $(
                fn $fn(&self, $($field : $ty),*) -> Result<(), $crate::runners::DispatchError> {
                    self.dispatch_event(move |s| s.$fn($($field),*))
                }
            )*
        }
    };
}

pub fn spawn_background_thread<F1, F2, T>(f: F1, on_return: Option<F2>)
where
    F1: FnOnce() -> T + Send + 'static,
    F2: FnOnce(T) + Send + 'static,
{
    std::thread::spawn(move || {
        if let Some(on_return) = on_return {
            on_return(f())
        } else {
            f();
        }
    });
}