alphabet-encoding 0.1.1

A way of encoding text
Documentation
use anyhow::{Context, Result};
use std::str::FromStr;

fn should_encode(c: char) -> bool {
    !(c != 'x' && c != ':' && c != '\\' && c.is_ascii_graphic() && !c.is_ascii_whitespace())
}

struct EncodedChar(char);
impl ToString for EncodedChar {
    fn to_string(&self) -> String {
        format!("x{:02x}", self.0 as u8)
    }
}

impl FromStr for EncodedChar {
    type Err = anyhow::Error;

    fn from_str(str: &str) -> Result<Self, Self::Err> {
        let str_chars: Vec<char> = str.chars().collect();
        let chars: String = str_chars[1..=2].iter().collect();
        let c = u8::from_str_radix(&chars, 16)
            .with_context(|| format!("Unable to convert `{}` to u8", &chars))?
            as char;

        Ok(Self(c))
    }
}

pub fn encode(str: String) -> String {
    str.chars()
        .map(|c| {
            if should_encode(c) {
                EncodedChar(c).to_string()
            } else {
                c.to_string()
            }
        })
        .collect()
}

pub fn decode(str: String) -> Result<String> {
    let str_chars: Vec<char> = str.chars().collect();

    Ok(str
        .match_indices('x')
        .map(|(i, _)| -> Result<(String, String)> {
            let chars: String = str_chars[i..=i + 2].iter().collect();
            let encoded = EncodedChar::from_str(&chars)?;
            Ok((chars, encoded.0.to_string()))
        })
        .filter_map(Result::ok)
        .fold(str.to_owned(), |str, (replace, replacement)| {
            str.replace(&replace, &replacement)
        }))
}