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
108
use ahash::AHashMap;
use miniz_oxide::inflate::decompress_to_vec;
use once_cell::sync::Lazy;

use crate::Lang;

pub(crate) type Words = Box<[&'static str]>;

macro_rules! generate_lazy_db_from_file {
    ($file_stem:ident) => {
        paste::paste! {
            static [<$file_stem:upper _COMPRESSED>]: Lazy<String> = Lazy::new(|| {
                let compressed_bytes = include_bytes!(concat!("gz/", stringify!($file_stem), ".gz"));
                let decompressed_bytes = decompress_to_vec(compressed_bytes)
                    .expect("Decompression failed");
                let decompressed_string = String::from_utf8(decompressed_bytes)
                    .expect("Decompression resulted in invalid UTF-8");

                decompressed_string
            });

            static [<$file_stem:upper>]: Lazy<Words> = Lazy::new(|| {
                [<$file_stem:upper _COMPRESSED>].lines().collect()
            });

            static [<$file_stem:upper _LEN>]: Lazy<AHashMap<usize, Words>> = Lazy::new(|| {
                let mut map = AHashMap::new();

                for &word in [<$file_stem:upper>].iter() {
                    let len = word.chars().count();
                    map.entry(len).or_insert_with(Vec::new).push(word);
                }

                map.into_iter().map(|(k, v)| (k, v.into_boxed_slice())).collect()
            });

            static [<$file_stem:upper _STARTS_WITH>]: Lazy<AHashMap<char, Words>> = Lazy::new(|| {
                let mut map = AHashMap::new();

                for &word in [<$file_stem:upper>].iter() {
                    let first = word.chars().nth(0).expect("empty word");
                    map.entry(first).or_insert_with(Vec::new).push(word);
                }

                map.into_iter().map(|(k, v)| (k, v.into_boxed_slice())).collect()
            });
        }
    };
}

#[cfg(feature = "de")]
generate_lazy_db_from_file!(de);
#[cfg(feature = "en")]
generate_lazy_db_from_file!(en);
#[cfg(feature = "es")]
generate_lazy_db_from_file!(es);
#[cfg(feature = "fr")]
generate_lazy_db_from_file!(fr);
#[cfg(feature = "zh")]
generate_lazy_db_from_file!(zh);

#[inline(always)]
pub(crate) fn get(lang: Lang) -> &'static Words {
    match lang {
        #[cfg(feature = "de")]
        Lang::De => &DE,
        #[cfg(feature = "en")]
        Lang::En => &EN,
        #[cfg(feature = "es")]
        Lang::Es => &ES,
        #[cfg(feature = "fr")]
        Lang::Fr => &FR,
        #[cfg(feature = "zh")]
        Lang::Zh => &ZH,
    }
}

#[inline(always)]
pub(crate) fn get_len(len: usize, lang: Lang) -> Option<&'static Words> {
    match lang {
        #[cfg(feature = "de")]
        Lang::De => DE_LEN.get(&len),
        #[cfg(feature = "en")]
        Lang::En => EN_LEN.get(&len),
        #[cfg(feature = "es")]
        Lang::Es => ES_LEN.get(&len),
        #[cfg(feature = "fr")]
        Lang::Fr => FR_LEN.get(&len),
        #[cfg(feature = "zh")]
        Lang::Zh => ZH_LEN.get(&len),
    }
}

#[inline(always)]
pub(crate) fn get_starts_with(char: char, lang: Lang) -> Option<&'static Words> {
    match lang {
        #[cfg(feature = "de")]
        Lang::De => DE_STARTS_WITH.get(&char),
        #[cfg(feature = "en")]
        Lang::En => EN_STARTS_WITH.get(&char),
        #[cfg(feature = "es")]
        Lang::Es => ES_STARTS_WITH.get(&char),
        #[cfg(feature = "fr")]
        Lang::Fr => FR_STARTS_WITH.get(&char),
        #[cfg(feature = "zh")]
        Lang::Zh => ZH_STARTS_WITH.get(&char),
    }
}