podo-core-driver 0.4.4

Podo Driver FFI
Documentation
use std::any::Any;
use std::sync::{mpsc, Mutex};
use std::thread;

use super::super::state::StateFlag;
use super::message::{Message, Response};
use crate::error::RuntimeError;

pub struct Controller<M>
where
    M: 'static + PartialEq + Eq + Send + Sync,
{
    tx: Mutex<mpsc::Sender<Message<M>>>,
    rx: Mutex<mpsc::Receiver<Response>>,

    pub(super) t: Mutex<Option<thread::JoinHandle<Result<(), RuntimeError>>>>,
    pub(super) state: StateFlag<ControllerState>,
}

impl<M> Controller<M>
where
    M: 'static + PartialEq + Eq + Send + Sync,
{
    #[inline]
    pub(super) fn new(
        tx: mpsc::Sender<Message<M>>,
        rx: mpsc::Receiver<Response>,
        t: thread::JoinHandle<Result<(), RuntimeError>>,
    ) -> Self {
        Self {
            tx: Mutex::new(tx),
            rx: Mutex::new(rx),
            t: Mutex::new(Some(t)),
            state: StateFlag::default(),
        }
    }

    #[inline]
    pub fn wait(&self) -> Result<(), RuntimeError> {
        match self.t.lock().unwrap().take() {
            Some(t) => match t.join() {
                Ok(r) => r,
                Err(_) => RuntimeError::unexpected(),
            },
            None => RuntimeError::expect("Already stopped"),
        }
    }

    #[inline]
    pub fn stop(&self) -> Result<(), RuntimeError> {
        self.state.stop()?;
        self.send(Message::Stop)?;
        match self.t.lock().unwrap().take().unwrap().join() {
            Ok(r) => r,
            Err(_) => RuntimeError::unexpected(),
        }
    }

    #[inline]
    pub fn pause(&self) -> Result<(), RuntimeError> {
        self.set_state(ControllerState::Running, ControllerState::Paused)?;
        self.send(Message::Pause)
    }

    #[inline]
    pub fn resume(&self) -> Result<(), RuntimeError> {
        self.set_state(ControllerState::Paused, ControllerState::Running)?;
        self.send(Message::Resume)
    }

    #[inline]
    pub fn hibernate(&self) -> Result<(), RuntimeError> {
        self.set_state(ControllerState::Running, ControllerState::Hibernated)?;
        self.send(Message::Hibernate)
    }

    #[inline]
    pub fn wake_up(&self) -> Result<(), RuntimeError> {
        self.set_state(ControllerState::Hibernated, ControllerState::Running)?;
        self.send(Message::WakeUp)
    }

    #[inline]
    pub fn request<R>(&self, m: M) -> Result<R, RuntimeError>
    where
        R: 'static,
    {
        self.assert_state(ControllerState::Running)?;
        self.send(Message::Custom(m))
    }

    #[inline]
    fn send<R>(&self, m: Message<M>) -> Result<R, RuntimeError>
    where
        R: 'static,
    {
        let (tx, rx) = {
            // rx then tx
            let rx = self.rx.lock().unwrap();
            let tx = self.tx.lock().unwrap();
            (tx, rx)
        };
        tx.send(m)?;
        let m = match (rx.recv()? as Box<dyn Any>).downcast() {
            Ok(m) => Ok(*m),
            Err(_) => RuntimeError::unexpected(),
        };
        {
            // tx then rx
            drop(tx);
            drop(rx);
        }
        m
    }

    #[inline]
    fn assert_state(&self, state: ControllerState) -> Result<(), RuntimeError> {
        match self.state.status()? == state {
            true => Ok(()),
            false => RuntimeError::expect("Unexpected flag"),
        }
    }

    #[inline]
    fn set_state(&self, last: ControllerState, now: ControllerState) -> Result<(), RuntimeError> {
        self.assert_state(last)?;
        self.state.start(now)
    }
}

impl<M> Drop for Controller<M>
where
    M: 'static + PartialEq + Eq + Send + Sync,
{
    fn drop(&mut self) {
        self.stop().unwrap();
    }
}

#[derive(PartialEq, Eq)]
pub enum ControllerState {
    Running,
    Paused,
    Hibernated,
}

impl Default for ControllerState {
    #[inline]
    fn default() -> Self {
        Self::Running
    }
}

impl From<usize> for ControllerState {
    #[inline]
    fn from(state: usize) -> Self {
        match state {
            1 => Self::Paused,
            2 => Self::Hibernated,
            0 | _ => Self::Running,
        }
    }
}

impl Into<usize> for ControllerState {
    #[inline]
    fn into(self) -> usize {
        match self {
            Self::Running => 0,
            Self::Paused => 1,
            Self::Hibernated => 2,
        }
    }
}