yacll 0.7.1

Yet Another Curses-Like Library
Documentation
/*!
Enums and structs used for stylizing text and the cursor.
```
use yacll::stylize::{Styler, Style, Color, Attr, reset};
use yacll::Yogurt;

// my very cool (and not at all unreadable) style
fn cool_style() -> Style {
    Styler::new()
        .fg(Color::Red)
        .bg(Color::Blue)
        .ul(Color::Green)
        .attr(Attr::Underlined)
        .attr(Attr::Overlined)
        .attr(Attr::Strikethrough)
        .attr(Attr::Bold)
        .attr(Attr::Italic)
        .style()
}

fn main() -> yacll::Result<()> {
    let mut y = Yogurt::new();
    y.style(cool_style());
    y.print("Hello, world!\n")?;
    y.style(reset());
    y.flush()?;
    Ok(())
}
// try running `$ cargo run --example=style` if you have the repository cloned!
```
*/

/// Border to use with [`draw_box`](Yogurt::draw_box).
pub const BORDER: [char; 8] = ['', '', '', '', '', '', '', ''];
/// Border to use with [`draw_box`](Yogurt::draw_box).
pub const ROUND_BORDER: [char; 8] = ['', '', '', '', '', '', '', ''];

use crate::{Result, Yogurt};
pub use crossterm::style::ContentStyle as Style;
use crossterm::{
    cursor::{CursorShape, DisableBlinking, EnableBlinking, Hide, SetCursorShape, Show},
    queue,
    style::{Attribute, Attributes, SetStyle, self},
};

/// Represents a color.
///
/// # Platform-specific Notes
///
/// The following list of 16 base colors are available for almost all terminals (Windows 7 and 8 included).
///
/// | Light | Dark |
/// | :--| :--   |
/// | `DarkGrey` | `Black` |
/// | `Red` | `DarkRed` |
/// | `Green` | `DarkGreen` |
/// | `Yellow` | `DarkYellow` |
/// | `Blue` | `DarkBlue` |
/// | `Magenta` | `DarkMagenta` |
/// | `Cyan` | `DarkCyan` |
/// | `White` | `Grey` |
///
/// Most UNIX terminals and Windows 10 consoles support additional colors.
/// See [`Color::Rgb`] or [`Color::AnsiValue`] for more info.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum Color {
    /// Resets the terminal color.
    Reset,
    /// Black color.
    Black,
    /// Dark grey color.
    DarkGrey,
    /// Light red color.
    Red,
    /// Dark red color.
    DarkRed,
    /// Light green color.
    Green,
    /// Dark green color.
    DarkGreen,
    /// Light yellow color.
    Yellow,
    /// Dark yellow color.
    DarkYellow,
    /// Light blue color.
    Blue,
    /// Dark blue color.
    DarkBlue,
    /// Light magenta color.
    Magenta,
    /// Dark magenta color.
    DarkMagenta,
    /// Light cyan color.
    Cyan,
    /// Dark cyan color.
    DarkCyan,
    /// White color.
    White,
    /// Grey color.
    Grey,
    /// An RGB color. See [RGB color model](https://en.wikipedia.org/wiki/RGB_color_model) for more info.
    ///
    /// Most UNIX terminals and Windows 10 supported only.
    /// See [Platform-specific notes](enum.Color.html#platform-specific-notes) for more info.
    #[allow(missing_docs)]
    Rgb { r: u8, g: u8, b: u8 },
    /// An ANSI color. See [256 colors - cheat sheet](https://jonasjacek.github.io/colors/) for more info.
    ///
    /// Most UNIX terminals and Windows 10 supported only.
    /// See [Platform-specific notes](enum.Color.html#platform-specific-notes) for more info.
    AnsiValue(u8),
}

impl From<Color> for style::Color {
    fn from(col: Color) -> Self {
        use Color::*;
        match col {
            Reset => Self::Reset,
            Black => Self::Black,
            DarkGrey => Self::DarkGrey,
            Red => Self::Red,
            DarkRed => Self::DarkRed,
            Green => Self::Green,
            DarkGreen => Self::DarkGreen,
            Yellow => Self::Yellow,
            DarkYellow => Self::DarkYellow,
            Blue => Self::Blue,
            DarkBlue => Self::DarkBlue,
            Magenta => Self::Magenta,
            DarkMagenta => Self::DarkMagenta,
            Cyan => Self::Cyan,
            DarkCyan => Self::DarkCyan,
            White => Self::White,
            Grey => Self::Grey,
            Rgb { r, g, b } => Self::Rgb { r, g, b },
            AnsiValue(u) => Self::AnsiValue(u),

        }
    }
}

/// Style builder.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct Styler {
    foreground: Option<Color>,
    background: Option<Color>,
    underline: Option<Color>,
    attributes: Vec<Attr>,
}

impl Default for Styler {
    fn default() -> Self {
        Styler::new()
    }
}

impl Styler {
    /// Construct a new `Styler`.
    pub fn new() -> Self {
        Styler {
            foreground: None,
            background: None,
            underline: None,
            attributes: Vec::new(),
        }
    }
    /// Set the foreground color.
    pub fn fg(&mut self, color: Color) -> &mut Self {
        self.foreground = Some(color);
        self
    }
    /// Set the background color.
    pub fn bg(&mut self, color: Color) -> &mut Self {
        self.background = Some(color);
        self
    }
    /// Set the underline color.
    pub fn ul(&mut self, color: Color) -> &mut Self {
        self.underline = Some(color);
        self
    }
    /// Add an attribute to the list.
    pub fn attr(&mut self, attr: Attr) -> &mut Self {
        self.attributes.push(attr);
        self
    }
    /// Convert into a `Style`.
    pub fn style(&self) -> Style {
        Style {
            foreground_color: self.foreground.clone().map(|c| c.into()),
            background_color: self.background.clone().map(|c| c.into()),
            underline_color: self.underline.clone().map(|c| c.into()),
            attributes: Attributes::from(
                self.attributes
                    .iter()
                    .map(|a| <Attr as Into<Attribute>>::into(*a))
                    .collect::<Vec<_>>()
                    .as_slice(),
            ),
        }
    }
}

