keebrs 0.3.0

Keyboard firmware building blocks
//! Core routines for keyboard operation

use alloc::string::String;

use core::fmt::Debug;

use log::info;

use futures::{
    future::{
        select,
        Either,
    },
    prelude::*,
};

use crate::{
    key::ModuleKey,
    locator::Locator,
    net::{
        self,
        *,
    },
    serial::Msg,
    stateful::{
        ScanStateful,
        ScanStatefulExt,
    },
    translate::{
        Translate,
        Translated,
        Translator,
    },
};

enum UsbOut<U, O> {
    Invalid,
    Disconnected(U),
    Connected(O),
}

impl<U, O> UsbOut<U, O> {
    fn fut(&mut self) -> U {
        match core::mem::replace(self, UsbOut::Invalid) {
            Self::Disconnected(u) => u,
            _ => panic!("Expected disconnected usb output"),
        }
    }
    fn sink(&mut self) -> O {
        match core::mem::replace(self, UsbOut::Invalid) {
            Self::Connected(o) => o,
            _ => panic!("Expected connected usb output"),
        }
    }
}

/// The core keyboard manager
pub struct Core<N, U, T, S, O> {
    my_type: String,
    translator: Translator,
    locator: Locator,
    net: N,
    usb: UsbOut<U, O>,
    timer: T,
    scanner: S,
}

impl<N, U, T, S, O> Core<N, U, T, S, O>
where
    T: Stream + Unpin,
    S: ScanStateful + Unpin,
{
    /// Create a new keyboard core
    pub fn new(
        my_type: &str,
        translator: Translator,
        net: N,
        usb_sink: U,
        timer: T,
        scanner: S,
    ) -> Core<N, U, T, S, O>
    where
        U: Future<Output = O>,
    {
        let locator = Locator::new(translator.layout.modules.iter().map(|b| b.ty));
        Core {
            my_type: my_type.into(),
            translator,
            locator,
            timer,
            usb: UsbOut::Disconnected(usb_sink),
            scanner,
            net,
        }
    }
    /// Run in secondary mode
    pub async fn run(&mut self)
    where
        U: Future<Output = O> + Unpin,
        O: Sink<Translated> + Unpin,
        N: Net<Msg> + Unpin,
        <N as Sink<net::Msg<Msg>>>::Error: Debug,
    {
        let usb = self.usb.fut();
        match select(usb, self.net.try_next().map_ok(|o| o.map(|m| m.unpack()))).await {
            Either::Left((sink, _)) => {
                self.usb = UsbOut::Connected(sink);
                // Spin for a little bit to make sure the other modules are up.
                for _ in 0..5 {
                    self.timer.next().await;
                }
                self.run_primary().await
            }
            Either::Right((Ok(Some((_, Msg::Init))), _)) => self.run_secondary().await,
            _ => panic!("Unexpected message while waiting for init"),
        }
    }

    /// Run in secondary mode
    pub async fn run_secondary(&mut self)
    where
        N: NetSend<Msg> + NetRecv<Msg> + Unpin,
    {
        info!("Running secondary as {}", self.my_type);
        let _ = self
            .net
            .send_to(0, Msg::ModuleType(self.my_type.clone()))
            .await;
        loop {
            let incoming = self.net.try_next().map_ok(|o| o.map(|m| m.unpack()));
            let timer = self.timer.next();
            match select(incoming, timer).await {
                Either::Right(_) => {
                    let key = self.scanner.change().await;
                    if let Some(key) = key {
                        let _ = self.net.send_to(0, Msg::KeyEvent(key)).await;
                    } else {
                        continue;
                    }
                }
                Either::Left((Ok(Some((_, Msg::Init))), _)) => {
                    let _ = self
                        .net
                        .send_to(0, Msg::ModuleType(self.my_type.clone()))
                        .await;
                }
                _ => continue,
            };
        }
    }

    /// Run in primary mode
    pub async fn run_primary(&mut self)
    where
        O: Sink<Translated> + Unpin,
        N: Net<Msg> + Unpin,
        <N as Sink<net::Msg<Msg>>>::Error: Debug,
    {
        let mut sink = self.usb.sink();
        self.locator.add_module(0, self.my_type.clone());
        let _ = self
            .net
            .broadcast(Msg::Init)
            .await
            .expect("failed to init other modules");
        loop {
            let key = match select(
                self.net.try_next().map_ok(|o| o.map(|m| m.unpack())),
                self.timer.next(),
            )
            .await
            {
                Either::Left((Ok(Some((from, Msg::KeyEvent(key)))), _)) => {
                    let module = if let Some(newpos) = self.locator.remap(from) {
                        newpos
                    } else {
                        continue;
                    };
                    Some(ModuleKey {
                        module: module,
                        key,
                    })
                }
                Either::Left((Ok(Some((from, Msg::ModuleType(module_type)))), _)) => {
                    self.locator.add_module(from, module_type);
                    continue;
                }
                Either::Right((_, _)) => {
                    let key = self.scanner.change().await;
                    let module = if let Some(newpos) = self.locator.remap(0) {
                        newpos
                    } else {
                        continue;
                    };
                    key.map(|key| ModuleKey { module, key })
                }
                _ => continue,
            };
            if let Some(translated) = self.translator.translate(key) {
                match translated {
                    Translated::Broadcast(msg) => {
                        let _ = self.net.broadcast(msg).await;
                    }
                    Translated::Send(to, msg) => {
                        let _ = self.net.send_to(to, msg).await;
                    }
                    default => {
                        let _ = sink.send(default).await;
                    }
                }
            }
        }
    }
}