alphabet_encoding/
lib.rs

1use anyhow::{Context, Result};
2use std::str::FromStr;
3
4fn should_encode(c: char) -> bool {
5    !(c != 'x' && c != ':' && c != '\\' && c.is_ascii_graphic() && !c.is_ascii_whitespace())
6}
7
8struct EncodedChar(char);
9impl ToString for EncodedChar {
10    fn to_string(&self) -> String {
11        format!("x{:02x}", self.0 as u8)
12    }
13}
14
15impl FromStr for EncodedChar {
16    type Err = anyhow::Error;
17
18    fn from_str(str: &str) -> Result<Self, Self::Err> {
19        let str_chars: Vec<char> = str.chars().collect();
20        let chars: String = str_chars[1..=2].iter().collect();
21        let c = u8::from_str_radix(&chars, 16)
22            .with_context(|| format!("Unable to convert `{}` to u8", &chars))?
23            as char;
24
25        Ok(Self(c))
26    }
27}
28
29pub fn encode(str: String) -> String {
30    str.chars()
31        .map(|c| {
32            if should_encode(c) {
33                EncodedChar(c).to_string()
34            } else {
35                c.to_string()
36            }
37        })
38        .collect()
39}
40
41pub fn decode(str: String) -> Result<String> {
42    let str_chars: Vec<char> = str.chars().collect();
43
44    Ok(str
45        .match_indices('x')
46        .map(|(i, _)| -> Result<(String, String)> {
47            let chars: String = str_chars[i..=i + 2].iter().collect();
48            let encoded = EncodedChar::from_str(&chars)?;
49            Ok((chars, encoded.0.to_string()))
50        })
51        .filter_map(Result::ok)
52        .fold(str.to_owned(), |str, (replace, replacement)| {
53            str.replace(&replace, &replacement)
54        }))
55}