319 lines
12 KiB
Rust
319 lines
12 KiB
Rust
//! # ANSI Escape Code Library
|
|
//!
|
|
//! ANSI escape sequences are a standard for in-band signalling to control cursor location, color, font styling, and
|
|
//! other options on video text terminals and terminal emulators.
|
|
//!
|
|
//! This library contains all ANSI Escape Codes that are defined in the [ISO 6429 Standard][iso-6429]. ISO 6429 is
|
|
//! the international standard that followed from the efforts of aligning the european [ECMA-48 Standard][ecma-48] and
|
|
//! the american [ANSI X3.64 Standard][ansi-x364].
|
|
//!
|
|
//! ## Notation
|
|
//!
|
|
//! In the [ECMA-48 Standard][ecma-48] a convention has been adopted to assist the reader of the Standard.
|
|
//!
|
|
//! Capital letters are used to refer to a specific control function, mode, mode setting, or graphic character in order
|
|
//! to avoid confusion, for example, between the concept "space", and the character `SPACE`.
|
|
//!
|
|
//! As is intended by the [ECMA-48 Standard][ecma-48], this convention and all acronyms of modes, and control functions
|
|
//! are retained in this library, where rust permits.
|
|
//!
|
|
//! A character from the [ASCII table][ascii-table] is represented in the form `xx/yy`, where `xx` represents the column
|
|
//! number `00` to `07` in a 7-bit code table, and `yy` represents the row number `00` to `15`.
|
|
//!
|
|
//! ## Low-Level Control Functions
|
|
//!
|
|
//! The control functions of this library are sorted into several modules. You will find the low-level control functions
|
|
//! in the modules [c0], [c1], [control_sequences], [independent_control_functions], and [control_strings].
|
|
//!
|
|
//! The control functions can be put into normal strings. For example, to ring the bell:
|
|
//!
|
|
//! ```
|
|
//! use ansi::c0::BEL;
|
|
//! println!("Let's ring the bell {}", BEL);
|
|
//! ```
|
|
//!
|
|
//! Or to move the cursor to line 5, column 13:
|
|
//!
|
|
//! ```
|
|
//! use ansi::control_sequences::CUP;
|
|
//! print!("{}", CUP(5.into(), 13.into()));
|
|
//! ```
|
|
//!
|
|
//! It might be necessary in some circumstances to announce the active set of control sequences before they can be used.
|
|
//! This is possible by invoking one of the announcer sequences.
|
|
//!
|
|
//! ```
|
|
//! use ansi::c1::{ANNOUNCER_SEQUENCE, NEL};
|
|
//! // announce the C1 control function set, then move to the next line.
|
|
//! print!("{}{}", ANNOUNCER_SEQUENCE, NEL);
|
|
//! ```
|
|
//!
|
|
//! ## Source Material
|
|
//!
|
|
//! The second, and newer, editions of the [ECMA-48 Standard][ecma-48] are based on the text of the
|
|
//! [ISO 6429 Standard][iso-6429] and are technically identical with it. Since the [ISO 6429 Standard][iso-6429] is not
|
|
//! freely available on the internet, this implementation is based on the publicly available documents of the
|
|
//! [ECMA-48 Standard][ecma-48]. In particular on edition 5 of the [ECMA-48 Standard][ecma-48], which is identical to
|
|
//! the third edition of [ISO-6429][iso-6429].
|
|
//!
|
|
//! The [ANSI X3.64 Standard][ansi-x364] has been withdrawn by ANSI in 1994 in favour of the international standard.
|
|
//!
|
|
//! You can read more about the history of the standards on [Wikipedia: ANSI escape code][wikipedia-ansi].
|
|
//!
|
|
//! [ansi-x364]: https://nvlpubs.nist.gov/nistpubs/Legacy/FIPS/fipspub86.pdf
|
|
//! [ascii-table]: https://en.wikipedia.org/wiki/ASCII#/media/File:USASCII_code_chart.png
|
|
//! [ecma-48]: https://www.ecma-international.org/publications-and-standards/standards/ecma-48/
|
|
//! [iso-6429]: https://www.iso.org/standard/12782.html
|
|
//! [wikipedia-ansi]: https://en.wikipedia.org/wiki/ANSI_escape_code
|
|
#![allow(dead_code)]
|
|
|
|
use std::{fmt, str};
|
|
|
|
/// Converts the ascii table notation `xx/yy` into a rust string.
|
|
///
|
|
/// A character from the [ASCII table][ascii-table] is represented in the form `xx/yy`, where `xx` represents the column
|
|
/// number `00` to `07` in a 7-bit code table, and `yy` represents the row number `00` to `15`.
|
|
///
|
|
/// The macro can be used to convert a single code point into a str, or to convert a sequence of them.
|
|
///
|
|
/// ```ignore
|
|
/// let a: &'static str = ascii!(06 / 01);
|
|
/// let abc: &'static str = ascii!(06 / 01, 06 / 02, 06 / 03);
|
|
/// ```
|
|
///
|
|
/// ## Safety
|
|
///
|
|
/// This macro converts the given `xx/yy` combination into a ascii code by the formula `(xx << 4) + yy`.
|
|
/// The result is passed to the unsafe function std::str::from_utf8_unchecked.
|
|
///
|
|
/// This will result in an unsafe calculation, if the values for xx and yy are out of range. Valid ranges are:
|
|
///
|
|
/// - `xx: [0,7]`
|
|
/// - `yy: [0,15]`
|
|
///
|
|
/// Since this macro is not public and only used by the library itself, it is assumed to be used only within safe
|
|
/// bounds, and therefore considered safe.
|
|
///
|
|
/// [ascii-table]: https://en.wikipedia.org/wiki/ASCII#/media/File:USASCII_code_chart.png
|
|
macro_rules! ascii {
|
|
($($xx:literal/$yy:literal), *) => {
|
|
unsafe { std::str::from_utf8_unchecked(&[$(($xx << 4) + $yy),*]) }
|
|
};
|
|
}
|
|
|
|
/// The different types of control functions.
|
|
///
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
enum ControlFunctionType {
|
|
/// Elements of the C0 set.
|
|
///
|
|
/// C0 control functions are represented in 7-bit codes by bit combinations from `00/00` to `01/15`.
|
|
///
|
|
/// The control functions of the C0 set are defined in the module [c0].
|
|
C0,
|
|
|
|
/// Elements of the C1 set.
|
|
///
|
|
/// C1 control functions are represented in 7-bit codes by 2-character escape sequences of the form `ESC Fe`,
|
|
/// where `ESC` is represented by bit combination `01/11`, and `Fe` is represented by a bit combination from
|
|
/// `04/00` to `05/15`.
|
|
///
|
|
/// The control functions of the C1 set are defined in the module [c1].
|
|
C1,
|
|
|
|
/// Control Sequences.
|
|
///
|
|
/// Control sequences are strings of bit combinations starting with the control function
|
|
/// `CONTROL SEQUENCE INTRODUCER` ([`CSI`][c1::CSI]), followed by one or more bit combinations representing
|
|
/// parameters, if any, and by one ore more bit combinations identifying the control function. The control function
|
|
/// `CSI` itself is an element of the [c1] set.
|
|
///
|
|
/// The control sequences are defined in the module [control_sequences].
|
|
ControlSequence,
|
|
|
|
/// Independent Control Functions.
|
|
///
|
|
/// Independent control functions are represented in 7-bit codes by 2-character escape sequences of the form
|
|
/// `ESC Fs`, where `ESC` is represented by bit combination `01/11`, and `Fs` is represented by a bit combination
|
|
/// from `06/00` to `07/14`.
|
|
///
|
|
/// The independent control functions are defined in the module [independent_control_functions].
|
|
IndependentControlFunction,
|
|
|
|
/// Control Strings.
|
|
///
|
|
/// A control string is a string of bit combinations which may occur in the data stream as a logical entity for
|
|
/// control purposes. A control string consists of an opening delimiter, a command string or character string, and
|
|
/// a terminating delimiter, the String Terminator ([`ST`][c1::ST]).
|
|
///
|
|
/// The control strings are defined in the module [control_strings].
|
|
ControlString,
|
|
}
|
|
|
|
impl fmt::Debug for ControlFunctionType {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
match self {
|
|
ControlFunctionType::C0 => write!(f, "C0"),
|
|
ControlFunctionType::C1 => write!(f, "C1"),
|
|
ControlFunctionType::ControlSequence => write!(f, "Control Sequence"),
|
|
ControlFunctionType::IndependentControlFunction => {
|
|
write!(f, "Independent Control Function")
|
|
}
|
|
ControlFunctionType::ControlString => write!(f, "Control String"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An ansi control function defined in [ECMA-48][ecma-48].
|
|
///
|
|
/// [ecma-48]: https://www.ecma-international.org/publications-and-standards/standards/ecma-48/
|
|
pub struct ControlFunction {
|
|
/// The type of the control function.
|
|
function_type: ControlFunctionType,
|
|
|
|
/// The byte or byte combination identifying the control function.
|
|
value: &'static str,
|
|
|
|
/// An arbitrary number of arguments for this control function.
|
|
parameters: Vec<String>,
|
|
}
|
|
|
|
impl ControlFunction {
|
|
/// Creates a new control function of type [`C0`][ControlFunctionType::C0].
|
|
///
|
|
/// `C0` control functions do not accept any parameters.
|
|
const fn new_c0(value: &'static str) -> Self {
|
|
ControlFunction {
|
|
function_type: ControlFunctionType::C0,
|
|
value,
|
|
parameters: vec![],
|
|
}
|
|
}
|
|
|
|
/// Creates a new control function of type [`C1`][ControlFunctionType::C1].
|
|
///
|
|
/// `C1` control functions do not accept any parameters.
|
|
const fn new_c1(value: &'static str) -> Self {
|
|
ControlFunction {
|
|
function_type: ControlFunctionType::C1,
|
|
value,
|
|
parameters: vec![],
|
|
}
|
|
}
|
|
|
|
/// Creates a new control function of type [`ControlSequence`][ControlFunctionType::ControlSequence].
|
|
const fn new_sequence(value: &'static str, parameters: Vec<String>) -> Self {
|
|
ControlFunction {
|
|
function_type: ControlFunctionType::ControlSequence,
|
|
value,
|
|
parameters,
|
|
}
|
|
}
|
|
|
|
/// Creates a new control function of type
|
|
/// [`IndependentControlFunction`][ControlFunctionType::IndependentControlFunction].
|
|
const fn new_independent_control_function(value: &'static str) -> Self {
|
|
ControlFunction {
|
|
function_type: ControlFunctionType::IndependentControlFunction,
|
|
value,
|
|
parameters: vec![],
|
|
}
|
|
}
|
|
|
|
fn format_parameters(&self) -> String {
|
|
self.parameters.join(ascii!(03 / 11))
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for ControlFunction {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
match self.function_type {
|
|
ControlFunctionType::C0 => {
|
|
write!(f, "{}", self.value)
|
|
}
|
|
ControlFunctionType::C1 => {
|
|
write!(f, "{}{}", c0::ESC, self.value)
|
|
}
|
|
ControlFunctionType::ControlSequence => {
|
|
let parameters = self.format_parameters();
|
|
write!(f, "{}{}{}", c1::CSI, parameters, self.value)
|
|
}
|
|
ControlFunctionType::IndependentControlFunction => {
|
|
write!(f, "{}{}", c0::ESC, self.value)
|
|
}
|
|
ControlFunctionType::ControlString => {
|
|
write!(f, "{}", self.value)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for ControlFunction {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
let function: String = self
|
|
.value
|
|
.as_bytes()
|
|
.into_iter()
|
|
.map(|b| format!("{:02}/{:02}", b >> 4, (b & 0xF)))
|
|
.collect();
|
|
|
|
f.debug_struct("ControlFunction")
|
|
.field("function_type", &self.function_type)
|
|
.field("function", &function)
|
|
.field("parameters", &self.parameters)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl From<ControlFunction> for String {
|
|
fn from(control_function: ControlFunction) -> Self {
|
|
format!("{}", control_function)
|
|
}
|
|
}
|
|
|
|
pub mod c0;
|
|
pub mod c1;
|
|
pub mod control_sequences;
|
|
pub mod control_strings;
|
|
pub mod independent_control_functions;
|
|
pub mod modes;
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::c0::BEL;
|
|
use crate::ControlFunctionType;
|
|
|
|
/// Test the debug format of [`ControlFunctionType`].
|
|
#[test]
|
|
fn debug_control_function_type() {
|
|
assert_eq!(format!("{:?}", ControlFunctionType::C0), "C0");
|
|
assert_eq!(format!("{:?}", ControlFunctionType::C1), "C1");
|
|
assert_eq!(
|
|
format!("{:?}", ControlFunctionType::ControlSequence),
|
|
"Control Sequence"
|
|
);
|
|
assert_eq!(
|
|
format!("{:?}", ControlFunctionType::IndependentControlFunction),
|
|
"Independent Control Function"
|
|
);
|
|
assert_eq!(
|
|
format!("{:?}", ControlFunctionType::ControlString),
|
|
"Control String"
|
|
);
|
|
}
|
|
|
|
/// Test the debug format of [`ControlFunction`][crate::ControlFunction].
|
|
#[test]
|
|
fn debug_control_function() {
|
|
assert_eq!(
|
|
format!("{:?}", BEL),
|
|
"ControlFunction { function_type: C0, function: \"00/07\", parameters: [] }"
|
|
);
|
|
|
|
assert_eq!(
|
|
format!("{:?}", crate::control_sequences::CUP(None, Some(10))),
|
|
"ControlFunction { function_type: Control Sequence, function: \"04/08\", parameters: [\"1\", \"10\"] }"
|
|
);
|
|
}
|
|
}
|