Crate ansiconst

Source
Expand description

§Ansi Constants

A library for declaring nestable ANSI styles in const context.

§Motivation

The primary motivation of this crate is to provide the ability to use ANSI colours/effects in command-line programs by identifying them semantically rather than literally.

For example, when calling println!, instead of applying the style Green, Bold to the output, it has more semantic meaning to apply a style named Subheading that, when rendered (or “formatted” in Rust), produces the output that activates the Green, Bold styling.

Furthermore, semantic styles should be nestable, like styling frameworks such as HTML’s CSS. For example, it should be possible to nest text styled with a name like Manufacturer inside text styled with a name like Product Details and have the literal styles that these semantic styles translate to applied automatically. The nested style’s attributes should temporarily layer on top of, or maybe replace, the parent style’s attributes as appropriate.

The second key motivation of this crate is to support the above at compile-time. I.e. define semantic styles as const, and then use them with minimal overhead throughout a command-line program.

There are other crates that provide terminal-styling functionality, but none appear to fully support the use-case outlined above. The API provided by this crate in support of this use-case is presented in the following section.

§Key Features

§Compile-Time

Declare ANSI codes as const. This means the compiler will inline them wherever they are used, potentially improving runtime performance.

§Examples
use ansiconst::*;
use ansiconst::Colour::{Green, Blue};
use ansiconst::Effect::{Bold, Underline, Italic};

// Define styles as Ansi structs:
const    HEADING_ANSI: Ansi = ansi!(Green, Bold, Underline);
const SUBHEADING_ANSI: Ansi = ansi!(Blue, Italic);
const      RESET_ANSI: Ansi = Ansi::reset();

assert_eq!(   HEADING_ANSI.to_string(), "\x1B[1;4;32m");
assert_eq!(SUBHEADING_ANSI.to_string(), "\x1B[3;34m");
assert_eq!(     RESET_ANSI.to_string(), "\x1B[0m");

// Or, define styles as ANSI codes:
const    HEADING_CODE: &str = ansi_code!(Green, Bold, Underline);
const SUBHEADING_CODE: &str = ansi_code!(Blue, Italic);
const      RESET_CODE: &str = ansi_code!(Ansi::reset());

assert_eq!(               HEADING_CODE, "\x1B[1;4;32m");
assert_eq!(            SUBHEADING_CODE, "\x1B[3;34m");
assert_eq!(                 RESET_CODE, "\x1B[0m");

§Small

Ansi instances are designed to be as small as possible. For example, Effects are represented internally using bit flags rather than simple bools.

For this reason, the use of Ansi256 and Rgb colours is gated behind feature flags, because supporting them means Ansi instances must be ever so slightly bigger. Consider the memory sizes:

TypeBytes
Ansi6
Ansi feature=Ansi2568
Ansi feature=Rgb12
&'static str16

§Simple Macros

Apply ANSI codes using macros:

§Examples
use ansiconst::{*, Colour::Red, Effect::Bold};

let pet = "cat";
let age = 5;
let string1 =             styled!(Red, Bold, "My cat is 5 years old").to_string();
let string2 =      styled_format!(Red, Bold, "My {} is {} years old", pet, age);
let string3 = styled_format_args!(Red, Bold, "My {} is {} years old", pet, age).to_string();

assert_eq!(string1, "\x1B[1;31mMy cat is 5 years old\x1B[22;39m");
assert_eq!(string2, "\x1B[1;31mMy cat is 5 years old\x1B[22;39m");
assert_eq!(string3, "\x1B[1;31mMy cat is 5 years old\x1B[22;39m");

// Print "\x1B[1;31mMy cat is 5 years old\x1B[22;39m\n" to stdout and stderr:
paintln! (Red, Bold, "My {} is {} years old", pet, age);
epaintln!(Red, Bold, "My {} is {} years old", pet, age);

// Write "\x1B[1;31mMy cat is 5 years old\x1B[22;39m\n" to a writer:
use std::fmt::Write;
let mut sink = String::new();
styled_writeln!(&mut sink, Red, Bold, "My {} is {} years old", pet, age).unwrap();
assert_eq!(sink, "\x1B[1;31mMy cat is 5 years old\x1B[22;39m\n");

§Effortless Nesting

Nesting of ANSI codes is automatically handled, and uses the minimum ANSI code sequences when transitioning between nesting levels.

