diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
commit | 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch) | |
tree | bdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix-config-value/src/color.rs | |
parent | Releasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff) | |
download | rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip |
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix-config-value/src/color.rs')
-rw-r--r-- | vendor/gix-config-value/src/color.rs | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/vendor/gix-config-value/src/color.rs b/vendor/gix-config-value/src/color.rs new file mode 100644 index 000000000..74e0dd4ac --- /dev/null +++ b/vendor/gix-config-value/src/color.rs @@ -0,0 +1,346 @@ +#![allow(missing_docs)] +use std::{borrow::Cow, convert::TryFrom, fmt::Display, str::FromStr}; + +use bstr::{BStr, BString}; + +use crate::{Color, Error}; + +impl Display for Color { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut write_space = None; + if let Some(fg) = self.foreground { + fg.fmt(f)?; + write_space = Some(()); + } + + if let Some(bg) = self.background { + if write_space.take().is_some() { + write!(f, " ")?; + } + bg.fmt(f)?; + write_space = Some(()) + } + + if !self.attributes.is_empty() { + if write_space.take().is_some() { + write!(f, " ")?; + } + self.attributes.fmt(f)?; + } + Ok(()) + } +} + +fn color_err(input: impl Into<BString>) -> Error { + Error::new( + "Colors are specific color values and their attributes, like 'brightred', or 'blue'", + input, + ) +} + +impl TryFrom<&BStr> for Color { + type Error = Error; + + fn try_from(s: &BStr) -> Result<Self, Self::Error> { + let s = std::str::from_utf8(s).map_err(|err| color_err(s).with_err(err))?; + enum ColorItem { + Value(Name), + Attr(Attribute), + } + + let items = s.split_whitespace().filter_map(|s| { + if s.is_empty() { + return None; + } + + Some( + Name::from_str(s) + .map(ColorItem::Value) + .or_else(|_| Attribute::from_str(s).map(ColorItem::Attr)), + ) + }); + + let mut foreground = None; + let mut background = None; + let mut attributes = Attribute::empty(); + for item in items { + match item { + Ok(item) => match item { + ColorItem::Value(v) => { + if foreground.is_none() { + foreground = Some(v); + } else if background.is_none() { + background = Some(v); + } else { + return Err(color_err(s)); + } + } + ColorItem::Attr(a) => attributes |= a, + }, + Err(_) => return Err(color_err(s)), + } + } + + Ok(Color { + foreground, + background, + attributes, + }) + } +} + +impl TryFrom<Cow<'_, BStr>> for Color { + type Error = Error; + + fn try_from(c: Cow<'_, BStr>) -> Result<Self, Self::Error> { + Self::try_from(c.as_ref()) + } +} + +/// Discriminating enum for names of [`Color`] values. +/// +/// `gix-config` supports the eight standard colors, their bright variants, an +/// ANSI color code, or a 24-bit hex value prefixed with an octothorpe/hash. +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +#[allow(missing_docs)] +pub enum Name { + Normal, + Default, + Black, + BrightBlack, + Red, + BrightRed, + Green, + BrightGreen, + Yellow, + BrightYellow, + Blue, + BrightBlue, + Magenta, + BrightMagenta, + Cyan, + BrightCyan, + White, + BrightWhite, + Ansi(u8), + Rgb(u8, u8, u8), +} + +impl Display for Name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Normal => write!(f, "normal"), + Self::Default => write!(f, "default"), + Self::Black => write!(f, "black"), + Self::BrightBlack => write!(f, "brightblack"), + Self::Red => write!(f, "red"), + Self::BrightRed => write!(f, "brightred"), + Self::Green => write!(f, "green"), + Self::BrightGreen => write!(f, "brightgreen"), + Self::Yellow => write!(f, "yellow"), + Self::BrightYellow => write!(f, "brightyellow"), + Self::Blue => write!(f, "blue"), + Self::BrightBlue => write!(f, "brightblue"), + Self::Magenta => write!(f, "magenta"), + Self::BrightMagenta => write!(f, "brightmagenta"), + Self::Cyan => write!(f, "cyan"), + Self::BrightCyan => write!(f, "brightcyan"), + Self::White => write!(f, "white"), + Self::BrightWhite => write!(f, "brightwhite"), + Self::Ansi(num) => num.fmt(f), + Self::Rgb(r, g, b) => write!(f, "#{r:02x}{g:02x}{b:02x}"), + } + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for Name { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl FromStr for Name { + type Err = Error; + + fn from_str(mut s: &str) -> Result<Self, Self::Err> { + let bright = if let Some(rest) = s.strip_prefix("bright") { + s = rest; + true + } else { + false + }; + + match s { + "normal" if !bright => return Ok(Self::Normal), + "-1" if !bright => return Ok(Self::Normal), + "normal" if bright => return Err(color_err(s)), + "default" if !bright => return Ok(Self::Default), + "default" if bright => return Err(color_err(s)), + "black" if !bright => return Ok(Self::Black), + "black" if bright => return Ok(Self::BrightBlack), + "red" if !bright => return Ok(Self::Red), + "red" if bright => return Ok(Self::BrightRed), + "green" if !bright => return Ok(Self::Green), + "green" if bright => return Ok(Self::BrightGreen), + "yellow" if !bright => return Ok(Self::Yellow), + "yellow" if bright => return Ok(Self::BrightYellow), + "blue" if !bright => return Ok(Self::Blue), + "blue" if bright => return Ok(Self::BrightBlue), + "magenta" if !bright => return Ok(Self::Magenta), + "magenta" if bright => return Ok(Self::BrightMagenta), + "cyan" if !bright => return Ok(Self::Cyan), + "cyan" if bright => return Ok(Self::BrightCyan), + "white" if !bright => return Ok(Self::White), + "white" if bright => return Ok(Self::BrightWhite), + _ => (), + } + + if let Ok(v) = u8::from_str(s) { + return Ok(Self::Ansi(v)); + } + + if let Some(s) = s.strip_prefix('#') { + if s.len() == 6 { + let rgb = ( + u8::from_str_radix(&s[..2], 16), + u8::from_str_radix(&s[2..4], 16), + u8::from_str_radix(&s[4..], 16), + ); + + if let (Ok(r), Ok(g), Ok(b)) = rgb { + return Ok(Self::Rgb(r, g, b)); + } + } + } + + Err(color_err(s)) + } +} + +impl TryFrom<&BStr> for Name { + type Error = Error; + + fn try_from(s: &BStr) -> Result<Self, Self::Error> { + Self::from_str(std::str::from_utf8(s).map_err(|err| color_err(s).with_err(err))?) + } +} + +bitflags::bitflags! { + /// Discriminating enum for [`Color`] attributes. + /// + /// `gix-config` supports modifiers and their negators. The negating color + /// attributes are equivalent to having a `no` or `no-` prefix to the normal + /// variant. + #[derive(Default)] + pub struct Attribute: u32 { + const BOLD = 1 << 1; + const DIM = 1 << 2; + const ITALIC = 1 << 3; + const UL = 1 << 4; + const BLINK = 1 << 5; + const REVERSE = 1 << 6; + const STRIKE = 1 << 7; + /// Reset is special as we have to be able to parse it, without git actually doing anything with it + const RESET = 1 << 8; + + const NO_DIM = 1 << 21; + const NO_BOLD = 1 << 22; + const NO_ITALIC = 1 << 23; + const NO_UL = 1 << 24; + const NO_BLINK = 1 << 25; + const NO_REVERSE = 1 << 26; + const NO_STRIKE = 1 << 27; + } +} + +impl Display for Attribute { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut write_space = None; + for bit in 1..std::mem::size_of::<Attribute>() * 8 { + let attr = match Attribute::from_bits(1 << bit) { + Some(attr) => attr, + None => continue, + }; + if self.contains(attr) { + if write_space.take().is_some() { + write!(f, " ")? + } + match attr { + Attribute::RESET => write!(f, "reset"), + Attribute::BOLD => write!(f, "bold"), + Attribute::NO_BOLD => write!(f, "nobold"), + Attribute::DIM => write!(f, "dim"), + Attribute::NO_DIM => write!(f, "nodim"), + Attribute::UL => write!(f, "ul"), + Attribute::NO_UL => write!(f, "noul"), + Attribute::BLINK => write!(f, "blink"), + Attribute::NO_BLINK => write!(f, "noblink"), + Attribute::REVERSE => write!(f, "reverse"), + Attribute::NO_REVERSE => write!(f, "noreverse"), + Attribute::ITALIC => write!(f, "italic"), + Attribute::NO_ITALIC => write!(f, "noitalic"), + Attribute::STRIKE => write!(f, "strike"), + Attribute::NO_STRIKE => write!(f, "nostrike"), + _ => unreachable!("BUG: add new attribute flag"), + }?; + write_space = Some(()); + } + } + Ok(()) + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for Attribute { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl FromStr for Attribute { + type Err = Error; + + fn from_str(mut s: &str) -> Result<Self, Self::Err> { + let inverted = if let Some(rest) = s.strip_prefix("no-").or_else(|| s.strip_prefix("no")) { + s = rest; + true + } else { + false + }; + + match s { + "reset" if !inverted => Ok(Attribute::RESET), + "reset" if inverted => Err(color_err(s)), + "bold" if !inverted => Ok(Attribute::BOLD), + "bold" if inverted => Ok(Attribute::NO_BOLD), + "dim" if !inverted => Ok(Attribute::DIM), + "dim" if inverted => Ok(Attribute::NO_DIM), + "ul" if !inverted => Ok(Attribute::UL), + "ul" if inverted => Ok(Attribute::NO_UL), + "blink" if !inverted => Ok(Attribute::BLINK), + "blink" if inverted => Ok(Attribute::NO_BLINK), + "reverse" if !inverted => Ok(Attribute::REVERSE), + "reverse" if inverted => Ok(Attribute::NO_REVERSE), + "italic" if !inverted => Ok(Attribute::ITALIC), + "italic" if inverted => Ok(Attribute::NO_ITALIC), + "strike" if !inverted => Ok(Attribute::STRIKE), + "strike" if inverted => Ok(Attribute::NO_STRIKE), + _ => Err(color_err(s)), + } + } +} + +impl TryFrom<&BStr> for Attribute { + type Error = Error; + + fn try_from(s: &BStr) -> Result<Self, Self::Error> { + Self::from_str(std::str::from_utf8(s).map_err(|err| color_err(s).with_err(err))?) + } +} |