mirror of
https://github.com/ClementTsang/bottom
synced 2025-02-16 13:18:28 +00:00
feature: support 3-char hex colours (#1022)
This commit is contained in:
parent
edc61d428c
commit
f89b243589
5 changed files with 135 additions and 58 deletions
|
@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- [#1016](https://github.com/ClementTsang/bottom/pull/1016): Add support for displaying process usernames on Windows.
|
- [#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
|
## Changes
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ def get_hash(deployment_file):
|
||||||
elif str.lower(hash_type) == "sha1":
|
elif str.lower(hash_type) == "sha1":
|
||||||
deployment_hash = hashlib.sha1(deployment_file.read()).hexdigest()
|
deployment_hash = hashlib.sha1(deployment_file.read()).hexdigest()
|
||||||
else:
|
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)
|
exit(1)
|
||||||
|
|
||||||
print("Generated hash: %s" % str(deployment_hash))
|
print("Generated hash: %s" % str(deployment_hash))
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
use concat_string::concat_string;
|
||||||
|
use itertools::Itertools;
|
||||||
use tui::style::{Color, Style};
|
use tui::style::{Color, Style};
|
||||||
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use crate::utils::error;
|
use crate::utils::error;
|
||||||
|
|
||||||
|
@ -10,41 +13,44 @@ pub const HIGHLIGHT_COLOUR: Color = Color::LightBlue;
|
||||||
pub const AVG_COLOUR: Color = Color::Red;
|
pub const AVG_COLOUR: Color = Color::Red;
|
||||||
pub const ALL_COLOUR: Color = Color::Green;
|
pub const ALL_COLOUR: Color = Color::Green;
|
||||||
|
|
||||||
|
/// Convert a hex string to a colour.
|
||||||
fn convert_hex_to_color(hex: &str) -> error::Result<Color> {
|
fn convert_hex_to_color(hex: &str) -> error::Result<Color> {
|
||||||
fn hex_err(hex: &str) -> error::Result<u8> {
|
fn hex_component_to_int(hex: &str, first: &str, second: &str) -> error::Result<u8> {
|
||||||
Err(
|
u8::from_str_radix(&concat_string!(first, second), 16).map_err(|_| {
|
||||||
error::BottomError::ConfigError(format!(
|
error::BottomError::ConfigError(format!(
|
||||||
"\"{}\" is an invalid hex colour. It must be a valid 7 character hex string of the (ie: \"#112233\")."
|
"\"{hex}\" is an invalid hex color, could not decode."
|
||||||
, hex))
|
))
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_hex_to_rgb(hex: &str) -> error::Result<(u8, u8, u8)> {
|
fn invalid_hex_format(hex: &str) -> error::BottomError {
|
||||||
let hex_components: Vec<char> = hex.chars().collect();
|
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\".",
|
||||||
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
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let rgb = convert_hex_to_rgb(hex)?;
|
if !hex.starts_with('#') {
|
||||||
Ok(Color::Rgb(rgb.0, rgb.1, rgb.2))
|
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<Style> {
|
pub fn str_to_fg(input_val: &str) -> error::Result<Style> {
|
||||||
|
@ -58,7 +64,7 @@ pub fn str_to_colour(input_val: &str) -> error::Result<Color> {
|
||||||
} else if input_val.contains(',') {
|
} else if input_val.contains(',') {
|
||||||
convert_rgb_to_color(input_val)
|
convert_rgb_to_color(input_val)
|
||||||
} else {
|
} else {
|
||||||
convert_name_to_color(input_val)
|
convert_name_to_colour(input_val)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(error::BottomError::ConfigError(format!(
|
Err(error::BottomError::ConfigError(format!(
|
||||||
|
@ -72,7 +78,7 @@ fn convert_rgb_to_color(rgb_str: &str) -> error::Result<Color> {
|
||||||
let rgb_list = rgb_str.split(',').collect::<Vec<&str>>();
|
let rgb_list = rgb_str.split(',').collect::<Vec<&str>>();
|
||||||
if rgb_list.len() != 3 {
|
if rgb_list.len() != 3 {
|
||||||
return Err(error::BottomError::ConfigError(format!(
|
return Err(error::BottomError::ConfigError(format!(
|
||||||
"value \"{}\" is an invalid RGB colour. It must be a comma separated value with 3 integers from 0 to 255 (ie: \"255, 0, 155\").",
|
"value \"{}\" is an invalid RGB colour. It must be a comma separated value with 3 integers from 0 to 255 (ie: \"255, 0, 155\").",
|
||||||
rgb_str
|
rgb_str
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -91,13 +97,13 @@ fn convert_rgb_to_color(rgb_str: &str) -> error::Result<Color> {
|
||||||
Ok(Color::Rgb(rgb[0], rgb[1], rgb[2]))
|
Ok(Color::Rgb(rgb[0], rgb[1], rgb[2]))
|
||||||
} else {
|
} else {
|
||||||
Err(error::BottomError::ConfigError(format!(
|
Err(error::BottomError::ConfigError(format!(
|
||||||
"value \"{}\" contained invalid RGB values. It must be a comma separated value with 3 integers from 0 to 255 (ie: \"255, 0, 155\").",
|
"value \"{}\" contained invalid RGB values. It must be a comma separated value with 3 integers from 0 to 255 (ie: \"255, 0, 155\").",
|
||||||
rgb_str
|
rgb_str
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_name_to_color(color_name: &str) -> error::Result<Color> {
|
fn convert_name_to_colour(color_name: &str) -> error::Result<Color> {
|
||||||
match color_name.to_lowercase().trim() {
|
match color_name.to_lowercase().trim() {
|
||||||
"reset" => Ok(Color::Reset),
|
"reset" => Ok(Color::Reset),
|
||||||
"black" => Ok(Color::Black),
|
"black" => Ok(Color::Black),
|
||||||
|
@ -117,7 +123,7 @@ fn convert_name_to_color(color_name: &str) -> error::Result<Color> {
|
||||||
"lightcyan" | "light cyan" => Ok(Color::LightCyan),
|
"lightcyan" | "light cyan" => Ok(Color::LightCyan),
|
||||||
"white" => Ok(Color::White),
|
"white" => Ok(Color::White),
|
||||||
_ => Err(error::BottomError::ConfigError(format!(
|
_ => Err(error::BottomError::ConfigError(format!(
|
||||||
"\"{}\" is an invalid named colour.
|
"\"{}\" is an invalid named color.
|
||||||
|
|
||||||
The following are supported strings:
|
The following are supported strings:
|
||||||
+--------+-------------+---------------------+
|
+--------+-------------+---------------------+
|
||||||
|
@ -144,49 +150,119 @@ mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_colours() {
|
fn invalid_colour_names() {
|
||||||
// Test invalid spacing in single word.
|
// Test invalid spacing in single word.
|
||||||
assert!(convert_name_to_color("bl ack").is_err());
|
assert!(convert_name_to_colour("bl ack").is_err());
|
||||||
|
|
||||||
// Test invalid spacing in dual word.
|
// Test invalid spacing in dual word.
|
||||||
assert!(convert_name_to_color("darkg ray").is_err());
|
assert!(convert_name_to_colour("darkg ray").is_err());
|
||||||
|
|
||||||
// Test completely invalid colour.
|
// Test completely invalid colour.
|
||||||
assert!(convert_name_to_color("darkreset").is_err());
|
assert!(convert_name_to_colour("darkreset").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_valid_colours() {
|
fn valid_colour_names() {
|
||||||
// Standard color should work
|
// Standard color should work
|
||||||
assert_eq!(convert_name_to_color("red"), Ok(Color::Red));
|
assert_eq!(convert_name_to_colour("red"), Ok(Color::Red));
|
||||||
|
|
||||||
// Capitalizing should be fine.
|
// Capitalizing should be fine.
|
||||||
assert_eq!(convert_name_to_color("RED"), Ok(Color::Red));
|
assert_eq!(convert_name_to_colour("RED"), Ok(Color::Red));
|
||||||
|
|
||||||
// Spacing shouldn't be an issue now.
|
// Spacing shouldn't be an issue now.
|
||||||
assert_eq!(convert_name_to_color(" red "), Ok(Color::Red));
|
assert_eq!(convert_name_to_colour(" red "), Ok(Color::Red));
|
||||||
|
|
||||||
// The following are all equivalent.
|
// The following are all equivalent.
|
||||||
assert_eq!(convert_name_to_color("darkgray"), Ok(Color::DarkGray));
|
assert_eq!(convert_name_to_colour("darkgray"), Ok(Color::DarkGray));
|
||||||
assert_eq!(convert_name_to_color("darkgrey"), Ok(Color::DarkGray));
|
assert_eq!(convert_name_to_colour("darkgrey"), Ok(Color::DarkGray));
|
||||||
assert_eq!(convert_name_to_color("dark grey"), Ok(Color::DarkGray));
|
assert_eq!(convert_name_to_colour("dark grey"), Ok(Color::DarkGray));
|
||||||
assert_eq!(convert_name_to_color("dark gray"), Ok(Color::DarkGray));
|
assert_eq!(convert_name_to_colour("dark gray"), Ok(Color::DarkGray));
|
||||||
|
|
||||||
assert_eq!(convert_name_to_color("grey"), Ok(Color::Gray));
|
assert_eq!(convert_name_to_colour("grey"), Ok(Color::Gray));
|
||||||
assert_eq!(convert_name_to_color("gray"), Ok(Color::Gray));
|
assert_eq!(convert_name_to_colour("gray"), Ok(Color::Gray));
|
||||||
|
|
||||||
// One more test with spacing.
|
// One more test with spacing.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
convert_name_to_color(" lightmagenta "),
|
convert_name_to_colour(" lightmagenta "),
|
||||||
Ok(Color::LightMagenta)
|
Ok(Color::LightMagenta)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
convert_name_to_color("light magenta"),
|
convert_name_to_colour("light magenta"),
|
||||||
Ok(Color::LightMagenta)
|
Ok(Color::LightMagenta)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
convert_name_to_color(" light magenta "),
|
convert_name_to_colour(" light magenta "),
|
||||||
Ok(Color::LightMagenta)
|
Ok(Color::LightMagenta)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_hex_colours() {
|
||||||
|
assert_eq!(
|
||||||
|
convert_hex_to_color("#ffffff").unwrap(),
|
||||||
|
Color::Rgb(255, 255, 255)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
convert_hex_to_color("#000000").unwrap(),
|
||||||
|
Color::Rgb(0, 0, 0)
|
||||||
|
);
|
||||||
|
convert_hex_to_color("#111111").unwrap();
|
||||||
|
convert_hex_to_color("#11ff11").unwrap();
|
||||||
|
convert_hex_to_color("#1f1f1f").unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
convert_hex_to_color("#123abc").unwrap(),
|
||||||
|
Color::Rgb(18, 58, 188)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
convert_hex_to_color("#fff").unwrap(),
|
||||||
|
Color::Rgb(255, 255, 255)
|
||||||
|
);
|
||||||
|
assert_eq!(convert_hex_to_color("#000").unwrap(), Color::Rgb(0, 0, 0));
|
||||||
|
convert_hex_to_color("#111").unwrap();
|
||||||
|
convert_hex_to_color("#1f1").unwrap();
|
||||||
|
convert_hex_to_color("#f1f").unwrap();
|
||||||
|
convert_hex_to_color("#ff1").unwrap();
|
||||||
|
convert_hex_to_color("#1ab").unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
convert_hex_to_color("#1ab").unwrap(),
|
||||||
|
Color::Rgb(17, 170, 187)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_hex_colours() {
|
||||||
|
assert!(convert_hex_to_color("ffffff").is_err());
|
||||||
|
assert!(convert_hex_to_color("111111").is_err());
|
||||||
|
|
||||||
|
assert!(convert_hex_to_color("fff").is_err());
|
||||||
|
assert!(convert_hex_to_color("111").is_err());
|
||||||
|
assert!(convert_hex_to_color("fffffff").is_err());
|
||||||
|
assert!(convert_hex_to_color("1234567").is_err());
|
||||||
|
|
||||||
|
assert!(convert_hex_to_color("#fffffff").is_err());
|
||||||
|
assert!(convert_hex_to_color("#1234567").is_err());
|
||||||
|
assert!(convert_hex_to_color("#ff").is_err());
|
||||||
|
assert!(convert_hex_to_color("#12").is_err());
|
||||||
|
assert!(convert_hex_to_color("").is_err());
|
||||||
|
|
||||||
|
assert!(convert_hex_to_color("#pppppp").is_err());
|
||||||
|
assert!(convert_hex_to_color("#00000p").is_err());
|
||||||
|
assert!(convert_hex_to_color("#ppp").is_err());
|
||||||
|
|
||||||
|
assert!(convert_hex_to_color("#一").is_err());
|
||||||
|
assert!(convert_hex_to_color("#一二").is_err());
|
||||||
|
assert!(convert_hex_to_color("#一二三").is_err());
|
||||||
|
assert!(convert_hex_to_color("#一二三四").is_err());
|
||||||
|
|
||||||
|
assert!(convert_hex_to_color("#f一f").is_err());
|
||||||
|
assert!(convert_hex_to_color("#ff一11").is_err());
|
||||||
|
|
||||||
|
assert!(convert_hex_to_color("#🇨🇦").is_err());
|
||||||
|
assert!(convert_hex_to_color("#🇨🇦🇨🇦").is_err());
|
||||||
|
assert!(convert_hex_to_color("#🇨🇦🇨🇦🇨🇦").is_err());
|
||||||
|
assert!(convert_hex_to_color("#🇨🇦🇨🇦🇨🇦🇨🇦").is_err());
|
||||||
|
|
||||||
|
assert!(convert_hex_to_color("#हिन्दी").is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ fn test_invalid_colour_hex() {
|
||||||
.arg("./tests/invalid_configs/invalid_colour_hex.toml")
|
.arg("./tests/invalid_configs/invalid_colour_hex.toml")
|
||||||
.assert()
|
.assert()
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(predicate::str::contains("invalid hex colour"));
|
.stderr(predicate::str::contains("invalid hex color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks for if a hex is too long
|
/// Checks for if a hex is too long
|
||||||
|
@ -67,7 +67,7 @@ fn test_invalid_colour_hex_2() {
|
||||||
.arg("./tests/invalid_configs/invalid_colour_hex_2.toml")
|
.arg("./tests/invalid_configs/invalid_colour_hex_2.toml")
|
||||||
.assert()
|
.assert()
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(predicate::str::contains("invalid hex colour"));
|
.stderr(predicate::str::contains("invalid hex color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks unicode hex because the way we originally did it could cause char
|
/// Checks unicode hex because the way we originally did it could cause char
|
||||||
|
@ -79,7 +79,7 @@ fn test_invalid_colour_hex_3() {
|
||||||
.arg("./tests/invalid_configs/invalid_colour_hex_3.toml")
|
.arg("./tests/invalid_configs/invalid_colour_hex_3.toml")
|
||||||
.assert()
|
.assert()
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(predicate::str::contains("invalid hex colour"));
|
.stderr(predicate::str::contains("invalid hex color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -89,7 +89,7 @@ fn test_invalid_colour_name() {
|
||||||
.arg("./tests/invalid_configs/invalid_colour_name.toml")
|
.arg("./tests/invalid_configs/invalid_colour_name.toml")
|
||||||
.assert()
|
.assert()
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(predicate::str::contains("invalid named colour"));
|
.stderr(predicate::str::contains("invalid named color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -119,7 +119,7 @@ fn test_invalid_colour_string() {
|
||||||
.arg("./tests/invalid_configs/invalid_colour_string.toml")
|
.arg("./tests/invalid_configs/invalid_colour_string.toml")
|
||||||
.assert()
|
.assert()
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(predicate::str::contains("invalid named colour"));
|
.stderr(predicate::str::contains("invalid named color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
[colors]
|
[colors]
|
||||||
table_header_color="#我死"
|
table_header_color = "#加拿大"
|
||||||
|
|
Loading…
Add table
Reference in a new issue