Additionally, nested ANSI codes can be disabled entirely, or on a per-attribute basis. Parent Ansis can prevent nested Ansis from rendering ANSI codes for any/all attributes by protecting those attributes in the outer Ansi, using methods such as .protect_attrs() and .only().

§Examples
use ansiconst::{*, Effect::{Bold, Underline}};

const INNER:           Styled<&str> = styled!(Underline,        "Inner");
const INNER_PROTECTED: Styled<&str> = styled!(Underline.only(), "Inner");

// Example 1: blended styles
assert_eq!(
    styled_format!(Bold, "Bold {INNER} Bold again"),
    // "Inner" is both Bold and Underline
    "\x1B[1mBold \x1B[4mInner\x1B[24m Bold again\x1B[22m"
);

// Example 2: protected inner style
assert_eq!(
    styled_format!(Bold, "Bold {INNER_PROTECTED} Bold again"),
    // "Inner" is not Bold, only Underline, due to inner's .only()
    "\x1B[1mBold \x1B[22;4mInner\x1B[24;1m Bold again\x1B[22m"
);

// Example 3: protected outer style
assert_eq!(
    // Note: outer Bold.only() this time
    styled_format!(Bold.only(), "Bold {INNER} Bold again"),
    // Entire string is Bold, nested Underline was ignored
    "\x1B[1mBold Inner Bold again\x1B[22m"
);

// Example 4: both protected
assert_eq!(
    // Note: Bold.only() again
    styled_format!(Bold.only(), "Bold {INNER_PROTECTED} Bold again"),
    // Entire string is Bold, because outer's .only() takes precedence over inner's
    "\x1B[1mBold Inner Bold again\x1B[22m"
);

Note: automatic handling of nested styles is achieved by storing the last-applied ANSI style in a thread_local! static variable, and therefore this library requires std. See Styled<T> for details.

§Examples

use ansiconst::*;
use ansiconst::Colour::{Green, Cyan, Yellow, Purple};
use ansiconst::Effect::{Bold, NotBold, Italic, Underline, Blink};

const HEADING:    Ansi = ansi!(Green, Bold, Underline);
const SUBHEADING: Ansi = ansi!(Cyan, Italic);
const STRONG:     Ansi = ansi!(Yellow, Bold);
const STRONGER:   Ansi = ansi!(Blink);
const STRONGEST:  Ansi = ansi!(Purple, NotBold);

// Styling with paintln!
paintln!(HEADING,    "The Book of Rust");
paintln!();
paintln!(SUBHEADING, "Chapter 1");
paintln!();

// Styling with println!
println!("This sentence shows how {} as you would expect.",
    styled_format_args!(STRONG, "styles can be {}, and they combine",
        styled_format_args!(STRONGER, "nested to {} depths",
            styled_format_args!(STRONGEST, "arbitrary")
        )
    )
);
println!("This sentence shows another {} colours/effects.",
    styled_format_args!(Green, Italic, "way of styling {} i.e. with inline",
        styled_format_args!(Yellow, Bold, "your text,")
    )
);

Modules§

io
Set default ANSI styles for writers and handles.

Macros§

ansi
Creates an ANSI style as an Ansi const.
ansi_code
Creates an ANSI style as a &'static str.
epaint
Like eprint! except with ANSI-styled output.
epaintln
Like eprintln! except with ANSI-styled output.
paint
Like print! except with ANSI-styled output.
paintln
Like println! except with ANSI-styled output.
styled
Creates an ANSI-styled value.
styled_format
Like format! except creates an ANSI-styled String.
styled_format_args
Like format_args! except creates ANSI-styled Arguments.
styled_write
Like write! except with ANSI-styled output.
styled_writeln
Like writeln! except with ANSI-styled output.

Structs§

Ansi
Represents an arbitrary combination of ANSI Effects and foreground/background Colours.
Attrs
A bitmask used to select an arbitrary combination of Ansi attributes.
Styled
Associates a Display target with an Ansi style, such that formatting produces the result of formatting the target with the style’s ANSI codes wrapped around it.

Enums§

Colour
Represents the colour codes that are used to set foreground and background colours on ANSI terminals.
Effect
Represents the control sequences, named Select Graphic Rendition (SGR), that are used to enable various effects (e.g. italic) on ANSI terminals.