ascii/free_functions.rs
1use ascii_char::{AsciiChar, ToAsciiChar};
2
3/// Terminals use [caret notation](https://en.wikipedia.org/wiki/Caret_notation)
4/// to display some typed control codes, such as ^D for EOT and ^Z for SUB.
5///
6/// This function returns the caret notation letter for control codes,
7/// or `None` for printable characters.
8///
9/// # Examples
10/// ```
11/// # use ascii::{AsciiChar, caret_encode};
12/// assert_eq!(caret_encode(b'\0'), Some(AsciiChar::At));
13/// assert_eq!(caret_encode(AsciiChar::DEL), Some(AsciiChar::Question));
14/// assert_eq!(caret_encode(b'E'), None);
15/// assert_eq!(caret_encode(b'\n'), Some(AsciiChar::J));
16/// ```
17pub fn caret_encode<C: Copy + Into<u8>>(c: C) -> Option<AsciiChar> {
18 // The formula is explained in the Wikipedia article.
19 let c = c.into() ^ 0b0100_0000;
20 if (b'?'..=b'_').contains(&c) {
21 // SAFETY: All bytes between '?' (0x3F) and '_' (0x5f) are valid ascii characters.
22 Some(unsafe { c.to_ascii_char_unchecked() })
23 } else {
24 None
25 }
26}
27
28/// Returns the control code represented by a [caret notation](https://en.wikipedia.org/wiki/Caret_notation)
29/// letter, or `None` if the letter is not used in caret notation.
30///
31/// This function is the inverse of `caret_encode()`.
32///
33/// # Examples
34///
35/// Basic usage:
36///
37/// ```
38/// # use ascii::{AsciiChar, caret_decode};
39/// assert_eq!(caret_decode(b'?'), Some(AsciiChar::DEL));
40/// assert_eq!(caret_decode(AsciiChar::D), Some(AsciiChar::EOT));
41/// assert_eq!(caret_decode(b'\0'), None);
42/// ```
43///
44/// Symmetry:
45///
46/// ```
47/// # use ascii::{AsciiChar, caret_encode, caret_decode};
48/// assert_eq!(caret_encode(AsciiChar::US).and_then(caret_decode), Some(AsciiChar::US));
49/// assert_eq!(caret_decode(b'@').and_then(caret_encode), Some(AsciiChar::At));
50/// ```
51pub fn caret_decode<C: Copy + Into<u8>>(c: C) -> Option<AsciiChar> {
52 // The formula is explained in the Wikipedia article.
53 match c.into() {
54 // SAFETY: All bytes between '?' (0x3F) and '_' (0x5f) after `xoring` with `0b0100_0000` are
55 // valid bytes, as they represent characters between '␀' (0x0) and '␠' (0x1f) + '␡' (0x7f)
56 b'?'..=b'_' => Some(unsafe { AsciiChar::from_ascii_unchecked(c.into() ^ 0b0100_0000) }),
57 _ => None,
58 }
59}