File size: 3,476 Bytes
d90101d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | //! ZSH prompt styling utilities.
//!
//! This module provides helpers for generating ZSH-native prompt escape
//! sequences. Unlike ANSI escape codes, ZSH prompt escapes are interpreted by
//! ZSH's prompt renderer, making them work correctly in PROMPT and RPROMPT
//! contexts.
use std::fmt::{self, Display};
/// ZSH prompt color using 256-color palette.
///
/// Maps to ZSH's `%F{N}` prompt escape sequence where N is a color code.
#[derive(Debug, Clone, Copy)]
pub struct ZshColor(u8);
impl ZshColor {
/// White (color 15)
pub const WHITE: Self = Self(15);
/// Cyan (color 134)
pub const CYAN: Self = Self(134);
/// Green (color 2)
pub const GREEN: Self = Self(2);
/// Yellow (color 3)
pub const YELLOW: Self = Self(3);
/// Dimmed gray (color 240)
pub const DIMMED: Self = Self(240);
/// Creates a color from a 256-color palette value.
#[cfg(test)]
pub const fn new(value: u8) -> Self {
Self(value)
}
}
impl Display for ZshColor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
/// A styled string for ZSH prompts.
///
/// Wraps text with ZSH prompt escape sequences for colors and formatting.
#[derive(Debug, Clone)]
pub struct ZshStyled<'a> {
text: &'a str,
fg: Option<ZshColor>,
bold: bool,
}
impl<'a> ZshStyled<'a> {
/// Creates a new styled string with the given text.
pub fn new(text: &'a str) -> Self {
Self { text, fg: None, bold: false }
}
/// Sets the foreground color.
pub fn fg(mut self, color: ZshColor) -> Self {
self.fg = Some(color);
self
}
/// Makes the text bold.
pub fn bold(mut self) -> Self {
self.bold = true;
self
}
}
impl Display for ZshStyled<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Opening escapes
if self.bold {
write!(f, "%B")?;
}
if let Some(ref color) = self.fg {
write!(f, "%F{{{}}}", color)?;
}
// Text content
write!(f, "{}", self.text)?;
// Closing escapes (in reverse order)
if self.fg.is_some() {
write!(f, "%f")?;
}
if self.bold {
write!(f, "%b")?;
}
Ok(())
}
}
/// Extension trait for styling strings for ZSH prompts.
pub trait ZshStyle {
/// Creates a ZSH-styled wrapper for this string.
fn zsh(&self) -> ZshStyled<'_>;
}
impl ZshStyle for str {
fn zsh(&self) -> ZshStyled<'_> {
ZshStyled::new(self)
}
}
impl ZshStyle for String {
fn zsh(&self) -> ZshStyled<'_> {
ZshStyled::new(self.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_plain_text() {
let actual = "hello".zsh().to_string();
let expected = "hello";
assert_eq!(actual, expected);
}
#[test]
fn test_bold() {
let actual = "hello".zsh().bold().to_string();
let expected = "%Bhello%b";
assert_eq!(actual, expected);
}
#[test]
fn test_bold_and_color() {
let actual = "hello".zsh().bold().fg(ZshColor::WHITE).to_string();
let expected = "%B%F{15}hello%f%b";
assert_eq!(actual, expected);
}
#[test]
fn test_fixed_color() {
let actual = "hello".zsh().fg(ZshColor::new(240)).to_string();
let expected = "%F{240}hello%f";
assert_eq!(actual, expected);
}
}
|