Add trait for mocking

This commit is contained in:
Nicolas 2022-04-03 14:42:55 +02:00
parent e6e1c6fb25
commit 84cbe22ce7
3 changed files with 181 additions and 106 deletions

123
src/color/factory.rs Normal file
View File

@ -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<dyn ColorSampler>;
/// 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<ColorEnum>, 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<dyn ColorSampler> {
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<ColorEnum>, 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))
}
}

58
src/color/mod.rs Normal file
View File

@ -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<Color>
}
impl SimpleColorSampler {
pub fn new(values: Vec<Color>) -> 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);
}
}

View File

@ -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<Color>
}
impl Pallet {
pub fn new(values: Vec<Color>) -> 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<PalletEnum>, 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()
}
}