diff --git a/src/color/factory.rs b/src/color/factory.rs new file mode 100644 index 0000000..a42f53a --- /dev/null +++ b/src/color/factory.rs @@ -0,0 +1,123 @@ +use clap::ArgEnum; +use crossterm::style::Color::*; +use rand::Rng; +use rand::prelude::IteratorRandom; +use crate::color::{ColorSampler, SimpleColorSampler}; + +#[derive(Copy, Clone, ArgEnum)] +pub enum ColorEnum { + Red, + Green, + Blue, + LightRed, + LightGreen, + LightBlue, + White, + Rainbow +} + +pub trait ColorSamplerFactory { + /// Creates the requested color. + /// # Arguments + /// * `color`: The color type. + fn create(&self, colors: ColorEnum) -> Box; + + /// Chooses a random color color. + /// If none is provided, a random one of all available is chosen. + /// # Arguments + /// * `options`: A list of all options. + /// * `rng`: The number generator. + fn choose(&self, options: Vec, rng: &mut impl Rng) -> ColorEnum; +} + +pub struct SimpleColorSamplerFactory; + +impl SimpleColorSamplerFactory { + pub fn new() -> Self { + Self { } + } +} + +impl ColorSamplerFactory for SimpleColorSamplerFactory { + fn create(&self, colors: ColorEnum) -> Box { + match colors { + ColorEnum::Red => Box::new(SimpleColorSampler::new(vec![Yellow, DarkYellow, Red])), + ColorEnum::Green => Box::new(SimpleColorSampler::new(vec![Cyan, DarkGreen, Green])), + ColorEnum::Blue => Box::new(SimpleColorSampler::new(vec![Magenta, DarkBlue, Blue])), + ColorEnum::LightRed => Box::new(SimpleColorSampler::new(vec![White, Yellow, Red])), + ColorEnum::LightGreen => Box::new(SimpleColorSampler::new(vec![White, Cyan, Green])), + ColorEnum::LightBlue => Box::new(SimpleColorSampler::new(vec![White, Blue, Magenta])), + ColorEnum::White => Box::new(SimpleColorSampler::new(vec![Black, Grey, White])), + ColorEnum::Rainbow => Box::new(SimpleColorSampler::new(vec![Magenta, Blue, Green, Yellow, Red])) + } + } + + fn choose(&self, mut options: Vec, rng: &mut impl Rng) -> ColorEnum { + if options.is_empty() { + options.push(ColorEnum::Red); + options.push(ColorEnum::Green); + options.push(ColorEnum::Blue); + options.push(ColorEnum::LightRed); + options.push(ColorEnum::LightGreen); + options.push(ColorEnum::LightBlue); + options.push(ColorEnum::White); + options.push(ColorEnum::Rainbow); + } + options.into_iter().choose_stable(rng).unwrap() + } +} + +#[cfg(test)] +mod test { + use rand::rngs::mock::StepRng; + use super::*; + + #[test] + fn create() { + let factory = SimpleColorSamplerFactory::new(); + + factory.create(ColorEnum::Red); + factory.create(ColorEnum::Green); + factory.create(ColorEnum::Blue); + factory.create(ColorEnum::LightRed); + factory.create(ColorEnum::LightGreen); + factory.create(ColorEnum::LightBlue); + factory.create(ColorEnum::White); + factory.create(ColorEnum::Rainbow); + } + + #[test] + fn choose_from_all() { + let rng = &mut StepRng::new(0, 1); + + let factory = SimpleColorSamplerFactory::new(); + + assert!(matches!(factory.choose(Vec::new(), rng), ColorEnum::Rainbow)); + } + + #[test] + fn choose_from_options() { + let rng = &mut StepRng::new(0, 1); + let options = vec![ColorEnum::Blue]; + + let factory = SimpleColorSamplerFactory::new(); + + assert!(matches!(factory.choose(options, rng), ColorEnum::Blue)); + } + + #[test] + fn enum_copy() { + let color = ColorEnum::Green; + let copy = color; + + assert!(matches!(copy, ColorEnum::Green)) + } + + #[test] + fn enum_clone() { + let color = ColorEnum::Green; + let copy = color.clone(); + + assert!(matches!(copy, ColorEnum::Green)) + } +} \ No newline at end of file diff --git a/src/color/mod.rs b/src/color/mod.rs new file mode 100644 index 0000000..6fbabc5 --- /dev/null +++ b/src/color/mod.rs @@ -0,0 +1,58 @@ +pub mod factory; + +use crossterm::style::Color; +use crossterm::style::Color::*; + +/// A collection of colors. +pub trait ColorSampler { + /// Gets a color for the given fill. + /// # Arguments + /// * `fill`: `0 <= fill` and `fill < 1` + fn sample(&self, fill: f32) -> Color; +} + +pub struct SimpleColorSampler { + values: Vec +} + +impl SimpleColorSampler { + pub fn new(values: Vec) -> Self { + Self { values } + } +} + +impl ColorSampler for SimpleColorSampler { + fn sample(&self, fill: f32) -> Color { + let index = self.values.len() as f32 * fill; + + assert!(index >= 0.0); + + self.values[index as usize] + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn sample() { + let sampler = SimpleColorSampler::new(vec![Red, Yellow, Green]); + + assert_eq!(Red, sampler.sample(0.1)); + assert_eq!(Yellow, sampler.sample(0.4)); + assert_eq!(Green, sampler.sample(0.7)); + } + + #[test] + #[should_panic] + fn sample_index_negative() { + SimpleColorSampler::new(Vec::new()).sample(-0.1); + } + + #[test] + #[should_panic] + fn sample_index_equals_one() { + SimpleColorSampler::new(Vec::new()).sample(1.0); + } +} \ No newline at end of file diff --git a/src/pallet.rs b/src/pallet.rs deleted file mode 100644 index ff5c58e..0000000 --- a/src/pallet.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crossterm::style::Color; -use crossterm::style::Color::*; -use rand::prelude::IteratorRandom; -use rand::Rng; -use clap::ArgEnum; - -/// A collection of colors. -pub struct Pallet { - values: Vec -} - -impl Pallet { - pub fn new(values: Vec) -> Self { - Self { values } - } - - pub fn red() -> Pallet { - Pallet::new(vec![Yellow, DarkYellow, Red]) - } - - pub fn red_light() -> Pallet { - Pallet::new(vec![White, Yellow, Red]) - } - - pub fn green() -> Pallet { - Pallet::new(vec![Cyan, DarkGreen, Green]) - } - - pub fn green_light() -> Pallet { - Pallet::new(vec![White, Cyan, Green]) - } - - pub fn blue() -> Pallet { - Pallet::new(vec![Magenta, DarkBlue, Blue]) - } - - pub fn blue_light() -> Pallet { - Pallet::new(vec![White, Magenta, Blue]) - } - - pub fn white() -> Pallet { - Pallet::new(vec![Black, Grey, White]) - } - - pub fn rainbow() -> Pallet { - Pallet::new(vec![Magenta, Blue, Green, Yellow, Red]) - } - - /// Gets a color for the given fill. - /// # Arguments - /// * `fill`: `0 <= fill` and `fill < 1` - pub fn sample(&self, fill: f32) -> Color { - let pos = self.values.len() as f32 * fill; - let index = pos as usize; - - self.values[index] - } -} - -#[derive(Copy, Clone, ArgEnum)] -pub enum PalletEnum { - Red, - RedLight, - Green, - GreenLight, - Blue, - BlueLight, - White, - Rainbow -} - - -/// Chooses a random color pallet. -/// If none is provided, a random one of all available is chosen. -/// # Arguments -/// * `options`: A list of all options. -/// * `rng`: The number generator. -pub fn choose_pallet(mut options: Vec, rng: &mut impl Rng) -> PalletEnum { - if options.is_empty() { - options.push(PalletEnum::Red); - options.push(PalletEnum::RedLight); - options.push(PalletEnum::Green); - options.push(PalletEnum::GreenLight); - options.push(PalletEnum::Blue); - options.push(PalletEnum::BlueLight); - options.push(PalletEnum::White); - options.push(PalletEnum::Rainbow); - } - options.into_iter().choose(rng).unwrap() -} - -/// Creates the requested pallet. -/// # Arguments -/// * `pallet`: The pallet type. -pub fn create_pallet(pallet: PalletEnum) -> Pallet { - match pallet { - PalletEnum::Red => Pallet::red(), - PalletEnum::RedLight => Pallet::red_light(), - PalletEnum::Green => Pallet::green(), - PalletEnum::GreenLight => Pallet::green_light(), - PalletEnum::Blue => Pallet::blue(), - PalletEnum::BlueLight => Pallet::blue_light(), - PalletEnum::White => Pallet::white(), - PalletEnum::Rainbow => Pallet::rainbow() - } -} \ No newline at end of file