markdown2unicode 0.2.1

Converter from markdown notation to unicode characters
Documentation
#[doc = include_str!("../README.md")]
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(
            convert("Here is some *emphasis* **bold** ***bold emphasis*** and `mono` text.\n"),
            "Here is some 𝘦𝘮𝘱𝘩𝘢𝘴𝘪𝘴 𝐛𝐨𝐥𝐝 𝒃𝒐𝒍𝒅 𝒆𝒎𝒑𝒉𝒂𝒔𝒊𝒔 and 𝚖𝚘𝚗𝚘 text.\n",
        );
    }
}

use lazy_static::lazy_static;
use std::collections::HashMap;

fn c2i(s: &str) -> HashMap<char, usize> {
    s.chars().enumerate().map(|(i, c)| (c, i)).collect()
}

fn i2c(s: &str) -> HashMap<usize, char> {
    s.chars().enumerate().collect()
}

const REGULAR: &str =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_-+=?/|'\"`";
const BOLD: &str =
    "𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗!@#$%^&*()_-+=?/|'\"`";
const ITALIC: &str =
    "𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻0123456789!@#$%^&*()_-+=?/|'\"`";
const BOLD_ITALIC: &str =
    "𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛0123456789!@#$%^&*()_-+=?/|'\"`";
const MONO: &str =
    "𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿!@#$%^&*()_-+=?/|'\"`";

lazy_static! {
    static ref REGULAR_I: HashMap<char, usize> = c2i(REGULAR);
    static ref BOLD_I: HashMap<char, usize> = c2i(BOLD);
    static ref ITALIC_I: HashMap<char, usize> = c2i(ITALIC);
    static ref BOLD_ITALIC_I: HashMap<char, usize> = c2i(BOLD_ITALIC);
    static ref MONO_I: HashMap<char, usize> = c2i(MONO);
    static ref REGULAR_C: HashMap<usize, char> = i2c(REGULAR);
    static ref BOLD_C: HashMap<usize, char> = i2c(BOLD);
    static ref ITALIC_C: HashMap<usize, char> = i2c(ITALIC);
    static ref BOLD_ITALIC_C: HashMap<usize, char> = i2c(BOLD_ITALIC);
    static ref MONO_C: HashMap<usize, char> = i2c(MONO);
}

#[derive(Clone)]
enum Mode {
    Regular,
    Bold,
    Italic,
    BoldItalic,
    Mono,
}

impl Mode {
    fn asterisk(&self, asterisks: i32) -> Mode {
        match self {
            Mode::Regular => match asterisks {
                0 => Mode::Italic,
                1 => Mode::Bold,
                2 => Mode::BoldItalic,
                _ => self.clone(),
            },
            Mode::Italic => match asterisks {
                0 => Mode::Regular,
                1 => Mode::BoldItalic,
                2 => Mode::Bold,
                _ => self.clone(),
            },
            Mode::Bold => match asterisks {
                0 => Mode::BoldItalic,
                1 => Mode::Regular,
                2 => Mode::Italic,
                _ => self.clone(),
            },
            Mode::BoldItalic => match asterisks {
                0 => Mode::Bold,
                1 => Mode::Italic,
                2 => Mode::Regular,
                _ => self.clone(),
            },
            Mode::Mono => self.clone(),
        }
    }

    fn backtick(&self) -> Mode {
        match self {
            Mode::Regular => Mode::Mono,
            Mode::Mono => Mode::Regular,
            _ => self.clone(),
        }
    }
}

pub fn convert(md_text: &str) -> String {
    let mut result = String::new();
    let mut mode = Mode::Regular;
    let mut asterisks = 0;

    let len_md_text_minus_1 = md_text.len() - 1;
    let md_text_chars = md_text.chars().collect::<Vec<_>>();

    for (i, c) in md_text_chars.iter().enumerate() {
        if let Some(chr_index) = REGULAR_I.get(c) {
            match c {
                '*' => {
                    if i < len_md_text_minus_1 && md_text_chars[i + 1] == '*' {
                        asterisks = (asterisks + 1) % 4;
                    } else {
                        mode = mode.asterisk(asterisks);
                        asterisks = 0;
                    }
                }
                '`' => {
                    mode = mode.backtick();
                }
                _ => match mode {
                    Mode::Regular => {
                        result.push(*c);
                    }
                    Mode::Italic => {
                        result.push(ITALIC_C[chr_index]);
                    }
                    Mode::Bold => {
                        result.push(BOLD_C[chr_index]);
                    }
                    Mode::BoldItalic => {
                        result.push(BOLD_ITALIC_C[chr_index]);
                    }
                    Mode::Mono => {
                        result.push(MONO_C[chr_index]);
                    }
                },
            }
        } else {
            result.push(*c);
            continue;
        }
    }

    result
}