1use rand::seq::SliceRandom;
2use rust_embed::RustEmbed;
3use serde::Deserialize;
4use serde_json::from_str;
5use std::{error::Error, ffi::OsString, fs, io, path::PathBuf};
6
7#[derive(RustEmbed)]
8#[folder = "resources/runtime"]
9struct Resources;
10
11#[allow(dead_code)]
12#[derive(Deserialize, Clone, Debug)]
13pub struct Language {
14 name: String,
15
16 words: Vec<String>,
17}
18
19impl Language {
20 pub fn new(file_name: String) -> Self {
21 read_language_from_file(format!("{}.json", file_name)).unwrap()
22 }
23
24 pub fn get_random(&self, num: usize) -> Vec<String> {
25 let mut rng = &mut rand::thread_rng();
26
27 self.words.choose_multiple(&mut rng, num).cloned().collect()
28 }
29}
30
31fn config_dir() -> PathBuf {
32 dirs::config_dir()
33 .expect("Failed to find config directory.")
34 .join("ferristype")
35}
36
37fn language_dir() -> PathBuf {
38 config_dir().join("words")
39}
40
41fn read_language_from_file(file_name: String) -> Result<Language, Box<dyn Error>> {
42 let file = fs::read(language_dir().join(&file_name))
43 .ok()
44 .or_else(|| Resources::get(&format!("words/{}", &file_name)).map(|f| f.data.into_owned()))
45 .unwrap();
46 let file_as_str = String::from_utf8(file).expect("Unable to interpret file as a string");
47 let lang = from_str(&file_as_str).expect("Unable to deserialize language json");
48 Ok(lang)
49}
50
51pub fn list_installed_language() -> io::Result<Vec<OsString>> {
52 Ok(language_dir()
53 .read_dir()?
54 .filter_map(Result::ok)
55 .map(|e| e.file_name())
56 .collect())
57}