summaryrefslogtreecommitdiffstats
path: root/vendor/gix-config-value/src/color.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
commit10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch)
treebdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix-config-value/src/color.rs
parentReleasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff)
downloadrustc-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.rs346
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))?)
+ }
+}