use crate::color::Rgb; use serde::{Deserialize, Serialize}; use std::collections::vec_deque; pub mod collide; pub mod fade; pub mod flashing; pub mod moving_pixel; pub mod moving_rainbow; pub mod orb; pub mod solid; pub use collide::{Collide, CollideParams}; pub use fade::{Fade, FadeParams}; pub use flashing::{Flashing, FlashingParams}; pub use moving_pixel::{MovingPixel, MovingPixelParams}; pub use moving_rainbow::{MovingRainbow, MovingRainbowParams}; pub use orb::{Orb, OrbParams}; pub use solid::{Solid, SolidParams}; pub trait FormRender { fn render(&self) -> String; } pub trait InputRender { fn render(&self, name: &str, multi_index: Option) -> String; } impl InputRender for bool { fn render(&self, name: &str, multi_index: Option) -> String { format!( r#""#, if *self { " checked" } else { "" }, if let Some(i) = multi_index { format!( r#" name="{name}-{i}" rust-form-multi="{name}""#, name = name, i = i ) } else { format!(r#" name="{name}""#, name = name) }, name = name ) } } impl InputRender for Rgb { fn render(&self, name: &str, multi_index: Option) -> String { format!( r#""#, self.to_hex_str(), if let Some(i) = multi_index { format!( r#" name="{name}-{i}" rust-form-multi="{name}""#, name = name, i = i ) } else { format!(r#" name="{name}""#, name = name) }, name = name ) } } impl InputRender for Vec { fn render(&self, name: &str, _multi_index: Option) -> String { self.iter() // .chain(iter::once(&Rgb::default())) .enumerate() .fold(String::new(), |acc, (i, c)| { acc + &c.render(name, Some(i)) + "\n" }) } } impl InputRender for u8 { fn render(&self, name: &str, multi_index: Option) -> String { format!( r#""#, self, if let Some(i) = multi_index { format!( r#" name="{name}-{i}" rust-form-multi="{name}""#, name = name, i = i ) } else { format!(r#" name="{name}""#, name = name) }, name = name ) } } impl InputRender for u16 { fn render(&self, name: &str, multi_index: Option) -> String { format!( r#""#, self, if let Some(i) = multi_index { format!( r#" name="{name}-{i}" rust-form-multi="{name}""#, name = name, i = i ) } else { format!(r#" name="{name}""#, name = name) }, name = name ) } } #[derive(Serialize, Deserialize, Clone, Debug)] pub enum Parameters { Collide(CollideParams), Fade(FadeParams), MovingPixel(MovingPixelParams), MovingRainbow(MovingRainbowParams), Orb(OrbParams), Solid(SolidParams), Flashing(FlashingParams), } impl Default for Parameters { fn default() -> Self { Self::Solid(SolidParams::default()) } } impl FormRender for Parameters { fn render(&self) -> String { match self { Self::Collide(ref p) => p.render(), Self::Fade(ref p) => p.render(), Self::MovingPixel(ref p) => p.render(), Self::MovingRainbow(ref p) => p.render(), Self::Orb(ref p) => p.render(), Self::Solid(ref p) => p.render(), Self::Flashing(ref p) => p.render(), } } } impl Parameters { pub fn default_with_name(name: &str) -> Option { match name { "Collide" => Some(Self::Collide(CollideParams::default())), "Fade" => Some(Self::Fade(FadeParams::default())), "MovingPixel" => Some(Self::MovingPixel(MovingPixelParams::default())), "MovingRainbow" => Some(Self::MovingRainbow(MovingRainbowParams::default())), "Orb" => Some(Self::Orb(OrbParams::default())), "Solid" => Some(Self::Solid(SolidParams::default())), "Flashing" => Some(Self::Flashing(FlashingParams::default())), _ => None, } } pub const fn get_name(&self) -> &str { match self { Self::Collide(_) => "Collide", Self::Fade(_) => "Fade", Self::MovingPixel(_) => "MovingPixel", Self::MovingRainbow(_) => "MovingRainbow", Self::Orb(_) => "Orb", Self::Solid(_) => "Solid", Self::Flashing(_) => "Flashing", } } pub const fn get_names() -> &'static [&'static str] { &[ "Solid", "Collide", "Fade", "MovingPixel", "MovingRainbow", "Orb", "Flashing", ] } pub fn to_pattern(&self) -> Box { match self { Self::Collide(ref p) => Box::new(Collide::new(p)), Self::Fade(ref p) => Box::new(Fade::new(p)), Self::MovingPixel(ref p) => Box::new(MovingPixel::new(p)), Self::MovingRainbow(ref p) => Box::new(MovingRainbow::new(p)), Self::Orb(ref p) => Box::new(Orb::new(p)), Self::Solid(ref p) => Box::new(Solid::new(p)), Self::Flashing(ref p) => Box::new(Flashing::new(p)), } } } pub trait Pattern: std::fmt::Debug + Send + Sync { fn init(&mut self, num_lights: u16) -> Result<(), ()>; fn step(&mut self) -> Result; fn get_strip(&self) -> vec_deque::Iter; } // #[cfg(test)] // mod tests { // use super::*; // const NUM_LIGHTS: u16 = 10; // fn test_strip() -> Vec { // vec![color::BLACK; NUM_LIGHTS.into()] // } // #[test] // fn moving_pixel() { // let color = Rgb(123, 152, 89); // let mut pat = MovingPixel::new(color.clone()); // let mut strip = test_strip(); // assert!(pat.init(&mut strip, NUM_LIGHTS).is_ok()); // // One is my color // assert_eq!(strip.iter().filter(|c| **c == color).count(), 1); // // The rest are off // assert_eq!( // strip.iter().filter(|c| **c == color::BLACK).count(), // (NUM_LIGHTS - 1).into() // ); // pat.step(&mut strip); // // One is my color // assert_eq!(strip.iter().filter(|c| **c == color).count(), 1); // // The rest are off // assert_eq!( // strip.iter().filter(|c| **c == color::BLACK).count(), // (NUM_LIGHTS - 1).into() // ); // } // #[test] // fn solid() {} // #[test] // fn moving_rainbow() {} // #[test] // fn fade() {} // #[test] // fn collide() {} // }