ansi_control_codes/
lib.rs

1//! # ANSI Escape Code Library
2//!
3//! ANSI escape sequences are a standard for in-band signalling to control cursor location, color, font styling, and
4//! other options on video text terminals and terminal emulators.
5//!
6//! This library contains all ANSI Escape Codes that are defined in the [ISO 6429 Standard][iso-6429]. ISO 6429 is
7//! the international standard that followed from the efforts of aligning the european [ECMA-48 Standard][ecma-48] and
8//! the american [ANSI X3.64 Standard][ansi-x364].
9//!
10//! ## Notation
11//!
12//! In the [ECMA-48 Standard][ecma-48] a convention has been adopted to assist the reader of the Standard.
13//!
14//! Capital letters are used to refer to a specific control function, mode, mode setting, or graphic character in order
15//! to avoid confusion, for example, between the concept "space", and the character `SPACE`.
16//!
17//! As is intended by the [ECMA-48 Standard][ecma-48], this convention and all acronyms of modes, and control functions
18//! are retained in this library, where rust permits.
19//!
20//! A character from the [ASCII table][ascii-table] is represented in the form `xx/yy`, where `xx` represents the column
21//! number `00` to `07` in a 7-bit code table, and `yy` represents the row number `00` to `15`.
22//!
23//! ## Parsing ansi-control-codes
24//!
25//! The module [`parser`] contains a parser that can be used to parse strings and extract any ansi-control-codes
26//! that are present within. To use the parser module, enable the feature `parser`.
27//!
28//! ```text
29//! cargo add ansi-control-codes --features parser
30//! ```
31//!
32//! Refer to the [`parser`]'s module documentation for more details.
33//!
34//! ## Explaining ansi-control-codes
35//!
36//! The module [`explain`] contains additional functionality to explain ansi-control-codes. Enable this module to get
37//! the additional functions [`short_name`][explain::Explain::short_name], [`long_name`][explain::Explain::long_name],
38//! [`short_description`][explain::Explain::short_description], [`long_description`][explain::Explain::long_description]
39//! to inspect and explain control functions.
40//!
41//! ```text
42//! cargo add ansi-control-codes --features explain
43//! ```
44//!
45//! Refer to the [`explain`]'s module documentation for more details.
46//!
47//! ## Low-Level Control Functions
48//!
49//! The control functions of this library are sorted into several modules. You will find the low-level control functions
50//! in the modules [c0], [c1], [control_sequences], [independent_control_functions]
51//!
52//! The control functions can be put into normal strings. For example, to ring the bell:
53//!
54//! ```
55//! use ansi_control_codes::c0::BEL;
56//! println!("Let's ring the bell {}", BEL);
57//! ```
58//!
59//! Or to move the cursor to line 5, column 13:
60//!
61//! ```
62//! use ansi_control_codes::control_sequences::CUP;
63//! print!("{}", CUP(5.into(), 13.into()));
64//! ```
65//!
66//! It might be necessary in some circumstances to announce the active set of control sequences before they can be used.
67//! This is possible by invoking one of the announcer sequences.
68//!
69//! ```
70//! use ansi_control_codes::c1::{ANNOUNCER_SEQUENCE, NEL};
71//! // announce the C1 control function set, then move to the next line.
72//! print!("{}{}", ANNOUNCER_SEQUENCE, NEL);
73//! ```
74//!
75//! ## Categories of control functions
76//!
77//! Most control functions are categorized into different groups. They can be accessed from the module
78//! [categories].
79//!
80//! ```
81//! use ansi_control_codes::categories::format_effectors::{CR, LF};
82//! println!("line1{}{}line2", CR, LF);
83//! ```
84//!
85//! ## High-Level Functions
86//!
87//! For your convenience and ease-of-use of the ansi control codes, some functionality is exposed in wrapper functions.
88//! See the following module documentations for a more in-depth introduction to these functions.
89//!
90//! - Working with control strings in module [control_strings].
91//!
92//! ## Source Material
93//!
94//! The second, and newer, editions of the [ECMA-48 Standard][ecma-48] are based on the text of the
95//! [ISO 6429 Standard][iso-6429] and are technically identical with it. Since the [ISO 6429 Standard][iso-6429] is not
96//! freely available on the internet, this implementation is based on the publicly available documents of the
97//! [ECMA-48 Standard][ecma-48]. In particular on edition 5 of the [ECMA-48 Standard][ecma-48], which is identical to
98//! the third edition of [ISO-6429][iso-6429].
99//!
100//! The [ANSI X3.64 Standard][ansi-x364] has been withdrawn by ANSI in 1994 in favour of the international standard.
101//!
102//! You can read more about the history of the standards on [Wikipedia: ANSI escape code][wikipedia-ansi].
103//!
104//! [ansi-x364]: https://nvlpubs.nist.gov/nistpubs/Legacy/FIPS/fipspub86.pdf
105//! [ascii-table]: https://en.wikipedia.org/wiki/ASCII#/media/File:USASCII_code_chart.png
106//! [ecma-48]: https://www.ecma-international.org/publications-and-standards/standards/ecma-48/
107//! [iso-6429]: https://www.iso.org/standard/12782.html
108//! [wikipedia-ansi]: https://en.wikipedia.org/wiki/ANSI_escape_code
109#![deny(missing_debug_implementations, missing_docs)]
110#![allow(clippy::zero_prefixed_literal)]
111use std::{error::Error, fmt, str};
112
113/// Converts the ascii table notation `xx/yy` into a rust string.
114///
115/// A character from the [ASCII table][ascii-table] is represented in the form `xx/yy`, where `xx` represents the column
116/// number `00` to `07` in a 7-bit code table, and `yy` represents the row number `00` to `15`.
117///
118/// The macro can be used to convert a single code point into a str, or to convert a sequence of them.
119///
120/// ```ignore
121/// let a: &'static str = ascii!(06 / 01);
122/// let abc: &'static str = ascii!(06 / 01, 06 / 02, 06 / 03);
123/// ```
124///
125/// ## Safety
126///
127/// This macro converts the given `xx/yy` combination into a ascii code by the formula `(xx << 4) + yy`.
128/// The result is passed to the unsafe function std::str::from_utf8_unchecked.
129///
130/// This will result in an unsafe calculation, if the values for xx and yy are out of range. Valid ranges are:
131///
132/// - `xx: [0,7]`
133/// - `yy: [0,15]`
134///
135/// Since this macro is not public and only used by the library itself, it is assumed to be used only within safe
136/// bounds, and therefore considered safe.
137///
138/// [ascii-table]: https://en.wikipedia.org/wiki/ASCII#/media/File:USASCII_code_chart.png
139macro_rules! ascii {
140    ($($xx:literal/$yy:literal), *) => {
141        unsafe { std::str::from_utf8_unchecked(&[$(($xx << 4) + $yy),*]) }
142    };
143}
144
145/// Possible errors when specifying a custom control function.
146///
147/// It is possible to define custom control functions, so called private-use or experimental functions.
148/// These private-use functions still need to follow some rules. If they violate the rules, one of these
149/// variants is returned as an error.
150#[derive(Debug)]
151pub enum InvalidControlFunction {
152    /// All control function values must be valid ASCII.
153    InvalidAsciiError,
154    /// All control functions must have a one- or two-byte function identifier.
155    InvalidFunctionValueError,
156    /// If the function has an intermediate byte, it must by `02 / 00`. All other intermediate bytes are invalid.
157    InvalidIntermediateByteError,
158    /// All private-use functions must be in the range `07 / 00` to `07 / 15`.
159    InvalidPrivateUseError,
160}
161
162impl fmt::Display for InvalidControlFunction {
163    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
164        match self {
165            InvalidControlFunction::InvalidAsciiError => {
166                write!(formatter, "Control function must be valid ASCII")
167            }
168            InvalidControlFunction::InvalidFunctionValueError => write!(
169                formatter,
170                "Control function must have one- or two-byte identifier"
171            ),
172            InvalidControlFunction::InvalidIntermediateByteError => {
173                write!(formatter, "Intermediate byte must be 02/00")
174            }
175            InvalidControlFunction::InvalidPrivateUseError => write!(
176                formatter,
177                "Private use functions are only allowed in range 07/00 to 07/15"
178            ),
179        }
180    }
181}
182
183impl Error for InvalidControlFunction {}
184
185/// The different types of control functions.
186///
187#[derive(Clone, Copy, PartialEq, Eq)]
188enum ControlFunctionType {
189    /// Elements of the C0 set.
190    ///
191    /// C0 control functions are represented in 7-bit codes by bit combinations from `00/00` to `01/15`.
192    ///
193    /// The control functions of the C0 set are defined in the module [c0].
194    C0,
195
196    /// Elements of the C1 set.
197    ///
198    /// C1 control functions are represented in 7-bit codes by 2-character escape sequences of the form `ESC Fe`,
199    /// where `ESC` is represented by bit combination `01/11`, and `Fe` is represented by a bit combination from
200    /// `04/00` to `05/15`.
201    ///
202    /// The control functions of the C1 set are defined in the module [c1].
203    C1,
204
205    /// Control Sequences.
206    ///
207    /// Control sequences are strings of bit combinations starting with the control function
208    /// `CONTROL SEQUENCE INTRODUCER` ([`CSI`][c1::CSI]), followed by one or more bit combinations representing
209    /// parameters, if any, and by one ore more bit combinations identifying the control function. The control function
210    /// `CSI` itself is an element of the [c1] set.
211    ///
212    /// The control sequences are defined in the module [control_sequences].
213    ControlSequence,
214
215    /// Independent Control Functions.
216    ///
217    /// Independent control functions are represented in 7-bit codes by 2-character escape sequences of the form
218    /// `ESC Fs`, where `ESC` is represented by bit combination `01/11`, and `Fs` is represented by a bit combination
219    /// from `06/00` to `07/14`.
220    ///
221    /// The independent control functions are defined in the module [independent_control_functions].
222    IndependentControlFunction,
223}
224
225impl fmt::Debug for ControlFunctionType {
226    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
227        match self {
228            ControlFunctionType::C0 => write!(f, "C0"),
229            ControlFunctionType::C1 => write!(f, "C1"),
230            ControlFunctionType::ControlSequence => write!(f, "Control Sequence"),
231            ControlFunctionType::IndependentControlFunction => {
232                write!(f, "Independent Control Function")
233            }
234        }
235    }
236}
237
238/// An ansi control function defined in [ECMA-48][ecma-48].
239///
240/// This struct implements the `PartialEq` trait for String-like types (all types that implement `AsRef<str>`).
241/// It can be used to compare ControlFunctions with string-like values using `==` or `!=` functions.
242///
243/// Example:
244/// ```
245/// use ansi_control_codes::c0;
246///
247/// let some_string = String::from("\u{001B}");
248/// if (c0::ESC == some_string) {
249///     println!("ESC!")
250/// }
251/// ```
252///
253/// [ecma-48]: https://www.ecma-international.org/publications-and-standards/standards/ecma-48/
254#[derive(PartialEq, Eq)]
255pub struct ControlFunction<'a> {
256    /// The type of the control function.
257    function_type: ControlFunctionType,
258
259    /// The byte or byte combination identifying the control function.
260    value: &'a str,
261
262    /// An arbitrary number of arguments for this control function.
263    parameters: Vec<String>,
264}
265
266impl ControlFunction<'static> {
267    /// Creates a new control function of type [`C0`][ControlFunctionType::C0].
268    ///
269    /// `C0` control functions do not accept any parameters.
270    const fn new_c0(value: &'static str) -> Self {
271        ControlFunction {
272            function_type: ControlFunctionType::C0,
273            value,
274            parameters: vec![],
275        }
276    }
277
278    /// Creates a new control function of type [`C1`][ControlFunctionType::C1].
279    ///
280    /// `C1` control functions do not accept any parameters.
281    const fn new_c1(value: &'static str) -> Self {
282        ControlFunction {
283            function_type: ControlFunctionType::C1,
284            value,
285            parameters: vec![],
286        }
287    }
288
289    /// Creates a new control function of type
290    /// [`IndependentControlFunction`][ControlFunctionType::IndependentControlFunction].
291    const fn new_independent_control_function(value: &'static str) -> Self {
292        ControlFunction {
293            function_type: ControlFunctionType::IndependentControlFunction,
294            value,
295            parameters: vec![],
296        }
297    }
298}
299
300impl<'a> ControlFunction<'a> {
301    /// Creates a new control function of type [`ControlSequence`][ControlFunctionType::ControlSequence].
302    const fn new_sequence(value: &'a str, parameters: Vec<String>) -> Self {
303        ControlFunction {
304            function_type: ControlFunctionType::ControlSequence,
305            value,
306            parameters,
307        }
308    }
309
310    /// Creates a new control function representing a control sequence that is declared as private use.
311    ///
312    /// These functions are not standardized and their function is unknown.
313    /// Yet, the standard allows these functions to exist for experimental use.
314    ///
315    /// If the specified value lies outside of the valid private use area, this function will return Err.
316    pub fn private_use(
317        value: &'a str,
318        parameters: Vec<String>,
319    ) -> Result<Self, InvalidControlFunction> {
320        if !value.is_ascii() {
321            return Err(InvalidControlFunction::InvalidAsciiError);
322        }
323        if value.as_bytes().len() > 2 {
324            return Err(InvalidControlFunction::InvalidFunctionValueError);
325        }
326        let function_value = if value.as_bytes().len() == 2 {
327            if &value[0..1] != ascii!(02 / 00) {
328                return Err(InvalidControlFunction::InvalidIntermediateByteError);
329            }
330            &value[1..2]
331        } else {
332            &value[0..1]
333        };
334
335        if function_value.as_bytes()[0] >> 4 != 7 {
336            return Err(InvalidControlFunction::InvalidPrivateUseError);
337        }
338
339        Ok(ControlFunction {
340            function_type: ControlFunctionType::ControlSequence,
341            value,
342            parameters,
343        })
344    }
345
346    fn format_parameters(&self) -> String {
347        self.parameters.join(ascii!(03 / 11))
348    }
349}
350
351impl<'a> fmt::Display for ControlFunction<'a> {
352    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
353        match self.function_type {
354            ControlFunctionType::C0 => {
355                write!(f, "{}", self.value)
356            }
357            ControlFunctionType::C1 | ControlFunctionType::IndependentControlFunction => {
358                write!(f, "{}{}", c0::ESC, self.value)
359            }
360            ControlFunctionType::ControlSequence => {
361                let parameters = self.format_parameters();
362                write!(f, "{}{}{}", c1::CSI, parameters, self.value)
363            }
364        }
365    }
366}
367
368impl<'a> fmt::Debug for ControlFunction<'a> {
369    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
370        let function: String = self
371            .value
372            .as_bytes()
373            .iter()
374            .map(|b| format!("{:02}/{:02}", b >> 4, (b & 0xF)))
375            .collect::<Vec<_>>()
376            .join(" ");
377
378        f.debug_struct("ControlFunction")
379            .field("function_type", &self.function_type)
380            .field("function", &function)
381            .field("parameters", &self.parameters)
382            .finish()
383    }
384}
385
386impl<'a> From<ControlFunction<'a>> for String {
387    fn from(control_function: ControlFunction) -> Self {
388        format!("{}", control_function)
389    }
390}
391
392impl<'a, T> PartialEq<T> for ControlFunction<'a>
393where
394    T: AsRef<str>,
395{
396    // comparison for control sequences must be done on the evaluated sequence.
397    #[allow(clippy::cmp_owned)]
398    fn eq(&self, other: &T) -> bool {
399        let other_str = other.as_ref();
400
401        match self.function_type {
402            ControlFunctionType::C0 => self.value == other_str,
403            ControlFunctionType::C1 | ControlFunctionType::IndependentControlFunction => {
404                if other_str.len() != 2 {
405                    return false;
406                }
407                other_str[0..1] == *c0::ESC.value && other_str[1..2] == *self.value
408            }
409            ControlFunctionType::ControlSequence => self.to_string() == other_str,
410        }
411    }
412}
413
414impl<'a> PartialEq<ControlFunction<'a>> for &str {
415    fn eq(&self, other: &ControlFunction) -> bool {
416        other == self
417    }
418}
419
420impl<'a> PartialEq<ControlFunction<'a>> for String {
421    fn eq(&self, other: &ControlFunction) -> bool {
422        other == self
423    }
424}
425
426pub mod c0;
427pub mod c1;
428pub mod categories;
429pub mod control_sequences;
430pub mod control_strings;
431pub mod independent_control_functions;
432pub mod modes;
433
434#[cfg(feature = "parser")]
435pub mod parser;
436
437#[cfg(feature = "explain")]
438pub mod explain;
439
440#[cfg(test)]
441mod tests {
442    use crate::c0::{BEL, ESC};
443    use crate::c1::CSI;
444    use crate::control_sequences::CNL;
445    use crate::independent_control_functions::INT;
446    use crate::ControlFunctionType;
447
448    /// Test the debug format of [`ControlFunctionType`].
449    #[test]
450    fn debug_control_function_type() {
451        assert_eq!(format!("{:?}", ControlFunctionType::C0), "C0");
452        assert_eq!(format!("{:?}", ControlFunctionType::C1), "C1");
453        assert_eq!(
454            format!("{:?}", ControlFunctionType::ControlSequence),
455            "Control Sequence"
456        );
457        assert_eq!(
458            format!("{:?}", ControlFunctionType::IndependentControlFunction),
459            "Independent Control Function"
460        );
461    }
462
463    /// Test the debug format of [`ControlFunction`][crate::ControlFunction].
464    #[test]
465    fn debug_control_function() {
466        assert_eq!(
467            format!("{:?}", BEL),
468            "ControlFunction { function_type: C0, function: \"00/07\", parameters: [] }"
469        );
470
471        assert_eq!(
472            format!("{:?}", crate::control_sequences::CUP(None, Some(10))),
473            "ControlFunction { function_type: Control Sequence, function: \"04/08\", parameters: [\"1\", \"10\"] }"
474        );
475    }
476
477    #[test]
478    fn string_equality_c0() {
479        let esc_control = ESC;
480        let esc_str = "\u{001B}";
481        let esc_string = String::from(esc_str);
482
483        assert_eq!(
484            esc_control, esc_control,
485            "Asserting equality between same control codes failed"
486        );
487        assert_eq!(
488            esc_control, esc_str,
489            "Asserting equality between control code and string slice failed"
490        );
491        assert_eq!(
492            esc_control, esc_string,
493            "Asserting equality between control code and string failed"
494        );
495
496        assert!(
497            esc_control == esc_str,
498            "Failed to compare control code and string slice"
499        );
500        assert!(
501            esc_control == esc_string,
502            "Failed to compare control code and string"
503        );
504
505        // this should fail, as ESC and BEL are not equal
506        assert_ne!(
507            esc_control, "\u{0007}",
508            "Different control codes should not be equal"
509        );
510    }
511
512    #[test]
513    fn string_equality_c1() {
514        let csi_control = CSI;
515        let csi_str = "\u{001B}[";
516        let csi_string = String::from(csi_str);
517
518        assert_eq!(
519            csi_control, csi_control,
520            "Asserting equality between same control codes failed"
521        );
522        assert_eq!(
523            csi_control, csi_str,
524            "Asserting equality between control code and string slice failed"
525        );
526        assert_eq!(
527            csi_control, csi_string,
528            "Asserting equality between control code and string failed"
529        );
530
531        assert!(
532            csi_control == csi_str,
533            "Failed to compare control code and string slice"
534        );
535        assert!(
536            csi_control == csi_string,
537            "Failed to compare control code and string"
538        );
539
540        // this should fail, as CSI and OSC are not equal
541        assert_ne!(
542            csi_control, "\u{001B}]",
543            "Different control codes should not be equal"
544        );
545    }
546
547    #[test]
548    fn string_equality_control_sequence() {
549        let cnl_control = CNL(4.into());
550        let cnl_str = "\u{001B}[4E";
551        let cnl_string = String::from(cnl_str);
552
553        assert_eq!(
554            cnl_control, cnl_control,
555            "Asserting equality between same control codes failed"
556        );
557        assert_eq!(
558            cnl_control, cnl_str,
559            "Asserting equality between control code and string slice failed"
560        );
561        assert_eq!(
562            cnl_control, cnl_string,
563            "Asserting equality between control code and string failed"
564        );
565
566        assert!(
567            cnl_control == cnl_str,
568            "Failed to compare control code and string slice"
569        );
570        assert!(
571            cnl_control == cnl_string,
572            "Failed to compare control code and string"
573        );
574
575        // this should fail, as CNL for 4 lines and CNL for 3 lines should differ
576        assert_ne!(
577            cnl_control, "\u{001B}[3E",
578            "Different control codes should not be equal"
579        );
580    }
581
582    #[test]
583    fn string_equality_independent_control_functions() {
584        let icf_control = INT;
585        let icf_str = "\u{001B}a";
586        let icf_string = String::from(icf_str);
587
588        assert_eq!(
589            icf_control, icf_control,
590            "Asserting equality between same control codes failed"
591        );
592        assert_eq!(
593            icf_control, icf_str,
594            "Asserting equality between control code and string slice failed"
595        );
596        assert_eq!(
597            icf_control, icf_string,
598            "Asserting equality between control code and string failed"
599        );
600
601        assert!(
602            icf_control == icf_str,
603            "Failed to compare control code and string slice"
604        );
605        assert!(
606            icf_control == icf_string,
607            "Failed to compare control code and string"
608        );
609
610        // this should fail, as Interrupt and Enable Manual Input are different
611        assert_ne!(
612            icf_control, "\u{001B}b",
613            "Different control codes should not be equal"
614        );
615    }
616}