use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Led {
#[default]
All,
N(u8),
}
impl Led {
pub(crate) fn index(self) -> u8 {
match self {
Led::All => 0,
Led::N(n) => n,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum DeviceKind {
Mk1,
Mk2,
Mk3,
Mk4,
Unknown,
}
impl DeviceKind {
pub fn from_serial(serial: &str) -> DeviceKind {
match u32::from_str_radix(serial.trim(), 16) {
Ok(n) if n >= 0x4000_0000 => DeviceKind::Mk4,
Ok(n) if n >= 0x3000_0000 => DeviceKind::Mk3,
Ok(n) if n >= 0x2000_0000 => DeviceKind::Mk2,
Ok(_) => DeviceKind::Mk1,
Err(_) => DeviceKind::Unknown,
}
}
pub fn pattern_max(self) -> u8 {
match self {
DeviceKind::Mk3 | DeviceKind::Mk4 => 32,
_ => 16,
}
}
pub fn can_read_rgb(self) -> bool {
!matches!(self, DeviceKind::Mk1)
}
}
impl std::fmt::Display for DeviceKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
DeviceKind::Mk1 => "mk1",
DeviceKind::Mk2 => "mk2",
DeviceKind::Mk3 => "mk3",
DeviceKind::Mk4 => "mk4",
DeviceKind::Unknown => "unknown",
};
f.write_str(s)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeviceInfo {
pub serial: String,
pub path: String,
pub kind: DeviceKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OnTimeout {
Off,
StayLit,
PlayPattern {
start: u8,
end: u8,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PlayState {
pub playing: bool,
pub start: u8,
pub end: u8,
pub count: u8,
pub pos: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PatternLine {
pub color: crate::Color,
pub duration: Duration,
pub led: Led,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum BootMode {
Normal,
Play,
Off,
Other(u8),
}
impl BootMode {
pub(crate) fn to_byte(self) -> u8 {
match self {
BootMode::Normal => 0,
BootMode::Play => 1,
BootMode::Off => 2,
BootMode::Other(n) => n,
}
}
pub(crate) fn from_byte(b: u8) -> BootMode {
match b {
0 => BootMode::Normal,
1 => BootMode::Play,
2 => BootMode::Off,
n => BootMode::Other(n),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StartupParams {
pub mode: BootMode,
pub start: u8,
pub end: u8,
pub count: u8,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serial_prefix_picks_the_generation() {
assert_eq!(DeviceKind::from_serial("1a001b02"), DeviceKind::Mk1);
assert_eq!(DeviceKind::from_serial("1FFFFFFF"), DeviceKind::Mk1);
assert_eq!(DeviceKind::from_serial("20000000"), DeviceKind::Mk2);
assert_eq!(DeviceKind::from_serial("2a0b1c2d"), DeviceKind::Mk2);
assert_eq!(DeviceKind::from_serial("30000000"), DeviceKind::Mk3);
assert_eq!(DeviceKind::from_serial("40000000"), DeviceKind::Mk4);
assert_eq!(DeviceKind::from_serial("FFFFFFFF"), DeviceKind::Mk4);
assert_eq!(DeviceKind::from_serial("not-hex"), DeviceKind::Unknown);
}
#[test]
fn pattern_capacity_follows_generation() {
assert_eq!(DeviceKind::Mk1.pattern_max(), 16);
assert_eq!(DeviceKind::Mk2.pattern_max(), 16);
assert_eq!(DeviceKind::Mk3.pattern_max(), 32);
assert_eq!(DeviceKind::Mk4.pattern_max(), 32);
assert_eq!(DeviceKind::Unknown.pattern_max(), 16);
}
#[test]
fn only_mk1_lacks_rgb_readback() {
assert!(!DeviceKind::Mk1.can_read_rgb());
assert!(DeviceKind::Mk2.can_read_rgb());
}
}