/// Returns a style that resets all colors and attributes.
pub fn reset() -> Style {
    Styler::new()
        .fg(Color::Reset)
        .bg(Color::Reset)
        .ul(Color::Reset)
        .attr(Attr::Reset)
        .style()
}

/// Possible cursor attributes. See also [`cursor_off`](Yogurt::cursor_off) and
/// [`cursor_on`](Yogurt::cursor_on).
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum CursorAttr {
    /// Sets the cursor shape to an underscore.
    UnderScore,
    /// Sets the cursor shape to a line.
    Line,
    /// Sets the cursor shape to a block.
    Block,
    /// Makes cursor blink.
    Blinking,
    /// Makes text hidden.
    Hidden,
}

/// Styling of text and cursor.
impl Yogurt {
    /// Turn on a cursor attribute.
    pub fn cursor_on(&mut self, cursor: CursorAttr) -> Result<()> {
        match cursor {
            CursorAttr::UnderScore => {
                queue! { self.stdout, SetCursorShape(CursorShape::UnderScore) }?
            }
            CursorAttr::Line => queue! { self.stdout, SetCursorShape(CursorShape::Line) }?,
            CursorAttr::Block => queue! { self.stdout, SetCursorShape(CursorShape::Block) }?,
            CursorAttr::Blinking => queue! { self.stdout, EnableBlinking }?,
            CursorAttr::Hidden => queue! { self.stdout, Hide }?,
        }
        Ok(())
    }
    /// Turn off a cursor attribute. Turning off a cursor shape resets it to [`Block`](CursorAttr::Block).
    pub fn cursor_off(&mut self, cursor: CursorAttr) -> Result<()> {
        match cursor {
            CursorAttr::UnderScore | CursorAttr::Line | CursorAttr::Block => {
                queue! { self.stdout, SetCursorShape(CursorShape::Block) }?
            }
            CursorAttr::Blinking => queue! { self.stdout, DisableBlinking }?,
            CursorAttr::Hidden => queue! { self.stdout, Show }?,
        }
        Ok(())
    }
    /// Enable a style.
    pub fn style(&mut self, style: Style) -> Result<()> {
        queue! {
            self.stdout,
            SetStyle(style),
        }?;
        Ok(())
    }
}

// copypasted from crossterm https://docs.rs/crossterm/0.25.0/src/crossterm/style/types/attribute.rs.html#8
// why make a custom attribute type? cuz there's too much extra stuff in the crossterm one
// (like for example how is fraktur even converted to monospace? i doubt any terminals actually support it)
macro_rules! Attribute {
    (
        $(
            $(#[$inner:ident $($args:tt)*])*
            $name:ident = $sgr:expr,
        )*
    ) => {
        /// Represents an attribute.
        ///
        /// | Attribute | Windows | UNIX | Notes |
        /// | :-- | :--: | :--: | :-- |
        /// | `Reset` | ✓ | ✓ | |
        /// | `Bold` | ✓ | ✓ | |
        /// | `Dim` | ✓ | ✓ | |
        /// | `Italic` | ? | ? | Not widely supported, sometimes treated as inverse. |
        /// | `Underlined` | ✓ | ✓ | |
        /// | `Blink` | ? | ? | Not widely supported, sometimes treated as inverse. |
        /// | `Reverse` | ✓ | ✓ | |
        /// | `Hidden` | ✓ | ✓ | Also known as Conceal. |
        #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
        #[non_exhaustive]
        pub enum Attr {
            $(
                $(#[$inner $($args)*])*
                $name,
            )*
        }

        // there used to be some SGR thingy here but i removed that because it wasn't used anywhere

        impl Attr {
            /// Iterates over all the variants of the Attr enum.
            pub fn iter() -> impl Iterator<Item = Attr> {
                use self::Attr::*;
                [ $($name,)* ].iter().copied()
            }
        }
    }
}

Attribute! {
    /// Resets all the attributes.
    Reset = 0,
    /// Increases the text intensity.
    Bold = 1,
    /// Decreases the text intensity.
    Dim = 2,
    /// Emphasises the text.
    Italic = 3,
    /// Underlines the text.
    Underlined = 4,
    /// Makes the text blink.
    Blink = 5,
    /// Swaps foreground and background colors.
    Inverse = 7,
    /// Hides the text (also known as Conceal).
    Hidden = 8,
    /// Crosses out the text.
    Strikethrough = 9,
    /// Draws a line over the top of the text.
    Overlined = 53,
}

impl From<Attr> for Attribute {
    fn from(attr: Attr) -> Self {
        use Attr::*;
        match attr {
            Reset => Attribute::Reset,
            Bold => Attribute::Bold,
            Dim => Attribute::Dim,
            Italic => Attribute::Italic,
            Underlined => Attribute::Underlined,
            Blink => Attribute::SlowBlink,
            Inverse => Attribute::Reverse,
            Hidden => Attribute::Hidden,
            Strikethrough => Attribute::CrossedOut,
            Overlined => Attribute::OverLined,
        }
    }
}