diff --git a/src/main.rs b/src/main.rs index 00550d8..0384217 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,20 +4,17 @@ use std::{ f32::consts::FRAC_PI_2, io::stdout, net::{Ipv4Addr, SocketAddr, UdpSocket}, - num::ParseIntError, path::PathBuf, - str::FromStr, thread::sleep, time::Duration, }; use bracket_color::prelude::{HSV, RGB}; -use clap::{Parser, Subcommand, ValueEnum}; +use clap::{builder::TypedValueParser, Parser, Subcommand, ValueEnum}; use image::{imageops::FilterType, io::Reader as ImageReader, Pixel, Rgb, RgbImage}; use rand::Rng; use splink_client::{send_frame, Layout}; -use thiserror::Error; /// Blinkenwall v3 prototype client #[derive(Parser, Debug)] @@ -39,42 +36,37 @@ struct Args { action: Action, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct Color { - r: u8, - g: u8, - b: u8, -} +#[derive(Clone, Copy)] +struct ColorParser; +impl TypedValueParser for ColorParser { + type Value = Rgb; -#[derive(Clone, Debug, Error)] -enum ColorError { - #[error("Wrong parameter length")] - WrongLength, - #[error("Illegal integer")] - BadNumber(#[from] ParseIntError), -} - -impl FromStr for Color { - type Err = ColorError; - - fn from_str(s: &str) -> Result { + fn parse_ref( + &self, + _cmd: &clap::Command, + _arg: Option<&clap::Arg>, + value: &std::ffi::OsStr, + ) -> Result { + let s = value + .to_str() + .ok_or(clap::Error::raw(clap::ErrorKind::InvalidUtf8, ""))?; let s = s.strip_prefix('#').unwrap_or(s); if s.len() != 6 { - return Err(ColorError::WrongLength); + return Err(clap::Error::raw( + clap::ErrorKind::InvalidValue, + "Must be a 6-digit hexadecimal number", + )); } - let r = u8::from_str_radix(&s[0..2], 16)?; - let g = u8::from_str_radix(&s[2..4], 16)?; - let b = u8::from_str_radix(&s[4..6], 16)?; + let mut channels = [0; 3]; + for i in 0..3 { + channels[i] = u8::from_str_radix(&s[i * 2..i * 2 + 2], 16).map_err(|_| { + clap::Error::raw(clap::ErrorKind::InvalidValue, "Invalid hex literal") + })?; + } - Ok(Self { r, g, b }) - } -} - -impl From for Rgb { - fn from(c: Color) -> Rgb { - Rgb([c.r, c.g, c.b]) + Ok(Rgb(channels)) } } @@ -85,7 +77,8 @@ enum Action { animation: Animation, }, Solid { - color: Color, + #[clap(value_parser = ColorParser)] + color: Rgb, }, Image { path: PathBuf, @@ -196,7 +189,7 @@ fn main() -> anyhow::Result<()> { let image = match args.action { Action::Solid { color } => { - RgbImage::from_pixel(layout.width_px(), layout.height_px(), color.into()) + RgbImage::from_pixel(layout.width_px(), layout.height_px(), color) } Action::Clear => RgbImage::new(layout.width_px(), layout.height_px()), Action::Image { path } => ImageReader::open(path)?