diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c2ad221..16eb4ad8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Features - [#1016](https://github.com/ClementTsang/bottom/pull/1016): Add support for displaying process usernames on Windows. +- [#1022](https://github.com/ClementTsang/bottom/pull/1022): Support three-character hex colour strings for styling. ## Changes diff --git a/scripts/packager.py b/scripts/packager.py index 30f56182..8a4a8b4e 100644 --- a/scripts/packager.py +++ b/scripts/packager.py @@ -34,7 +34,7 @@ def get_hash(deployment_file): elif str.lower(hash_type) == "sha1": deployment_hash = hashlib.sha1(deployment_file.read()).hexdigest() else: - print('Unsupported hash format "%s". Please use SHA512, SHA256, or SHA1.', hash_type) + print('Unsupported hash format "%s". Please use SHA512, SHA256, or SHA1.', hash_type) exit(1) print("Generated hash: %s" % str(deployment_hash)) diff --git a/src/canvas/canvas_styling/colour_utils.rs b/src/canvas/canvas_styling/colour_utils.rs index 89b1b170..2814c580 100644 --- a/src/canvas/canvas_styling/colour_utils.rs +++ b/src/canvas/canvas_styling/colour_utils.rs @@ -1,4 +1,7 @@ +use concat_string::concat_string; +use itertools::Itertools; use tui::style::{Color, Style}; +use unicode_segmentation::UnicodeSegmentation; use crate::utils::error; @@ -10,41 +13,44 @@ pub const HIGHLIGHT_COLOUR: Color = Color::LightBlue; pub const AVG_COLOUR: Color = Color::Red; pub const ALL_COLOUR: Color = Color::Green; +/// Convert a hex string to a colour. fn convert_hex_to_color(hex: &str) -> error::Result { - fn hex_err(hex: &str) -> error::Result { - Err( + fn hex_component_to_int(hex: &str, first: &str, second: &str) -> error::Result { + u8::from_str_radix(&concat_string!(first, second), 16).map_err(|_| { error::BottomError::ConfigError(format!( - "\"{}\" is an invalid hex colour. It must be a valid 7 character hex string of the (ie: \"#112233\")." - , hex)) - ) + "\"{hex}\" is an invalid hex color, could not decode." + )) + }) } - fn convert_hex_to_rgb(hex: &str) -> error::Result<(u8, u8, u8)> { - let hex_components: Vec = hex.chars().collect(); - - if hex_components.len() == 7 { - let mut r_string = hex_components[1].to_string(); - r_string.push(hex_components[2]); - let mut g_string = hex_components[3].to_string(); - g_string.push(hex_components[4]); - let mut b_string = hex_components[5].to_string(); - b_string.push(hex_components[6]); - - let r = u8::from_str_radix(&r_string, 16).or_else(|_err| hex_err(hex))?; - let g = u8::from_str_radix(&g_string, 16).or_else(|_err| hex_err(hex))?; - let b = u8::from_str_radix(&b_string, 16).or_else(|_err| hex_err(hex))?; - - return Ok((r, g, b)); - } - - Err(error::BottomError::ConfigError(format!( - "\"{}\" is an invalid hex colour. It must be a 7 character string of the form \"#112233\".", - hex - ))) + fn invalid_hex_format(hex: &str) -> error::BottomError { + error::BottomError::ConfigError(format!( + "\"{hex}\" is an invalid hex color. It must be either a 7 character hex string of the form \"#12ab3c\" or a 3 character hex string of the form \"#1a2\".", + )) } - let rgb = convert_hex_to_rgb(hex)?; - Ok(Color::Rgb(rgb.0, rgb.1, rgb.2)) + if !hex.starts_with('#') { + return Err(invalid_hex_format(hex)); + } + + let components = hex.graphemes(true).collect_vec(); + if components.len() == 7 { + // A 6-long hex. + let r = hex_component_to_int(hex, components[1], components[2])?; + let g = hex_component_to_int(hex, components[3], components[4])?; + let b = hex_component_to_int(hex, components[5], components[6])?; + + Ok(Color::Rgb(r, g, b)) + } else if components.len() == 4 { + // A 3-long hex. + let r = hex_component_to_int(hex, components[1], components[1])?; + let g = hex_component_to_int(hex, components[2], components[2])?; + let b = hex_component_to_int(hex, components[3], components[3])?; + + Ok(Color::Rgb(r, g, b)) + } else { + Err(invalid_hex_format(hex)) + } } pub fn str_to_fg(input_val: &str) -> error::Result