ferristype/lang/
mod.rs

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}