keebrs 0.3.0

Keyboard firmware building blocks
//! Keyboard LED management

use embedded_hal::digital::v2::OutputPin;

/// "Disabled" Led implementatin
pub struct Disabled;

/// An active-low LED pin
pub struct ActiveLow<G>(pub G);

/// An active-high LED pin
pub struct ActiveHigh<G>(pub G);

/// An LED
pub trait Led {
    /// Turn the LED on
    fn set_on(&mut self);
    /// Turn the LED off
    fn set_off(&mut self);
}

impl Led for Disabled {
    fn set_on(&mut self) {}
    fn set_off(&mut self) {}
}

impl<G> Led for ActiveHigh<G>
where
    G: OutputPin,
{
    fn set_on(&mut self) {
        if self.0.set_high().is_err() {
            panic!("error setting led high");
        }
    }
    fn set_off(&mut self) {
        if self.0.set_low().is_err() {
            panic!("error setting led low");
        }
    }
}

impl<G> Led for ActiveLow<G>
where
    G: OutputPin,
{
    fn set_on(&mut self) {
        if self.0.set_low().is_err() {
            panic!("error setting led low");
        }
    }
    fn set_off(&mut self) {
        if self.0.set_high().is_err() {
            panic!("error setting led high");
        }
    }
}

/// Structure for LED outputs.
pub struct Leds<N, C, S, P, K> {
    /// NumLock
    pub num: N,
    /// CapsLock
    pub caps: C,
    /// ScrollLock
    pub scroll: S,
    /// ComposeLock
    pub compose: P,
    /// Kana
    pub kana: K,
}

/// Default "no leds" collection
pub type NoLeds = Leds<Disabled, Disabled, Disabled, Disabled, Disabled>;

impl Default for NoLeds {
    fn default() -> Self {
        Leds {
            num: Disabled,
            caps: Disabled,
            scroll: Disabled,
            compose: Disabled,
            kana: Disabled,
        }
    }
}

impl Leds<Disabled, Disabled, Disabled, Disabled, Disabled> {
    /// Create a new empty Leds collection
    pub fn new() -> NoLeds {
        Self::default()
    }
}

impl<N, C, S, P, K> Leds<N, C, S, P, K> {
    /// Add a caps led
    pub fn caps<T>(self, caps: T) -> Leds<N, T, S, P, K> {
        Leds {
            num: self.num,
            caps,
            scroll: self.scroll,
            compose: self.compose,
            kana: self.kana,
        }
    }

    /// Add a num led
    pub fn num<T>(self, num: T) -> Leds<T, C, S, P, K> {
        Leds {
            num,
            caps: self.caps,
            scroll: self.scroll,
            compose: self.compose,
            kana: self.kana,
        }
    }

    /// Add a scroll led
    pub fn scroll<T>(self, scroll: T) -> Leds<N, C, T, P, K> {
        Leds {
            num: self.num,
            caps: self.caps,
            scroll,
            compose: self.compose,
            kana: self.kana,
        }
    }

    /// Add a compose led
    pub fn compose<T>(self, compose: T) -> Leds<N, C, S, T, K> {
        Leds {
            num: self.num,
            caps: self.caps,
            scroll: self.scroll,
            compose,
            kana: self.kana,
        }
    }

    /// Add a kana led
    pub fn kana<T>(self, kana: T) -> Leds<N, C, S, P, T> {
        Leds {
            num: self.num,
            caps: self.caps,
            scroll: self.scroll,
            compose: self.compose,
            kana,
        }
    }
}

impl<N, C, S, P, K> LedReport for Leds<N, C, S, P, K>
where
    C: Led,
    N: Led,
    S: Led,
    P: Led,
    K: Led,
{
    /// Set the Led state from a report byte
    fn from_report_byte(&mut self, report: u8) {
        let leds: &mut [&mut dyn Led] = &mut [
            &mut self.num,
            &mut self.caps,
            &mut self.scroll,
            &mut self.compose,
            &mut self.kana,
        ];
        for (bit, led) in leds.iter_mut().enumerate() {
            if report & (0x01 << bit) != 0 {
                led.set_on();
            } else {
                led.set_off();
            }
        }
    }
}

/// A type that can control LEDs via an output report
pub trait LedReport {
    /// Set the Led state from the report byte
    fn from_report_byte(&mut self, report: u8);
}