i-dunno 0.6.0

RFC 8771 Internationalized Deliberately Unreadable Network Notation
Documentation
use std::char;
use std::collections::BTreeSet;
use std::io::{self, BufRead, Write};
use std::u32;

// Adapted from https://github.com/PeterReid/unicode_skeleton/blob/master/data/confusables_to_data.rs

pub fn process_confusables<R: BufRead, W: Write>(
    input: R,
    mut output: W,
) -> io::Result<()> {
    let mut chars = BTreeSet::<char>::new();
    let mut strs = BTreeSet::<String>::new();

    // First line contains a BOM
    for line in input.lines().skip(1).filter_map(|line| {
        if let Ok(line) = line {
            Some(line)
        } else {
            None
        }
    }) {
        // Delete comments
        let line = if let Some(comment_begin) = line.find('#') {
            &line[..comment_begin]
        } else {
            &line
        };
        if line.len() > 0 {
            let mut line_chunks = line.split(" ;\t");
            let from = line_chunks.next().expect("Failed to parse line");
            let tos = line_chunks.next().expect("Failed to parse line");

            let from = u32::from_str_radix(from, 16)
                .expect("Failed to parse `from` as hex");

            let tos: Vec<u32> = tos
                .split(" ")
                .map(|to| {
                    u32::from_str_radix(to, 16)
                        .expect("Failed to parse `to` as hex")
                })
                .collect();

            chars.insert(char::from_u32(from).unwrap());

            if tos.len() == 1 {
                chars.insert(char::from_u32(tos[0]).unwrap());
            } else {
                strs.insert(
                    tos.iter().map(|&i| char::from_u32(i).unwrap()).collect(),
                );
            }
        }
    }

    output.write(b"/// A set of all characters that may be confusable\n")?;
    output.write(b"/// with other strings or characters.\n")?;
    output.write(
        &format!("pub const CONFUSABLES_CHARS: [char; {}] = [\n", chars.len())
            .as_bytes(),
    )?;
    for ch in chars {
        output
            .write(&format!("    '\\u{{{:04x}}}',\n", ch as u32).as_bytes())?;
    }
    output.write(b"];\n\n")?;

    output.write(b"/// A set of all strings that may be confusable\n")?;
    output.write(b"/// with other strings or characters.\n")?;
    output.write(
        &format!("pub const CONFUSABLES_STRS: [&str; {}] = [\n", strs.len())
            .as_bytes(),
    )?;
    for s in strs {
        output.write(b"    \"")?;
        for ch in s.chars() {
            output.write(&format!("\\u{{{:04x}}}", ch as u32).as_bytes())?;
        }
        output.write(b"\",\n")?;
    }
    output.write(b"];\n")?;
    Ok(())
}