omekasy 1.1.0

Decorate alphanumeric characters in your input with various font; special characters in Unicode
use std::collections::HashMap;

use crate::font::{Font, FontMap};

/// This struct holds each font's mapping between normal characters to ones of the font.
pub struct Converter {
    font_mappings: HashMap<Font, FontMap>,
}

impl Converter {
    pub fn new(fonts: &[Font]) -> Self {
        let mut font_mappings = HashMap::new();
        for font in fonts {
            font_mappings.insert(*font, font.characters());
        }

        Self { font_mappings }
    }

    /// Convert given characters to specified font.
    /// Non-alphanumeric characters remain unchanged.
    pub fn convert(&self, source: &[char], font: Font) -> String {
        let mapping = self
            .font_mappings
            .get(&font)
            .expect("Unexpected font specified");
        source
            .iter()
            .map(|original| {
                if let Some(converted) = mapping.get(original) {
                    converted
                } else {
                    original
                }
            })
            .collect()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn setup_converter() -> Converter {
        Converter::new(&[
            Font::Bold,
            Font::Italic,
            Font::BoldItalic,
            Font::Sans,
            Font::BoldSans,
            Font::ItalicSans,
            Font::BoldItalicSans,
            Font::Script,
            Font::BoldScript,
            Font::Fraktur,
            Font::BoldFraktur,
            Font::Monospace,
            Font::Blackboard,
        ])
    }

    #[test]
    fn skip_non_target_chars() {
        let converter = setup_converter();
        let source = "ernct_jahmlsz ใ‚wgdqi-uxfpvobyk"
            .chars()
            .collect::<Vec<_>>();
        assert_eq!(
            "๐ž๐ซ๐ง๐œ๐ญ_๐ฃ๐š๐ก๐ฆ๐ฅ๐ฌ๐ณ ใ‚๐ฐ๐ ๐๐ช๐ข-๐ฎ๐ฑ๐Ÿ๐ฉ๐ฏ๐จ๐›๐ฒ๐ค",
            converter.convert(&source, Font::Bold)
        );
    }

    #[test]
    fn bold() {
        let converter = setup_converter();
        let source = "8WymXbLV3nINUhOoQkKGfuY9HsZSC675jzBEtATDFMRgPpeaxiJcr0q4l1w2dv"
            .chars()
            .collect::<Vec<_>>();
        assert_eq!(
            "๐Ÿ–๐–๐ฒ๐ฆ๐—๐›๐‹๐•๐Ÿ‘๐ง๐ˆ๐๐”๐ก๐Ž๐จ๐๐ค๐Š๐†๐Ÿ๐ฎ๐˜๐Ÿ—๐‡๐ฌ๐™๐’๐‚๐Ÿ”๐Ÿ•๐Ÿ“๐ฃ๐ณ๐๐„๐ญ๐€๐“๐ƒ๐…๐Œ๐‘๐ ๐๐ฉ๐ž๐š๐ฑ๐ข๐‰๐œ๐ซ๐ŸŽ๐ช๐Ÿ’๐ฅ๐Ÿ๐ฐ๐Ÿ๐๐ฏ",
            converter.convert(&source, Font::Bold)
        );
    }

    #[test]
    fn italic() {
        let converter = setup_converter();
        let source = "8WymXbLV3nINUhOoQkKGfuY9HsZSC675jzBEtATDFMRgPpeaxiJcr0q4l1w2dv"
            .chars()
            .collect::<Vec<_>>();
        assert_eq!(
            "8๐‘Š๐‘ฆ๐‘š๐‘‹๐‘๐ฟ๐‘‰3๐‘›๐ผ๐‘๐‘ˆโ„Ž๐‘‚๐‘œ๐‘„๐‘˜๐พ๐บ๐‘“๐‘ข๐‘Œ9๐ป๐‘ ๐‘๐‘†๐ถ675๐‘—๐‘ง๐ต๐ธ๐‘ก๐ด๐‘‡๐ท๐น๐‘€๐‘…๐‘”๐‘ƒ๐‘๐‘’๐‘Ž๐‘ฅ๐‘–๐ฝ๐‘๐‘Ÿ0๐‘ž4๐‘™1๐‘ค2๐‘‘๐‘ฃ",
            converter.convert(&source, Font::Italic)
        );
    }

    #[test]
    fn bold_italic() {
        let converter = setup_converter();
        let source = "8WymXbLV3nINUhOoQkKGfuY9HsZSC675jzBEtATDFMRgPpeaxiJcr0q4l1w2dv"
            .chars()
            .collect::<Vec<_>>();
        assert_eq!(
            "8๐‘พ๐’š๐’Ž๐‘ฟ๐’ƒ๐‘ณ๐‘ฝ3๐’๐‘ฐ๐‘ต๐‘ผ๐’‰๐‘ถ๐’๐‘ธ๐’Œ๐‘ฒ๐‘ฎ๐’‡๐’–๐’€9๐‘ฏ๐’”๐’๐‘บ๐‘ช675๐’‹๐’›๐‘ฉ๐‘ฌ๐’•๐‘จ๐‘ป๐‘ซ๐‘ญ๐‘ด๐‘น๐’ˆ๐‘ท๐’‘๐’†๐’‚๐’™๐’Š๐‘ฑ๐’„๐’“0๐’’4๐’1๐’˜2๐’…๐’—",
            converter.convert(&source, Font::BoldItalic)
        );
    }

    #[test]
    fn sans() {
        let converter = setup_converter();
        let source = "8WymXbLV3nINUhOoQkKGfuY9HsZSC675jzBEtATDFMRgPpeaxiJcr0q4l1w2dv"
            .chars()
            .collect::<Vec<_>>();
        assert_eq!(
            "๐Ÿช๐–ถ๐—’๐—†๐–ท๐–ป๐–ซ๐–ต๐Ÿฅ๐—‡๐–จ๐–ญ๐–ด๐—๐–ฎ๐—ˆ๐–ฐ๐—„๐–ช๐–ฆ๐–ฟ๐—Ž๐–ธ๐Ÿซ๐–ง๐—Œ๐–น๐–ฒ๐–ข๐Ÿจ๐Ÿฉ๐Ÿง๐—ƒ๐—“๐–ก๐–ค๐—๐– ๐–ณ๐–ฃ๐–ฅ๐–ฌ๐–ฑ๐—€๐–ฏ๐—‰๐–พ๐–บ๐—‘๐—‚๐–ฉ๐–ผ๐—‹๐Ÿข๐—Š๐Ÿฆ๐—…๐Ÿฃ๐—๐Ÿค๐–ฝ๐—",
            converter.convert(&source, Font::Sans)
        );
    }

    #[test]
    fn bold_sans() {
        let converter = setup_converter();
        let source = "8WymXbLV3nINUhOoQkKGfuY9HsZSC675jzBEtATDFMRgPpeaxiJcr0q4l1w2dv"
            .chars()
            .collect::<Vec<_>>();
        assert_eq!(
            "๐Ÿด๐—ช๐˜†๐—บ๐—ซ๐—ฏ๐—Ÿ๐—ฉ๐Ÿฏ๐—ป๐—œ๐—ก๐—จ๐—ต๐—ข๐—ผ๐—ค๐—ธ๐—ž๐—š๐—ณ๐˜‚๐—ฌ๐Ÿต๐—›๐˜€๐—ญ๐—ฆ๐—–๐Ÿฒ๐Ÿณ๐Ÿฑ๐—ท๐˜‡๐—•๐—˜๐˜๐—”๐—ง๐——๐—™๐— ๐—ฅ๐—ด๐—ฃ๐—ฝ๐—ฒ๐—ฎ๐˜…๐—ถ๐—๐—ฐ๐—ฟ๐Ÿฌ๐—พ๐Ÿฐ๐—น๐Ÿญ๐˜„๐Ÿฎ๐—ฑ๐˜ƒ",
            converter.convert(&source, Font::BoldSans)
        );
    }

    #[test]
    fn italic_sans() {
        let converter = setup_converter();
        let source = "8WymXbLV3nINUhOoQkKGfuY9HsZSC675jzBEtATDFMRgPpeaxiJcr0q4l1w2dv"
            .chars()
            .collect::<Vec<_>>();
        assert_eq!(
            "๐Ÿด๐˜ž๐˜บ๐˜ฎ๐˜Ÿ๐˜ฃ๐˜“๐˜๐Ÿฏ๐˜ฏ๐˜๐˜•๐˜œ๐˜ฉ๐˜–๐˜ฐ๐˜˜๐˜ฌ๐˜’๐˜Ž๐˜ง๐˜ถ๐˜ ๐Ÿต๐˜๐˜ด๐˜ก๐˜š๐˜Š๐Ÿฒ๐Ÿณ๐Ÿฑ๐˜ซ๐˜ป๐˜‰๐˜Œ๐˜ต๐˜ˆ๐˜›๐˜‹๐˜๐˜”๐˜™๐˜จ๐˜—๐˜ฑ๐˜ฆ๐˜ข๐˜น๐˜ช๐˜‘๐˜ค๐˜ณ๐Ÿฌ๐˜ฒ๐Ÿฐ๐˜ญ๐Ÿญ๐˜ธ๐Ÿฎ๐˜ฅ๐˜ท",
            converter.convert(&source, Font::ItalicSans)
        );
    }

    #[test]
    fn bold_italic_sans() {
        let converter = setup_converter();
        let source = "8WymXbLV3nINUhOoQkKGfuY9HsZSC675jzBEtATDFMRgPpeaxiJcr0q4l1w2dv"
            .chars()
            .collect::<Vec<_>>();
        assert_eq!(
            "๐Ÿด๐™’๐™ฎ๐™ข๐™“๐™—๐™‡๐™‘๐Ÿฏ๐™ฃ๐™„๐™‰๐™๐™๐™Š๐™ค๐™Œ๐™ ๐™†๐™‚๐™›๐™ช๐™”๐Ÿต๐™ƒ๐™จ๐™•๐™Ž๐˜พ๐Ÿฒ๐Ÿณ๐Ÿฑ๐™Ÿ๐™ฏ๐˜ฝ๐™€๐™ฉ๐˜ผ๐™๐˜ฟ๐™๐™ˆ๐™๐™œ๐™‹๐™ฅ๐™š๐™–๐™ญ๐™ž๐™…๐™˜๐™ง๐Ÿฌ๐™ฆ๐Ÿฐ๐™ก๐Ÿญ๐™ฌ๐Ÿฎ๐™™๐™ซ",
            converter.convert(&source, Font::BoldItalicSans)
        );
    }

    #[test]
    fn script() {
        let converter = setup_converter();
        let source = "8WymXbLV3nINUhOoQkKGfuY9HsZSC675jzBEtATDFMRgPpeaxiJcr0q4l1w2dv"
            .chars()
            .collect::<Vec<_>>();
        assert_eq!(
            "8๐’ฒ๐“Ž๐“‚๐’ณ๐’ทโ„’๐’ฑ3๐“ƒโ„๐’ฉ๐’ฐ๐’ฝ๐’ชโ„ด๐’ฌ๐“€๐’ฆ๐’ข๐’ป๐“Š๐’ด9โ„‹๐“ˆ๐’ต๐’ฎ๐’ž675๐’ฟ๐“โ„ฌโ„ฐ๐“‰๐’œ๐’ฏ๐’Ÿโ„ฑโ„ณโ„›โ„Š๐’ซ๐“…โ„ฏ๐’ถ๐“๐’พ๐’ฅ๐’ธ๐“‡0๐“†4๐“1๐“Œ2๐’น๐“‹",
            converter.convert(&source, Font::Script)
        );
    }

    #[test]
    fn bold_script() {
        let converter = setup_converter();
        let source = "8WymXbLV3nINUhOoQkKGfuY9HsZSC675jzBEtATDFMRgPpeaxiJcr0q4l1w2dv"
            .chars()
            .collect::<Vec<_>>();
        assert_eq!(
            "8๐“ฆ๐”‚๐“ถ๐“ง๐“ซ๐“›๐“ฅ3๐“ท๐“˜๐“๐“ค๐“ฑ๐“ž๐“ธ๐“ ๐“ด๐“š๐“–๐“ฏ๐“พ๐“จ9๐“—๐“ผ๐“ฉ๐“ข๐“’675๐“ณ๐”ƒ๐“‘๐“”๐“ฝ๐“๐“ฃ๐““๐“•๐“œ๐“ก๐“ฐ๐“Ÿ๐“น๐“ฎ๐“ช๐”๐“ฒ๐“™๐“ฌ๐“ป0๐“บ4๐“ต1๐”€2๐“ญ๐“ฟ",
            converter.convert(&source, Font::BoldScript)
        );
    }

    #[test]
    fn fraktur() {
        let converter = setup_converter();
        let source = "8WymXbLV3nINUhOoQkKGfuY9HsZSC675jzBEtATDFMRgPpeaxiJcr0q4l1w2dv"
            .chars()
            .collect::<Vec<_>>();
        assert_eq!(
            "8๐”š๐”ถ๐”ช๐”›๐”Ÿ๐”๐”™3๐”ซโ„‘๐”‘๐”˜๐”ฅ๐”’๐”ฌ๐””๐”จ๐”Ž๐”Š๐”ฃ๐”ฒ๐”œ9โ„Œ๐”ฐโ„จ๐”–โ„ญ675๐”ง๐”ท๐”…๐”ˆ๐”ฑ๐”„๐”—๐”‡๐”‰๐”โ„œ๐”ค๐”“๐”ญ๐”ข๐”ž๐”ต๐”ฆ๐”๐” ๐”ฏ0๐”ฎ4๐”ฉ1๐”ด2๐”ก๐”ณ",
            converter.convert(&source, Font::Fraktur)
        );
    }

    #[test]
    fn bold_fraktur() {
        let converter = setup_converter();
        let source = "8WymXbLV3nINUhOoQkKGfuY9HsZSC675jzBEtATDFMRgPpeaxiJcr0q4l1w2dv"
            .chars()
            .collect::<Vec<_>>();
        assert_eq!(
            "8๐–‚๐–ž๐–’๐–ƒ๐–‡๐•ท๐–3๐–“๐•ด๐•น๐–€๐–๐•บ๐–”๐•ผ๐–๐•ถ๐•ฒ๐–‹๐–š๐–„9๐•ณ๐–˜๐–…๐•พ๐•ฎ675๐–๐–Ÿ๐•ญ๐•ฐ๐–™๐•ฌ๐•ฟ๐•ฏ๐•ฑ๐•ธ๐•ฝ๐–Œ๐•ป๐–•๐–Š๐–†๐–๐–Ž๐•ต๐–ˆ๐–—0๐––4๐–‘1๐–œ2๐–‰๐–›",
            converter.convert(&source, Font::BoldFraktur)
        );
    }

    #[test]
    fn monospace() {
        let converter = setup_converter();
        let source = "8WymXbLV3nINUhOoQkKGfuY9HsZSC675jzBEtATDFMRgPpeaxiJcr0q4l1w2dv"
            .chars()
            .collect::<Vec<_>>();
        assert_eq!(
            "๐Ÿพ๐š†๐šข๐š–๐š‡๐š‹๐™ป๐š…๐Ÿน๐š—๐™ธ๐™ฝ๐š„๐š‘๐™พ๐š˜๐š€๐š”๐™บ๐™ถ๐š๐šž๐šˆ๐Ÿฟ๐™ท๐šœ๐š‰๐š‚๐™ฒ๐Ÿผ๐Ÿฝ๐Ÿป๐š“๐šฃ๐™ฑ๐™ด๐š๐™ฐ๐šƒ๐™ณ๐™ต๐™ผ๐š๐š๐™ฟ๐š™๐šŽ๐šŠ๐šก๐š’๐™น๐šŒ๐š›๐Ÿถ๐šš๐Ÿบ๐š•๐Ÿท๐š ๐Ÿธ๐š๐šŸ",
            converter.convert(&source, Font::Monospace)
        );
    }

    #[test]
    fn blackboard() {
        let converter = setup_converter();
        let source = "8WymXbLV3nINUhOoQkKGfuY9HsZSC675jzBEtATDFMRgPpeaxiJcr0q4l1w2dv"
            .chars()
            .collect::<Vec<_>>();
        assert_eq!(
            "๐Ÿ ๐•Ž๐•ช๐•ž๐•๐•“๐•ƒ๐•๐Ÿ›๐•Ÿ๐•€โ„•๐•Œ๐•™๐•†๐• โ„š๐•œ๐•‚๐”พ๐•—๐•ฆ๐•๐Ÿกโ„๐•คโ„ค๐•Šโ„‚๐Ÿž๐ŸŸ๐Ÿ๐•›๐•ซ๐”น๐”ผ๐•ฅ๐”ธ๐•‹๐”ป๐”ฝ๐•„โ„๐•˜โ„™๐•ก๐•–๐•’๐•ฉ๐•š๐•๐•”๐•ฃ๐Ÿ˜๐•ข๐Ÿœ๐•๐Ÿ™๐•จ๐Ÿš๐••๐•ง",
            converter.convert(&source, Font::Blackboard)
        );
    }
}