Crate term_painter
source ·Expand description
This is a crate for coloring and formatting terminal output. Simple example:
extern crate term_painter;
use term_painter::ToStyle;
use term_painter::Color::*;
use term_painter::Attr::*;
fn main() {
println!("{} or {} or {}",
Red.paint("Red"),
Bold.paint("Bold"),
Red.bold().paint("Both!")
);
}
This crate uses rust-lang/term
to do the formatting. Of course,
you can use term
directly, but it’s kinda clumsy. Hence this library.
§How to use it
Formatting is done in two steps:
-
Creating a style
-
Use this style to “paint” something and get a
Painted
object -
Creating a style
To create a style a startpoint is needed: This can either be a startpoint
with an attached modifier (like Red
: modifies the fg-color) or the
Plain
startpoint, which does not modify anything.
After that, the startpoint can be modified with modifiers like bold()
or
fg()
.
extern crate term_painter;
use term_painter::ToStyle;
use term_painter::Color::*;
use term_painter::Attr::*;
fn main() {
let x = 5;
// These two are equivalent: nothing is formatted/painted
println!("{} | {}", x, Plain.paint(x));
// These two are equivalent, too
println!("{} | {}", Red.paint(x), Plain.fg(Red).paint(x));
}
You can chain as many modifiers as you want. Every modifier overrides preceding modifier:
// blue, not red
println!("{}", Plain.fg(Red).fg(Blue).paint("Apple"));
- Use the style
After building the style, you can use it in two different ways.
One way is to call paint
to use it on some object.
paint
will return the wrapper object Painted
that holds your object and
the specified style. Painted
implements any formatting trait (like
Display
and Debug
) if and only if the type of the given Object, T
,
does. So a Painted
object can be printed via println!
or similar macros.
When it gets printed, it will apply the given style before printing the
object of type T
and will reset the style after printing.
Note
: paint
will consume the passed object. This is no problem when
passing constant literals (like paint("cheesecake")
) or types that are
Copy
. Otherwise it could be confusing because just printing should not
consume a variable. To prevent consuming, just pass a reference to the
object (with &
). Example:
extern crate term_painter;
use term_painter::ToStyle;
use term_painter::Color::*;
use term_painter::Attr::*;
fn main() {
let non_copy = "cake".to_string(); // String is *not* Copy
let copy = 27; // i32 *is* Copy
println!("{}", Plain.paint(&non_copy));
println!("{}", Plain.paint(©));
// non_copy is still usable here...
// copy is still usable here...
println!("{}", Plain.paint(non_copy));
println!("{}", Plain.paint(copy));
// non_copy was moved into `paint`, so it not usable anymore...
// copy is still usable here...
}
Another way is to call with
. with
takes another function (usually a
closure) and everything that is printed within that closure is formatted
with the given style. Specifically, with()
sets the given style,
calls the given function and resets the style afterwards. It can be
chained and used together with paint()
. Inner calls will overwrite
outer calls of with
.
extern crate term_painter;
use term_painter::ToStyle;
use term_painter::Color::*;
use term_painter::Attr::*;
fn main() {
Red.with(|| {
print!("JustRed");
Bold.with(|| {
print!(" BoldRed {} BoldRed ", Underline.paint("Underline"));
});
print!("JustRed ");
print!("{}", Blue.paint("Blue (overwrite) "));
Green.with(|| {
println!("Green (overwrite)");
});
});
}
§Some Notes
If you don’t want to pollute your namespace with Color
and Attr
names,
you can use a more qualified name (Color::Red.paint(..)
) and remove these
use
statements:
use term_painter::Color::*;
use term_painter::Attr::*;
Please note that global state is changed when printing a Painted
object. This means that some state is set before and reset after printing.
This means that, for example, using this library in format!
or write!
won’t work. The color formatting is not stored in the resulting string.
Although Unix terminals do modify color and formatting by printing special
control characters, Windows and others do not. This library uses the
plattform independent library term
, thus saving formatted text in a
string not possible. This was a design choice.
This crate also assumes that the terminal state is not altered by anything
else. Calling term
function directly might result in strange behaviour.
This is due to the fact that one can not read the current terminal state.
In order to work like this, this crate needs to track terminal state
itself. However, there shouldn’t be any problems when the terminal state
is completely reset in between using those two different methods.
Another possible source of confusion might be multithreading. Terminal state and handles are hold in thread local variables. If two terminal handles would reference the same physical terminal, those two threads could interfere with each other. I have not tested it, though. Usually, you don’t want to print to the same Terminal in two threads simultanously anyway.
Functions of term
sometimes return a Result
that is Err
when the
function fails to set the state. However, this crate silently ignores those
failures. To check the capabilities of the terminal, use term
directly.
Structs§
- Wraps an object of type
T
and a style. When attempting to print it, the given style is applied before printing and reset afterwards. All formatting traits (Display
,Debug
, …) that are implemented forT
are also implemented the wrapper typePainted<T>
. - Saves all properties of a style. Implements
ToStyle
, so you can call style modifiers on it.
Enums§
- Lists possible attributes. It implements
ToStyle
so it’s possible to callToStyle
’s methods directly on aAttr
variant like: - Lists all possible Colors. It implements
ToStyle
so it’s possible to callToStyle
’s methods directly on aColor
variant like:
Traits§
- Everything that can be seen as part of a style. This is the core of this crate. All functions (“style modifier”) consume self and return a modified version of the style.