diff options
Diffstat (limited to 'vendor/clap/src/builder/styled_str.rs')
-rw-r--r-- | vendor/clap/src/builder/styled_str.rs | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/vendor/clap/src/builder/styled_str.rs b/vendor/clap/src/builder/styled_str.rs new file mode 100644 index 000000000..d36329dee --- /dev/null +++ b/vendor/clap/src/builder/styled_str.rs @@ -0,0 +1,349 @@ +/// Terminal-styling container +/// +/// For now, this is the same as a [`Str`][crate::builder::Str]. This exists to reserve space in +/// the API for exposing terminal styling. +#[derive(Clone, Default, Debug, PartialEq, Eq)] +pub struct StyledStr { + #[cfg(feature = "color")] + pieces: Vec<(Option<Style>, String)>, + #[cfg(not(feature = "color"))] + pieces: String, +} + +impl StyledStr { + /// Create an empty buffer + #[cfg(feature = "color")] + pub const fn new() -> Self { + Self { pieces: Vec::new() } + } + + /// Create an empty buffer + #[cfg(not(feature = "color"))] + pub const fn new() -> Self { + Self { + pieces: String::new(), + } + } + + /// Display using [ANSI Escape Code](https://en.wikipedia.org/wiki/ANSI_escape_code) styling + #[cfg(feature = "color")] + pub fn ansi(&self) -> impl std::fmt::Display + '_ { + AnsiDisplay { styled: self } + } + + pub(crate) fn header(&mut self, msg: impl Into<String>) { + self.stylize_(Some(Style::Header), msg.into()); + } + + pub(crate) fn literal(&mut self, msg: impl Into<String>) { + self.stylize_(Some(Style::Literal), msg.into()); + } + + pub(crate) fn placeholder(&mut self, msg: impl Into<String>) { + self.stylize_(Some(Style::Placeholder), msg.into()); + } + + #[cfg_attr(not(feature = "error-context"), allow(dead_code))] + pub(crate) fn good(&mut self, msg: impl Into<String>) { + self.stylize_(Some(Style::Good), msg.into()); + } + + #[cfg_attr(not(feature = "error-context"), allow(dead_code))] + pub(crate) fn warning(&mut self, msg: impl Into<String>) { + self.stylize_(Some(Style::Warning), msg.into()); + } + + pub(crate) fn error(&mut self, msg: impl Into<String>) { + self.stylize_(Some(Style::Error), msg.into()); + } + + #[allow(dead_code)] + pub(crate) fn hint(&mut self, msg: impl Into<String>) { + self.stylize_(Some(Style::Hint), msg.into()); + } + + pub(crate) fn none(&mut self, msg: impl Into<String>) { + self.stylize_(None, msg.into()); + } + + pub(crate) fn stylize(&mut self, style: impl Into<Option<Style>>, msg: impl Into<String>) { + self.stylize_(style.into(), msg.into()); + } + + pub(crate) fn trim(&mut self) { + self.trim_start(); + self.trim_end(); + } + + pub(crate) fn trim_start(&mut self) { + if let Some((_, item)) = self.iter_mut().next() { + *item = item.trim_start().to_owned(); + } + } + + #[cfg(feature = "color")] + pub(crate) fn trim_end(&mut self) { + if let Some((_, item)) = self.pieces.last_mut() { + *item = item.trim_end().to_owned(); + } + } + + #[cfg(not(feature = "color"))] + pub(crate) fn trim_end(&mut self) { + self.pieces = self.pieces.trim_end().to_owned(); + } + + #[cfg(feature = "help")] + pub(crate) fn indent(&mut self, initial: &str, trailing: &str) { + if let Some((_, first)) = self.iter_mut().next() { + first.insert_str(0, initial); + } + let mut line_sep = "\n".to_owned(); + line_sep.push_str(trailing); + for (_, content) in self.iter_mut() { + *content = content.replace('\n', &line_sep); + } + } + + #[cfg(all(not(feature = "wrap_help"), feature = "help"))] + pub(crate) fn wrap(&mut self, _hard_width: usize) {} + + #[cfg(feature = "wrap_help")] + pub(crate) fn wrap(&mut self, hard_width: usize) { + let mut wrapper = crate::output::textwrap::wrap_algorithms::LineWrapper::new(hard_width); + for (_, content) in self.iter_mut() { + let mut total = Vec::new(); + for (i, line) in content.split_inclusive('\n').enumerate() { + if 0 < i { + // start of a section does not imply newline + wrapper.reset(); + } + let line = crate::output::textwrap::word_separators::find_words_ascii_space(line) + .collect::<Vec<_>>(); + total.extend(wrapper.wrap(line)); + } + let total = total.join(""); + *content = total; + } + + self.trim_end(); + } + + #[cfg(feature = "color")] + fn stylize_(&mut self, style: Option<Style>, msg: String) { + if !msg.is_empty() { + self.pieces.push((style, msg)); + } + } + + #[cfg(not(feature = "color"))] + fn stylize_(&mut self, _style: Option<Style>, msg: String) { + self.pieces.push_str(&msg); + } + + #[inline(never)] + #[cfg(feature = "help")] + pub(crate) fn display_width(&self) -> usize { + let mut width = 0; + for (_, c) in self.iter() { + width += crate::output::display_width(c); + } + width + } + + #[cfg(feature = "help")] + pub(crate) fn is_empty(&self) -> bool { + self.pieces.is_empty() + } + + #[cfg(feature = "color")] + pub(crate) fn iter(&self) -> impl Iterator<Item = (Option<Style>, &str)> { + self.pieces.iter().map(|(s, c)| (*s, c.as_str())) + } + + #[cfg(not(feature = "color"))] + pub(crate) fn iter(&self) -> impl Iterator<Item = (Option<Style>, &str)> { + [(None, self.pieces.as_str())].into_iter() + } + + #[cfg(feature = "color")] + pub(crate) fn iter_mut(&mut self) -> impl Iterator<Item = (Option<Style>, &mut String)> { + self.pieces.iter_mut().map(|(s, c)| (*s, c)) + } + + #[cfg(not(feature = "color"))] + pub(crate) fn iter_mut(&mut self) -> impl Iterator<Item = (Option<Style>, &mut String)> { + [(None, &mut self.pieces)].into_iter() + } + + #[cfg(feature = "color")] + pub(crate) fn into_iter(self) -> impl Iterator<Item = (Option<Style>, String)> { + self.pieces.into_iter() + } + + #[cfg(not(feature = "color"))] + pub(crate) fn into_iter(self) -> impl Iterator<Item = (Option<Style>, String)> { + [(None, self.pieces)].into_iter() + } + + pub(crate) fn extend( + &mut self, + other: impl IntoIterator<Item = (impl Into<Option<Style>>, impl Into<String>)>, + ) { + for (style, content) in other { + self.stylize(style.into(), content.into()); + } + } + + #[cfg(feature = "color")] + pub(crate) fn write_colored(&self, buffer: &mut termcolor::Buffer) -> std::io::Result<()> { + use std::io::Write; + use termcolor::WriteColor; + + for (style, content) in &self.pieces { + let mut color = termcolor::ColorSpec::new(); + match style { + Some(Style::Header) => { + color.set_bold(true); + color.set_underline(true); + } + Some(Style::Literal) => { + color.set_bold(true); + } + Some(Style::Placeholder) => {} + Some(Style::Good) => { + color.set_fg(Some(termcolor::Color::Green)); + } + Some(Style::Warning) => { + color.set_fg(Some(termcolor::Color::Yellow)); + } + Some(Style::Error) => { + color.set_fg(Some(termcolor::Color::Red)); + color.set_bold(true); + } + Some(Style::Hint) => { + color.set_dimmed(true); + } + None => {} + } + + ok!(buffer.set_color(&color)); + ok!(buffer.write_all(content.as_bytes())); + ok!(buffer.reset()); + } + + Ok(()) + } +} + +impl Default for &'_ StyledStr { + fn default() -> Self { + static DEFAULT: StyledStr = StyledStr::new(); + &DEFAULT + } +} + +impl From<std::string::String> for StyledStr { + fn from(name: std::string::String) -> Self { + let mut styled = StyledStr::new(); + styled.none(name); + styled + } +} + +impl From<&'_ std::string::String> for StyledStr { + fn from(name: &'_ std::string::String) -> Self { + let mut styled = StyledStr::new(); + styled.none(name); + styled + } +} + +impl From<&'static str> for StyledStr { + fn from(name: &'static str) -> Self { + let mut styled = StyledStr::new(); + styled.none(name); + styled + } +} + +impl From<&'_ &'static str> for StyledStr { + fn from(name: &'_ &'static str) -> Self { + StyledStr::from(*name) + } +} + +impl PartialOrd for StyledStr { + fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for StyledStr { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.iter().map(cmp_key).cmp(other.iter().map(cmp_key)) + } +} + +fn cmp_key(c: (Option<Style>, &str)) -> (Option<usize>, &str) { + let style = c.0.map(|s| s.as_usize()); + let content = c.1; + (style, content) +} + +/// Color-unaware printing. Never uses coloring. +impl std::fmt::Display for StyledStr { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + for (_, content) in self.iter() { + ok!(std::fmt::Display::fmt(content, f)); + } + + Ok(()) + } +} + +#[cfg(feature = "color")] +struct AnsiDisplay<'s> { + styled: &'s StyledStr, +} + +#[cfg(feature = "color")] +impl std::fmt::Display for AnsiDisplay<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let mut buffer = termcolor::Buffer::ansi(); + ok!(self + .styled + .write_colored(&mut buffer) + .map_err(|_| std::fmt::Error)); + let buffer = buffer.into_inner(); + let buffer = ok!(String::from_utf8(buffer).map_err(|_| std::fmt::Error)); + ok!(std::fmt::Display::fmt(&buffer, f)); + + Ok(()) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum Style { + Header, + Literal, + Placeholder, + Good, + Warning, + Error, + Hint, +} + +impl Style { + fn as_usize(&self) -> usize { + match self { + Self::Header => 0, + Self::Literal => 1, + Self::Placeholder => 2, + Self::Good => 3, + Self::Warning => 4, + Self::Error => 5, + Self::Hint => 6, + } + } +} |