diff --git a/src/cli/cli.rs b/src/cli/cli.rs index fbf11b40..e85c2b3b 100644 --- a/src/cli/cli.rs +++ b/src/cli/cli.rs @@ -306,6 +306,29 @@ pub fn build_cli() -> App<'static, 'static> { ) .arg(color_arg.clone()), ) + .subcommand( + SubCommand::with_name("set") + .about("Set a color property to a specific value") + .long_about("Set the given property to a specific value\n\ + Example:\n \ + pastel random | pastel set luminance 0.9") + .arg( + Arg::with_name("property") + .help("The property that should be changed") + .possible_values(&["lightness", "hue", "chroma", + "lab-a", "lab-b", + "red", "green", "blue", + "hsl-hue", "hsl-saturation", "hsl-lightness"]) + .case_insensitive(true) + .required(true), + ) + .arg( + Arg::with_name("value") + .help("The new numerical value of the property") + .required(true), + ) + .arg(color_arg.clone()), + ) .subcommand( SubCommand::with_name("saturate") .long_about( diff --git a/src/cli/commands/color_commands.rs b/src/cli/commands/color_commands.rs index f7a1d426..a779731f 100644 --- a/src/cli/commands/color_commands.rs +++ b/src/cli/commands/color_commands.rs @@ -2,6 +2,10 @@ use crate::colorspace::get_mixing_function; use crate::commands::prelude::*; use pastel::Fraction; +fn clamp(lower: f64, upper: f64, x: f64) -> f64 { + f64::max(f64::min(upper, x), lower) +} + use super::show::show_color; macro_rules! color_command { @@ -72,3 +76,79 @@ color_command!(MixCommand, config, matches, color, { mix(&base, &color, fraction) }); + +color_command!(SetCommand, config, matches, color, { + let property = matches.value_of("property").expect("required argument"); + let property = property.to_lowercase(); + let property = property.as_ref(); + + let value = number_arg(matches, "value")?; + + match property { + "red" | "green" | "blue" => { + let mut rgba = color.to_rgba(); + let value = clamp(0.0, 255.0, value) as u8; + match property { + "red" => { + rgba.r = value; + } + "green" => { + rgba.g = value; + } + "blue" => { + rgba.b = value; + } + _ => unreachable!(), + } + Color::from_rgba(rgba.r, rgba.g, rgba.b, rgba.alpha) + } + "hsl-hue" | "hsl-saturation" | "hsl-lightness" => { + let mut hsla = color.to_hsla(); + match property { + "hsl-hue" => { + hsla.h = value; + } + "hsl-saturation" => { + hsla.s = value; + } + "hsl-lightness" => { + hsla.l = value; + } + _ => unreachable!(), + } + Color::from_hsla(hsla.h, hsla.s, hsla.l, hsla.alpha) + } + "lightness" | "lab-a" | "lab-b" => { + let mut lab = color.to_lab(); + match property { + "lightness" => { + lab.l = value; + } + "lab-a" => { + lab.a = value; + } + "lab-b" => { + lab.b = value; + } + _ => unreachable!(), + } + Color::from_lab(lab.l, lab.a, lab.b, lab.alpha) + } + "hue" | "chroma" => { + let mut lch = color.to_lch(); + match property { + "hue" => { + lch.h = value; + } + "chroma" => { + lch.c = value; + } + _ => unreachable!(), + } + Color::from_lch(lch.l, lch.c, lch.h, lch.alpha) + } + &_ => { + unreachable!("Unknown property"); + } + } +}); diff --git a/src/cli/commands/io.rs b/src/cli/commands/io.rs index d1537e69..2fe20e77 100644 --- a/src/cli/commands/io.rs +++ b/src/cli/commands/io.rs @@ -10,7 +10,7 @@ use crate::{PastelError, Result}; use pastel::Color; pub fn number_arg(matches: &ArgMatches, name: &str) -> Result { - let value_str = matches.value_of(name).unwrap(); + let value_str = matches.value_of(name).expect("required argument"); value_str .parse::() .map_err(|_| PastelError::CouldNotParseNumber(value_str.into())) diff --git a/src/cli/commands/mod.rs b/src/cli/commands/mod.rs index 98446668..e1437ce3 100644 --- a/src/cli/commands/mod.rs +++ b/src/cli/commands/mod.rs @@ -47,6 +47,7 @@ impl Command { "lighten" => Command::WithColor(Box::new(color_commands::LightenCommand)), "darken" => Command::WithColor(Box::new(color_commands::DarkenCommand)), "rotate" => Command::WithColor(Box::new(color_commands::RotateCommand)), + "set" => Command::WithColor(Box::new(color_commands::SetCommand)), "complement" => Command::WithColor(Box::new(color_commands::ComplementCommand)), "mix" => Command::WithColor(Box::new(color_commands::MixCommand)), "to-gray" => Command::WithColor(Box::new(color_commands::ToGrayCommand)), diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index df8306e0..ff7073a5 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -107,3 +107,33 @@ fn sort_by_basic() { .success() .stdout("hsl(0,0.0%,0.0%)\nhsl(0,0.0%,50.2%)\nhsl(0,0.0%,100.0%)\n"); } + +#[test] +fn set_basic() { + pastel() + .arg("set") + .arg("hsl-hue") + .arg("120") + .arg("red") + .assert() + .success() + .stdout("hsl(120,100.0%,50.0%)\n"); + + pastel() + .arg("set") + .arg("hsl-saturation") + .arg("0.1") + .arg("red") + .assert() + .success() + .stdout("hsl(0,10.0%,50.0%)\n"); + + pastel() + .arg("set") + .arg("hsl-lightness") + .arg("0.5") + .arg("white") + .assert() + .success() + .stdout("hsl(0,0.0%,50.0%)\n"); +}