Apply lightness to preset color profile

This commit is contained in:
Teoh Han Hui 2024-07-03 05:20:48 +08:00
parent 86e442b8a4
commit b0737a33ba
No known key found for this signature in database
GPG key ID: D43E2BABAF97DCAE
11 changed files with 343 additions and 90 deletions

78
Cargo.lock generated
View file

@ -32,6 +32,15 @@ version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
]
[[package]]
name = "autocfg"
version = "1.3.0"
@ -60,6 +69,12 @@ version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "by_address"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06"
[[package]]
name = "cc"
version = "1.0.99"
@ -99,6 +114,26 @@ dependencies = [
"serde",
]
[[package]]
name = "derive_more"
version = "1.0.0-beta.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7abbfc297053be59290e3152f8cbcd52c8642e0728b69ee187d991d4c1af08d"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "1.0.0-beta.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bba3e9872d7c58ce7ef0fcf1844fcc3e23ef2a58377b50df35dd98e42a5726e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "directories"
version = "5.0.1"
@ -126,6 +161,12 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fast-srgb8"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1"
[[package]]
name = "getrandom"
version = "0.2.15"
@ -157,15 +198,17 @@ dependencies = [
"bpaf",
"chrono",
"deranged",
"derive_more",
"directories",
"indexmap",
"palette",
"regex",
"rgb",
"serde",
"serde_json",
"serde_path_to_error",
"shell-words",
"strum",
"thiserror",
"tracing",
"tracing-subscriber",
"unicode-normalization",
@ -303,6 +346,29 @@ version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f"
[[package]]
name = "palette"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6"
dependencies = [
"approx",
"fast-srgb8",
"palette_derive",
]
[[package]]
name = "palette_derive"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30"
dependencies = [
"by_address",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pin-project-lite"
version = "0.2.14"
@ -367,12 +433,6 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "rgb"
version = "0.8.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8"
[[package]]
name = "rustversion"
version = "1.0.17"
@ -407,9 +467,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.118"
version = "1.0.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4"
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
dependencies = [
"itoa",
"ryu",

View file

@ -15,11 +15,13 @@ license = "MIT"
ansi_colours = { version = "1.2.2", default-features = false }
anyhow = { version = "1.0.86", default-features = false }
bpaf = { version = "0.9.12", default-features = false }
bytemuck = { version = "1.16.1", default-features = false }
chrono = { version = "0.4.38", default-features = false }
deranged = { version = "0.3.11", default-features = false }
derive_more = { version = "1.0.0-beta.6", default-features = false }
directories = { version = "5.0.1", default-features = false }
indexmap = { version = "2.2.6", default-features = false }
palette = { version = "0.7.6", default-features = false }
regex = { version = "1.10.5", default-features = false }
rgb = { version = "0.8.37", default-features = false }
serde = { version = "1.0.203", default-features = false }
@ -27,6 +29,7 @@ serde_json = { version = "1.0.118", default-features = false }
serde_path_to_error = { version = "0.1.16", default-features = false }
shell-words = { version = "1.1.0", default-features = false }
strum = { version = "0.26.3", default-features = false }
thiserror = { version = "1.0.61", default-features = false }
tracing = { version = "0.1.40", default-features = false }
tracing-subscriber = { version = "0.3.18", default-features = false }
unicode-normalization = { version = "0.1.23", default-features = false }

View file

@ -13,18 +13,21 @@ default-run = "hyfetch"
# ansi_colours = { workspace = true, features = ["rgb"] }
anyhow = { workspace = true, features = ["std"] }
bpaf = { workspace = true, features = [] }
# bytemuck = { workspace = true, features = [] }
chrono = { workspace = true, features = ["clock", "std"] }
deranged = { workspace = true, features = ["serde", "std"] }
# derive_more = { workspace = true, features = ["std"] }
derive_more = { workspace = true, features = ["from", "from_str", "into", "std"] }
directories = { workspace = true, features = [] }
indexmap = { workspace = true, features = ["serde", "std"] }
palette = { workspace = true, features = ["std"] }
regex = { workspace = true, features = ["perf", "std", "unicode"] }
rgb = { workspace = true, features = [] }
# rgb = { workspace = true, features = [] }
serde = { workspace = true, features = ["derive", "std"] }
serde_json = { workspace = true, features = ["std"] }
serde_path_to_error = { workspace = true, features = [] }
shell-words = { workspace = true, features = ["std"] }
strum = { workspace = true, features = ["derive", "std"] }
thiserror = { workspace = true, features = [] }
tracing = { workspace = true, features = ["attributes", "std"] }
tracing-subscriber = { workspace = true, features = ["ansi", "fmt", "smallvec", "std", "tracing-log"] }

View file

@ -5,16 +5,17 @@ use std::path::Path;
use anyhow::{Context, Result};
use chrono::Datelike;
use directories::ProjectDirs;
use hyfetch::cli_options::options;
use hyfetch::models::Config;
use hyfetch::neofetch_util::get_distro_ascii;
use hyfetch::presets::AssignLightness;
use hyfetch::utils::get_cache_path;
use tracing::debug;
fn main() -> Result<()> {
let options = options().run();
init_tracing_subsriber(options.debug).context("Failed to init tracing subscriber")?;
init_tracing_subsriber(options.debug).context("failed to init tracing subscriber")?;
debug!(?options, "CLI options");
@ -23,7 +24,7 @@ fn main() -> Result<()> {
if options.test_print {
println!(
"{}",
get_distro_ascii(options.distro.as_ref()).context("Failed to get distro ascii")?
get_distro_ascii(options.distro.as_ref()).context("failed to get distro ascii")?
);
return Ok(());
}
@ -31,20 +32,18 @@ fn main() -> Result<()> {
// TODO
let config = if options.config {
create_config(options.config_file).context("Failed to create config")?
create_config(options.config_file).context("failed to create config")?
} else if let Some(config) =
read_config(&options.config_file).context("Failed to read config")?
read_config(&options.config_file).context("failed to read config")?
{
config
} else {
create_config(options.config_file).context("Failed to create config")?
create_config(options.config_file).context("failed to create config")?
};
// Check if it's June (pride month)
let now = chrono::Local::now();
let cache_path = ProjectDirs::from("", "", "hyfetch")
.context("Failed to get base dirs")?
.cache_dir()
.to_owned();
let cache_path = get_cache_path().context("failed to get cache path")?;
let june_path = cache_path.join(format!("animation-displayed-{}", now.year()));
let show_pride_month =
options.june || now.month() == 6 && !june_path.is_file() && io::stdout().is_terminal();
@ -58,14 +57,31 @@ fn main() -> Result<()> {
println!();
if !june_path.is_file() {
fs::create_dir_all(cache_path).context("Failed to create cache dir")?;
fs::create_dir_all(cache_path).context("failed to create cache dir")?;
File::create(&june_path)
.with_context(|| format!("Failed to create file {june_path:?}"))?;
.with_context(|| format!("failed to create file {june_path:?}"))?;
}
}
// TODO
// Get preset
let preset = options.preset.unwrap_or(config.preset);
let color_profile = preset.color_profile();
debug!(?color_profile, "color profile");
// Lighten
let color_profile = if let Some(scale) = options.scale {
color_profile.lighten(scale)
} else if let Some(lightness) = options.lightness {
color_profile.with_lightness(AssignLightness::Replace(lightness))
} else {
color_profile.with_lightness_dl(config.lightness(), config.light_dark)
};
debug!(?color_profile, "lightened color profile");
// TODO
Ok(())
}
@ -85,18 +101,18 @@ where
return Ok(None);
},
Err(err) => {
return Err(err).with_context(|| format!("Failed to open {path:?}"));
return Err(err).with_context(|| format!("failed to open {path:?}"));
},
};
let mut buf = String::new();
file.read_to_string(&mut buf)
.with_context(|| format!("Failed to read {path:?}"))?;
.with_context(|| format!("failed to read {path:?}"))?;
let deserializer = &mut serde_json::Deserializer::from_str(&buf);
let config: Config = serde_path_to_error::deserialize(deserializer)
.with_context(|| format!("Failed to parse {path:?}"))?;
.with_context(|| format!("failed to parse {path:?}"))?;
debug!(?config, "read config");
@ -158,5 +174,5 @@ fn init_tracing_subsriber(debug: bool) -> Result<()> {
subscriber
.try_init()
.context("Failed to set the global default subscriber")
.context("failed to set the global default subscriber")
}

View file

@ -8,6 +8,7 @@ use bpaf::{construct, long, OptionParser, Parser};
use directories::BaseDirs;
use strum::VariantNames;
use crate::color_util::Lightness;
use crate::presets::Preset;
use crate::types::{AnsiMode, Backend};
@ -19,9 +20,9 @@ pub struct Options {
pub mode: Option<AnsiMode>,
pub backend: Option<Backend>,
pub args: Vec<String>,
pub colors_scale: Option<f32>,
pub colors_set_lightness: Option<f32>,
pub colors_use_overlay: bool,
pub scale: Option<f32>,
pub lightness: Option<Lightness>,
pub overlay: bool,
pub june: bool,
pub debug: bool,
pub distro: Option<String>,
@ -42,7 +43,7 @@ pub fn options() -> OptionParser<Options> {
.fallback_with(|| {
Ok::<_, anyhow::Error>(
BaseDirs::new()
.context("Failed to get base dirs")?
.context("failed to get base dirs")?
.config_dir()
.join("hyfetch.json"),
)
@ -107,15 +108,15 @@ BACKEND={{{}}}",
.argument::<String>("ARGS")
.parse(|s| shell_words::split(&s).context("ARGS should be valid command-line arguments"))
.fallback(vec![]);
let colors_scale = long("c-scale")
let scale = long("c-scale")
.help("Lighten colors by a multiplier")
.argument("SCALE")
.optional();
let colors_set_lightness = long("c-set-l")
let lightness = long("c-set-l")
.help("Set lightness value of the colors")
.argument("LIGHT")
.argument("LIGHTNESS")
.optional();
let colors_use_overlay = long("c-overlay")
let overlay = long("c-overlay")
.help("Use experimental overlay color adjusting instead of HSL lightness")
.switch();
let june = long("june").help("Show pride month easter egg").switch();
@ -151,9 +152,9 @@ BACKEND={{{}}}",
mode,
backend,
args,
colors_scale,
colors_set_lightness,
colors_use_overlay,
scale,
lightness,
overlay,
june,
debug,
distro,

View file

@ -1,49 +1,113 @@
use anyhow::{anyhow, Context, Result};
use std::num::ParseFloatError;
use anyhow::Result;
use deranged::RangedU8;
use rgb::RGB8;
use derive_more::{From, FromStr, Into};
use serde::{Deserialize, Serialize};
use thiserror::Error;
/// Represents the lightness component in HSL.
///
/// The range of valid values is
/// `(`[`Lightness::MIN`]`..=`[`Lightness::MAX`]`)`.
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Deserialize, Into, Serialize)]
pub struct Lightness(f32);
#[derive(Debug, Error)]
pub enum LightnessError {
#[error(
"invalid lightness {0}, expected value between {} and {}",
Lightness::MIN,
Lightness::MAX
)]
OutOfRange(f32),
}
#[derive(Debug, Error)]
pub enum ParseLightnessError {
#[error("invalid float")]
InvalidFloat(#[from] ParseFloatError),
#[error("invalid lightness")]
InvalidLightness(#[from] LightnessError),
}
/// An indexed color where the color palette is the set of colors used in
/// neofetch ascii art.
///
/// The range of valid values as supported in neofetch is `1`-`6`.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
pub struct NeofetchAsciiIndexedColor(RangedU8<1, 6>);
/// The range of valid values as supported in neofetch is
/// `(`[`NeofetchAsciiIndexedColor::MIN`]`..
/// =`[`NeofetchAsciiIndexedColor::MAX`]`)`.
#[derive(
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Hash,
Debug,
Deserialize,
From,
FromStr,
Into,
Serialize,
)]
pub struct NeofetchAsciiIndexedColor(
RangedU8<{ NeofetchAsciiIndexedColor::MIN }, { NeofetchAsciiIndexedColor::MAX }>,
);
/// An indexed color where the color palette is the set of unique colors in a
/// preset.
///
/// The range of valid values depends on the number of unique colors in a
/// certain preset.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
#[derive(
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Hash,
Debug,
Deserialize,
From,
FromStr,
Into,
Serialize,
)]
pub struct PresetIndexedColor(usize);
pub trait FromHex {
/// Creates color from hex code.
fn from_hex<S>(hex: S) -> Result<RGB8>
where
S: AsRef<str>;
impl Lightness {
const MAX: f32 = 1.0f32;
const MIN: f32 = 0.0f32;
pub fn new(value: f32) -> Result<Self, LightnessError> {
if !(Self::MIN..=Self::MAX).contains(&value) {
return Err(LightnessError::OutOfRange(value));
}
impl FromHex for RGB8 {
fn from_hex<S>(hex: S) -> Result<RGB8>
where
S: AsRef<str>,
{
let hex = hex.as_ref();
let hex = hex.strip_prefix('#').unwrap_or(hex);
if hex.len() != 6 {
Err(anyhow!("invalid length for hex color"))?;
}
let r =
u8::from_str_radix(&hex[0..2], 16).context("Failed to parse hex color component")?;
let g =
u8::from_str_radix(&hex[2..4], 16).context("Failed to parse hex color component")?;
let b =
u8::from_str_radix(&hex[4..6], 16).context("Failed to parse hex color component")?;
Ok(RGB8::new(r, g, b))
Ok(Self(value))
}
}
impl TryFrom<f32> for Lightness {
type Error = LightnessError;
fn try_from(value: f32) -> Result<Self, Self::Error> {
Lightness::new(value)
}
}
impl FromStr for Lightness {
type Err = ParseLightnessError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Lightness::new(s.parse()?)?)
}
}
impl NeofetchAsciiIndexedColor {
const MAX: u8 = 6;
const MIN: u8 = 1;
}

View file

@ -5,3 +5,4 @@ pub mod models;
pub mod neofetch_util;
pub mod presets;
pub mod types;
pub mod utils;

View file

@ -1,5 +1,6 @@
use serde::{Deserialize, Serialize};
use crate::color_util::Lightness;
use crate::neofetch_util::ColorAlignment;
use crate::presets::Preset;
use crate::types::{AnsiMode, Backend, LightDark};
@ -9,7 +10,7 @@ pub struct Config {
pub preset: Preset,
pub mode: AnsiMode,
pub light_dark: LightDark,
pub lightness: Option<f32>,
lightness: Option<Lightness>,
pub color_align: ColorAlignment,
pub backend: Backend,
#[serde(with = "self::args_serde_with")]
@ -18,6 +19,20 @@ pub struct Config {
pub pride_month_disable: bool,
}
impl Config {
pub fn default_lightness(term: &LightDark) -> Lightness {
match term {
LightDark::Dark => Lightness::new(0.65).unwrap(),
LightDark::Light => Lightness::new(0.4).unwrap(),
}
}
pub fn lightness(&self) -> Lightness {
self.lightness
.unwrap_or_else(|| Self::default_lightness(&self.light_dark))
}
}
mod args_serde_with {
use std::fmt;

View file

@ -16,6 +16,7 @@ use tracing::debug;
use crate::color_util::{NeofetchAsciiIndexedColor, PresetIndexedColor};
use crate::distros::Distro;
const NEOFETCH_COLOR_PATTERN: &str = r"\$\{c[0-9]\}";
static NEOFETCH_COLOR_RE: OnceLock<Regex> = OnceLock::new();
#[derive(Clone, Eq, PartialEq, Debug, Deserialize, Serialize)]
@ -41,14 +42,14 @@ pub fn get_command_path() -> Result<PathBuf> {
let path = path.join("neofetch");
match path.try_exists() {
Ok(true) => {
return path.canonicalize().context("Failed to canonicalize path");
return path.canonicalize().context("failed to canonicalize path");
},
Ok(false) => {
Err(anyhow!("{path:?} does not exist or is not readable"))?;
},
Err(err) => {
Err(err)
.with_context(|| format!("Failed to check for existence of {path:?}"))?;
.with_context(|| format!("failed to check for existence of {path:?}"))?;
},
}
}
@ -63,7 +64,7 @@ pub fn get_command_path() -> Result<PathBuf> {
if !path.is_file() {
continue;
}
return path.canonicalize().context("Failed to canonicalize path");
return path.canonicalize().context("failed to canonicalize path");
}
Err(anyhow!("neofetch command not found"))
@ -80,7 +81,7 @@ where
distro.as_ref().into()
} else {
get_distro_name()
.context("Failed to get distro name")?
.context("failed to get distro name")?
.into()
};
debug!(%distro, "distro name");
@ -101,7 +102,7 @@ where
let Some(width) = NEOFETCH_COLOR_RE
.get_or_init(|| {
Regex::new(r"\$\{c[0-9]\}").expect("neofetch color regex should not be invalid")
Regex::new(NEOFETCH_COLOR_PATTERN).expect("neofetch color regex should not be invalid")
})
.replace_all(asc, "")
.split('\n')
@ -140,11 +141,11 @@ fn run_neofetch_command_piped<S>(args: &[S]) -> Result<String>
where
S: AsRef<OsStr> + fmt::Debug,
{
let mut command = make_neofetch_command(args).context("Failed to make neofetch command")?;
let mut command = make_neofetch_command(args).context("failed to make neofetch command")?;
let output = command
.output()
.context("Failed to execute neofetch as child process")?;
.context("failed to execute neofetch as child process")?;
debug!(?output, "neofetch output");
if !output.status.success() {
@ -168,7 +169,7 @@ where
}
let out = String::from_utf8(output.stdout)
.context("Failed to process neofetch output as it contains invalid UTF-8")?
.context("failed to process neofetch output as it contains invalid UTF-8")?
.trim()
.to_owned();
Ok(out)
@ -181,7 +182,7 @@ where
#[cfg(not(windows))]
{
let mut command = Command::new("bash");
command.arg(get_command_path().context("Failed to get neofetch command path")?);
command.arg(get_command_path().context("failed to get neofetch command path")?);
command.args(args);
Ok(command)
}
@ -194,5 +195,5 @@ where
#[tracing::instrument(level = "debug")]
fn get_distro_name() -> Result<String> {
run_neofetch_command_piped(&["ascii_distro_name"])
.context("Failed to get distro name from neofetch")
.context("failed to get distro name from neofetch")
}

View file

@ -2,13 +2,16 @@ use std::iter;
use anyhow::{anyhow, Context, Result};
use indexmap::IndexSet;
use rgb::RGB8;
use palette::encoding::{self, Linear};
use palette::num::ClampAssign;
use palette::{Hsl, IntoColorMut, LinSrgb, Srgb};
use serde::{Deserialize, Serialize};
use strum::{EnumString, VariantNames};
use crate::color_util::FromHex;
use crate::color_util::Lightness;
use crate::types::LightDark;
#[derive(Clone, Hash, Debug, Deserialize, EnumString, Serialize, VariantNames)]
#[derive(Copy, Clone, Hash, Debug, Deserialize, EnumString, Serialize, VariantNames)]
#[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")]
pub enum Preset {
@ -82,9 +85,16 @@ pub enum Preset {
Xenogender,
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct ColorProfile {
pub colors: Vec<RGB8>,
pub colors: Vec<Srgb<u8>>,
}
#[derive(Clone, PartialEq, Debug)]
pub enum AssignLightness {
Replace(Lightness),
ClampMax(Lightness),
ClampMin(Lightness),
}
impl Preset {
@ -376,7 +386,7 @@ impl Preset {
}
impl ColorProfile {
pub fn new(colors: Vec<RGB8>) -> Self {
pub fn new(colors: Vec<Srgb<u8>>) -> Self {
Self { colors }
}
@ -386,9 +396,9 @@ impl ColorProfile {
{
let colors = hex_colors
.into_iter()
.map(RGB8::from_hex)
.collect::<Result<_>>()
.context("Failed to parse hex colors")?;
.map(|s| s.as_ref().parse())
.collect::<Result<_, _>>()
.context("failed to parse hex colors")?;
Ok(Self::new(colors))
}
@ -414,12 +424,79 @@ impl ColorProfile {
Ok(Self::new(weighted_colors))
}
/// Creates a new color profile, with the colors lightened by a multiplier.
pub fn lighten(&self, multiplier: f32) -> Self {
let mut rgb_f32_colors: Vec<LinSrgb> =
self.colors.iter().map(|c| c.into_linear()).collect();
{
let hsl_f32_colors: &mut [Hsl<Linear<encoding::Srgb>>] =
&mut rgb_f32_colors.into_color_mut();
for hsl_f32_color in hsl_f32_colors {
hsl_f32_color.lightness *= multiplier;
}
}
let rgb_u8_colors: Vec<_> = rgb_f32_colors
.into_iter()
.map(Srgb::<u8>::from_linear)
.collect();
Self {
colors: rgb_u8_colors,
}
}
/// Creates a new color profile, with the colors set to the specified HSL
/// lightness value.
pub fn with_lightness(&self, assign_lightness: AssignLightness) -> Self {
let mut rgb_f32_colors: Vec<_> =
self.colors.iter().map(|c| c.into_format::<f32>()).collect();
{
let hsl_f32_colors: &mut [Hsl] = &mut rgb_f32_colors.into_color_mut();
for hsl_f32_color in hsl_f32_colors {
match assign_lightness {
AssignLightness::Replace(lightness) => {
hsl_f32_color.lightness = lightness.into();
},
AssignLightness::ClampMax(lightness) => {
hsl_f32_color.lightness.clamp_max_assign(lightness.into());
},
AssignLightness::ClampMin(lightness) => {
hsl_f32_color.lightness.clamp_min_assign(lightness.into());
},
}
}
}
let rgb_u8_colors: Vec<_> = rgb_f32_colors
.into_iter()
.map(|c| c.into_format::<u8>())
.collect();
Self {
colors: rgb_u8_colors,
}
}
/// Creates a new color profile, with the colors set to the specified HSL
/// lightness value, with respect to dark/light terminals.
pub fn with_lightness_dl(&self, lightness: Lightness, term: LightDark) -> Self {
match term {
LightDark::Dark => self.with_lightness(AssignLightness::ClampMin(lightness)),
LightDark::Light => self.with_lightness(AssignLightness::ClampMax(lightness)),
}
}
/// Creates another color profile with only the unique colors.
pub fn unique_colors(&self) -> Self {
let unique_colors = self.colors.iter().collect::<IndexSet<_>>();
let unique_colors: IndexSet<[u8; 3]> = self.colors.iter().map(|c| (*c).into()).collect();
let unique_colors = {
let mut v = Vec::with_capacity(unique_colors.len());
v.extend(unique_colors);
v.extend(unique_colors.into_iter().map(Srgb::<u8>::from));
v
};
Self::new(unique_colors)

View file

@ -0,0 +1,12 @@
use std::path::PathBuf;
use anyhow::{Context, Result};
use directories::ProjectDirs;
pub fn get_cache_path() -> Result<PathBuf> {
let path = ProjectDirs::from("", "", "hyfetch")
.context("failed to get base dirs")?
.cache_dir()
.to_owned();
Ok(path)
}