yacll 0.5.0

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()
        .foreground(Color::Red)
        .background(Color::Blue)
        .underline(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::Color;
pub use crossterm::style::ContentStyle as Style;
use crossterm::{
    cursor::{CursorShape, DisableBlinking, EnableBlinking, Hide, SetCursorShape, Show},
    queue,
    style::{Attribute, Attributes, SetStyle},
};

/// Style builder.
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 foreground(&mut self, color: Color) -> &mut Self {
        self.foreground = Some(color);
        self
    }
    /// Set the background color.
    pub fn background(&mut self, color: Color) -> &mut Self {
        self.background = Some(color);
        self
    }
    /// Set the underline color.
    pub fn underline(&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(&mut self) -> Style {
        Style {
            foreground_color: self.foreground,
            background_color: self.background,
            underline_color: self.underline,
            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()
        .foreground(Color::Reset)
        .background(Color::Reset)
        .underline(Color::Reset)
        .attr(Attr::Reset)
        .style()
}

/// Possible cursor attributes. See also [`cursor_off`](Yogurt::cursor_off) and
/// [`cursor_on`](Yogurt::cursor_on).
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)]
        #[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,
        }
    }
}