rusty-rain 0.4.0

A cross platform CMatrix like program.
use super::{AUTHOR, Direction, MAXSPEED, MINSPEED};
use crate::characters::Characters;
use clap::{Parser, crate_description, crate_name, crate_version};

const HELP_DIRECTION: &str = "Set the direction of the Rain.
Default is set to down/south
OPTIONS:
    up, north,
    down, south,
    left, west,
    right, east
";

const HELP_COLORS: &str = "Set color of Rain with color string name or tuple
OPTIONS:
    white,
    red,
    blue,
    green,
    r,g,b
";

const HELP_CHARS: &str = "Set what kind of characters are printed as rain.
OPTIONS:
    all            - This shows most of the Character Groups all at once.
    alphalow       - Lower Case Alphabet Characters
    alphaup        - Upper Case Alphabet Characters
    arrow          - Arrow Emojis or Fancy Characters
    bin            - All Ones and Zeros
    cards          - Playing Cards
    clock          - 🕑
    crab           - 🦀
    dominosh       - 🀽
    dominosv       - 🁫
    earth          - 🌎
    emojis         - This is just a bunch of random Emojis
    jap            - Japanese Characters
    large-letters  - Cool Looking Large Letters
    moon           - 🌕
    num            - Good ol fashion Numbers
    numbered-balls - These are like pool balls
    numbered-cubes - These are like the pool balls but just cubes
    plants         - Plants of sorts
    smile          - 😃
    shapes         - Squares and Circles of a few colors
";

const HELP_HEAD: &str = "Set the color of the first char in Rain.
OPTIONS:
    white,
    red,
    blue,
    green,
    r,g,b
";

#[derive(Debug, Parser)]
#[command(
    author = AUTHOR,
    about = "A cross platform matrix rain made with Rust.",
    long_about = Some(crate_description!()),
    color = clap::ColorChoice::Always,
    name = crate_name!(),
    version = crate_version!())]
pub struct Cli {
    #[arg(short, long, default_value_t = false)]
    pub shade: bool,
    #[arg(short, long, help = HELP_CHARS, default_value_t = Characters::Bin)]
    pub chars: Characters,
    #[arg(short = 'C', long, help = HELP_COLORS, default_value_t = String::from("green"))]
    pub color: String,
    #[arg(short = 'H', long, help = HELP_HEAD, default_value_t = String::from("white"))]
    pub head: String,
    #[arg(short, long, help = HELP_DIRECTION, default_value_t = Direction::Down)]
    pub direction: Direction,
    #[arg(short = 'S', long, default_value_t = format!("{MAXSPEED},{MINSPEED}"))]
    pub speed: String,
}

impl Cli {
    pub fn rain_color(&self) -> (u8, u8, u8) {
        into_color(&self.color)
    }
    pub fn head_color(&self) -> (u8, u8, u8) {
        into_color(&self.head)
    }

    pub fn speed(&self) -> (u64, u64) {
        match self.speed.into_tuple() {
            Ok((max, min)) => (max, min),
            _ => (MAXSPEED, MINSPEED),
        }
    }
    pub fn speed_range(&self) -> std::ops::Range<u64> {
        let (max, min) = self.speed();
        max..min
    }
}

pub fn into_color(value: &str) -> (u8, u8, u8) {
    match value {
        c if StrTuple::<(u8, u8, u8)>::into_tuple(c).is_ok() => match c.into_tuple() {
            Ok((r, g, b)) => (r, g, b),
            _ => (255, 255, 255),
        },
        "red" => (255, 0, 0),
        "blue" => (0, 0, 255),
        "green" => (0, 255, 0),
        _ => (255, 255, 255),
    }
}

impl StrTuple<(u64, u64)> for &str {
    type Error = std::num::ParseIntError;
    fn into_tuple(self) -> Result<(u64, u64), Self::Error> {
        let mut nums = Vec::new();
        for num in self.split(',') {
            nums.push(num.parse::<u64>()?);
        }
        let a = nums[0];
        let b = nums[1];
        Ok((a, b))
    }
}

impl StrTuple<(u8, u8, u8)> for &str {
    type Error = std::num::ParseIntError;
    fn into_tuple(self) -> Result<(u8, u8, u8), Self::Error> {
        let mut nums = Vec::new();
        for num in self.split(',') {
            nums.push(num.parse::<u8>()?);
        }
        let a = nums[0];
        let b = nums[1];
        let c = nums[2];
        Ok((a, b, c))
    }
}

trait StrTuple<T> {
    type Error;
    fn into_tuple(self) -> Result<T, Self::Error>;
}