Skip to main content

typo_eq/
config.rs

1use std::env;
2use std::fs::{File, DirBuilder};
3use std::io::{Read, Write};
4use std::collections::hash_map::HashMap;
5use std::path::Path;
6
7use super::util::get_index;
8
9#[derive(Debug)]
10pub struct ConfigFile {
11    pub dictionary_path: String,
12    pub show_phrases: bool, 
13    pub profile: String,
14}
15
16#[derive(Debug, Clone)]
17pub struct ProfileFile {
18    pub path: String,
19    pub profiles: HashMap<String, Profile>,
20}
21
22
23impl ProfileFile {
24    pub fn new(path: String) -> Self {
25        ProfileFile {
26            path,
27            profiles: HashMap::new()
28        }
29    }
30    pub fn load(path: String) -> Result<Self, String> {
31        let file = File::open(path.clone());
32        if let Ok(mut file) = file {
33            let mut contents = String::new(); 
34            if let Err(_) = file.read_to_string(&mut contents) {
35                return Err("Could not read profile file to string".to_string());
36            }
37            let mut lines = contents.lines();
38            println!("{}", path);
39            let mut profile_file = ProfileFile{ path, profiles: HashMap::new()};
40            let mut current_profile: Option<Profile> = None;
41            while let Some(line) = lines.next() {
42                // Profiles look like this: "[default]"
43                if line.starts_with("[") && line.ends_with("]") {
44                    let profile_name = line.replace("[", "");
45                    let profile_name = profile_name.replace("]", "");
46                    let new_profile = Some(Profile {
47                        name: profile_name,
48                        words_learnt: HashMap::new(),
49                    });
50                    // If there is a profile currently in the stack, make
51                    // sure to add it to the file
52                    if let Some(profile) = current_profile {
53                        profile_file.profiles.insert(profile.name.clone(), profile);
54                    }
55                    current_profile = new_profile;
56                    continue;
57                }
58                // Every line after that profile declaration is a word
59                // with their complete count divided by a # like so:
60                // "some_word#13"
61                // The word is  "some_word" and it was completed 13 times
62                if let Some(profile) = current_profile.clone() {
63                    let mut profile = profile;
64                    let mut line_separated = line.split("#");
65                    let word = line_separated.next();
66                    let count = line_separated.next();
67                    if let Some(word) = word {
68                        // If the word does not have a count or that count
69                        // could not be parsed, default to 0
70                        let count = if let Some(count) = count {
71                            count.parse::<i64>().unwrap_or(0)
72                        } else {
73                            0
74                        };
75                        profile.words_learnt.insert(word.to_string(), count);
76                        current_profile = Some(profile);
77                    }
78                }
79            }
80            if let Some(profile) = current_profile {
81                // Push remaining profile after read
82                profile_file.profiles.insert(profile.name.clone(), profile);
83            } else {
84                // Default if there is no profiles
85                profile_file.profiles.insert("default".to_string(), Profile {
86                    words_learnt: HashMap::new(),
87                    name: "default".to_string(),
88                });
89            }
90            Ok(profile_file)
91        } else {
92            Err(format!("Profile files could not be opened on: {}", path.clone()))
93        }
94    }
95    pub fn save(self: &Self) ->  Result<(), String> {
96        if let Some(dir) = Path::new(&self.path).parent() {
97            DirBuilder::new().recursive(true).create(dir).unwrap();
98        }
99        let file = File::create(self.path.clone());
100        match file {
101            Ok(file) => {
102                self.save_to_file(file)
103            },
104            Err(err) => {
105                Err(format!(
106                    "Profile files could not be saved to: {} (Err: {:?})", 
107                    self.path.clone(), 
108                    err
109                ))
110            },
111        }
112    }
113    pub fn save_to_file(self: &Self, mut file: File) ->  Result<(), String> {
114        for profile in self.profiles.values() {
115            file.write_all(format!("[{}]\n", profile.name).as_bytes())
116                .expect("Profile could not be saved on profile file");
117            for (word, count) in &profile.words_learnt {
118                file.write_all(format!("{}#{}\n", word, count).as_bytes())
119                    .expect("Could not write word into profile")
120            }
121        }
122        Ok(())
123    }
124}
125
126#[derive(Debug, Clone)]
127pub struct Profile {
128    pub name: String,
129    pub words_learnt: HashMap<String, i64>,
130}
131
132#[derive(Debug, Clone)]
133pub struct Config {
134    pub dictionary_path: String,
135    pub show_phrases: bool, 
136    pub profile: Profile,
137    pub profile_file: ProfileFile,
138    pub debugging: bool,
139}
140
141pub fn extract_config(args: &Vec<String>) -> Result<Config, String> {
142    // Check if the dict file was set or use default
143    let has_dict = args.contains(&"--dict".to_string()) || args.contains(&"-d".to_string());
144    let dictionary_path;
145    if has_dict {
146        let index_of_dict = (get_index(&args, "--dict") + 1) as usize;
147        dictionary_path = args
148            .get(index_of_dict)
149            .unwrap_or(&"./src".to_string())
150            .to_owned();
151    } else {
152        let current_dir = env::current_dir();
153        match current_dir {
154            Ok(path) => {
155                dictionary_path = format!("{}{}", path.display(), "dict.xdxf");
156            }
157            Err(error) => return Err(error.to_string()),
158        }
159    }
160    let show_phrases = args.contains(&"--phrases".to_string()) || args.contains(&"-p".to_string());
161    let debugging = args.contains(&"--debug".to_string());
162
163    let has_profile = args.contains(&"--profile".to_string());
164    let mut profile = Profile {
165        name: "default".to_string(),
166        words_learnt: HashMap::new(),
167    };
168    // Import profile from profile_file
169    let home = env::var("HOME").expect("Cannot load profiles if $HOME is not set");
170    let path = format!("{}/{}", home, ".config/typo-eq/profiles.txt");
171    let profile_file;
172    if has_profile {
173        let index_of_profile = (get_index(&args, "--profile") + 1) as usize;
174        let profile_name = args.get(index_of_profile)
175            .unwrap_or(&"default".to_string())
176            .to_owned();
177        profile.name = profile_name.clone();
178        let file = ProfileFile::load(path.clone());
179        let saved_profile = if let Ok(file) = file {
180            profile_file = file;
181            if let Some(p) = profile_file.profiles.get(&profile_name) {
182               Some(p.clone()) 
183            } else {None}
184        } else {
185            profile_file = ProfileFile::new(path);
186            None
187        };
188        if let Some(saved_profile) = saved_profile {
189            profile = saved_profile;
190        }
191    } else {
192        profile_file = ProfileFile::new(path);
193    }
194
195    Ok(Config {
196        dictionary_path,
197        debugging,
198        show_phrases,
199        profile,
200        profile_file,
201    })
202}