wordlists 0.2.0

Take bits, give words.
Documentation
///
/// PGPfone defines a "radio alphabet" for conveying bytes verbally
/// over a noisy phone line. The words were chosen to reduce
/// "confusibility".
///
/// References
/// * Whole-Word Phonetic Distances and the PGPfone Alphabet
///     http://www.asel.udel.edu/icslp/cdrom/vol1/005/a005.pdf
///
use crate::*;
use lazy_static::lazy_static;

/// Compile-time embed of the "even byte" word-list.
const PGPFONE_EVEN_LIST: [&'static str; 256] =
    include!("../../static/wordlists/pgpfone/converted/even.json");

/// Compile-time embed of the "odd byte" word-list.
const PGPFONE_ODD_LIST: [&'static str; 256] =
    include!("../../static/wordlists/pgpfone/converted/odd.json");

lazy_static! {
    /// This is the PGPfone Wordlist as an Indexing wordlist.
    pub static ref PGPFONE: MultiListIndexingWordlist = {
        let even = Box::new(PGPFONE_EVEN_LIST);
        let odd = Box::new(PGPFONE_ODD_LIST);
        MultiListIndexingWordlist::new(
            "PGPfone Wordlist".to_string(),

            // MUST BE IN THIS ORDER:
            vec![even, odd]
        )
    };
}

#[cfg(test)]
mod test {
    use super::{PGPFONE, PGPFONE_EVEN_LIST, PGPFONE_ODD_LIST};
    use crate::bits::pack_indexes;
    use crate::{Result, Wordlist, WordlistByteConverter};

    #[test]
    fn validate_lists_complete() {
        assert_eq!(256, PGPFONE_EVEN_LIST.len());
        assert_eq!(256, PGPFONE_ODD_LIST.len());
        PGPFONE_EVEN_LIST
            .iter()
            .for_each(|v| assert!(!v.is_empty()));
        PGPFONE_ODD_LIST.iter().for_each(|v| assert!(!v.is_empty()));
    }

    #[test]
    fn validate_bit_width() {
        assert_eq!(8, PGPFONE.index_bit_width().unwrap());
    }

    #[test]
    fn validate_convert() -> Result<()> {
        // This differs from the paper (see page 4), I believe due to
        // a difference in the wordlist.
        // http://www.asel.udel.edu/icslp/cdrom/vol1/005/a005.pdf
        let words = vec![
            "escape",
            "corporate",
            "inverse",
            "specialist",
            "swelter",
            "tomorrow",
            "quiver",
            "repellent",
            "egghead",
            "Chicago",
            "rebirth",
            "miracle",
            "soybean",
            "miracle",
            "fracture",
            "Waterloo",
        ];
        let indexes: Vec<usize> = vec![
            0x5C, 0x39, 0x76, 0xD5, 0xDD, 0xE2, 0x9E, 0xC2, 0x56, 0x2C, 0xA2, 0x91, 0xC7, 0x91,
            0x65, 0xF9,
        ];

        let bits = PGPFONE.index_bit_width().unwrap();
        let patrick = pack_indexes(indexes, bits).unwrap();
        let bytes = PGPFONE.to_bytes(&words)?;
        let wordlist = PGPFONE.to_words(&bytes)?;
        assert_eq!(
            words, wordlist,
            "Verify we can create the expected wordlist from a list of bytes."
        );
        assert_eq!(
            patrick, bytes,
            "Verify we can convert back to bytes from a known wordlist."
        );
        Ok(())
    }
}