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}