1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//! Wordlist support
//!
//! NOTE: This implementation is not constant time and may leak information
//! via timing side-channels!
//!
//! Adapted from the `bip39` crate

use super::bits::{Bits, Bits11};
use std::{collections::BTreeMap, vec::Vec};
use wasm_bindgen::prelude::*;

/// Supported languages.
///
/// Presently only English is specified by the BIP39 standard
#[derive(Copy, Clone, Debug, Default)]
#[wasm_bindgen]
pub enum Language {
    /// English is presently the only supported language
    #[default]
    English,
}

impl Language {
    /// Get the word list for this language
    pub fn wordlist(&self) -> &'static WordList {
        match *self {
            Language::English => &lazy::WORDLIST_ENGLISH,
        }
    }

    /// Get a wordmap that allows word -> index lookups in the word list
    pub(crate) fn wordmap(&self) -> &'static WordMap {
        match *self {
            Language::English => &lazy::WORDMAP_ENGLISH,
        }
    }
}

pub(crate) struct WordMap {
    inner: BTreeMap<&'static str, Bits11>,
}

pub struct WordList {
    inner: Vec<&'static str>,
}

impl WordMap {
    pub fn get_bits(&self, word: &str) -> Option<Bits11> {
        self.inner.get(word).cloned()
    }
}

impl WordList {
    pub fn get_word(&self, bits: Bits11) -> &'static str {
        self.inner[bits.bits() as usize]
    }

    pub fn iter(&self) -> WordListIterator<'_> {
        WordListIterator { wordlist: self, index: 0 }
    }
}

pub struct WordListIterator<'a> {
    wordlist: &'a WordList,
    index: usize,
}

impl Iterator for WordListIterator<'_> {
    type Item = &'static str;

    fn next(&mut self) -> Option<Self::Item> {
        if self.index < self.wordlist.inner.len() {
            let word = self.wordlist.inner[self.index];
            self.index += 1;
            Some(word)
        } else {
            None
        }
    }
}

// TODO(tarcieri): use `const fn` instead of `Lazy`
mod lazy {
    use super::{Bits11, WordList, WordMap};
    //use alloc::vec::Vec;
    use once_cell::sync::Lazy;

    /// lazy generation of the word list
    fn gen_wordlist(lang_words: &'static str) -> WordList {
        let inner: Vec<_> = lang_words.split_whitespace().collect();

        debug_assert!(inner.len() == 2048, "Invalid wordlist length");

        WordList { inner }
    }

    /// lazy generation of the word map
    fn gen_wordmap(wordlist: &WordList) -> WordMap {
        let inner = wordlist.inner.iter().enumerate().map(|(i, item)| (*item, Bits11::from(i as u16))).collect();

        WordMap { inner }
    }

    pub(crate) static WORDLIST_ENGLISH: Lazy<WordList> = Lazy::new(|| gen_wordlist(include_str!("words/english.txt")));

    pub(crate) static WORDMAP_ENGLISH: Lazy<WordMap> = Lazy::new(|| gen_wordmap(&WORDLIST_ENGLISH));
}