wordle_cli/maintenance/
import.rs

1use std::env::temp_dir;
2use std::fs::File;
3use std::io::{BufRead, BufReader, Error, LineWriter, Write};
4use std::time::Instant;
5
6use console::{Emoji, style};
7use indicatif::{HumanDuration, ProgressBar, ProgressStyle};
8use uuid::Uuid;
9
10use crate::dictionary::{Dictionary, DictionaryEntry, get_dictionary};
11use crate::db::db_dictionary::DbDictionary;
12use crate::lang::locale::replace_unicode;
13
14static BOOKMARK: Emoji<'_, '_> = Emoji("🔖  ", "");
15static MINIDISC: Emoji<'_, '_> = Emoji("💽  ", "");
16static SPARKLE: Emoji<'_, '_> = Emoji("✨ ", ":-)");
17
18pub fn do_import(source_file: String, lang: &str) -> std::io::Result<()> {
19    let dictionary = get_dictionary(lang);
20
21    let started = Instant::now();
22
23    println!(
24        "{} {}Polishing file...",
25        style("[1/2]").bold().dim(),
26        BOOKMARK
27    );
28
29    println!(
30        "{} {}Importing file...",
31        style("[2/2]").bold().dim(),
32        MINIDISC
33    );
34
35    let progress_polish = setup_spinner();
36    progress_polish.set_message(format!("Processing {}...", source_file));
37
38    let meta_data = polish(&source_file, lang)?;
39
40    progress_polish.finish_with_message(format!("Finished processing {}. Importing...", source_file));
41
42    let progress_import = ProgressBar::new(meta_data.1);
43
44    let counter = import(meta_data.0, dictionary, &progress_import)?;
45
46    progress_polish.finish_and_clear();
47    progress_import.finish_and_clear();
48
49    println!("{} Done in {}. Added {} words to the dictionary!", SPARKLE, HumanDuration(started.elapsed()), counter);
50
51    Ok(())
52}
53
54/// Read raw word list from source_path and polish with matching lang strategy.
55/// The polished list is then written to a temporary file located in the tmp directory of the filesystem.
56///
57/// See [temp_dir] documentation for more information.
58///
59/// # Arguments
60///
61/// * `src_path` - A string slice that holds the path of the file you want to maintenance on the filesystem
62/// * `lang` - Language shortcode of the imported words.
63fn polish(source_path: &str, lang: &str) -> Result<(String, u64), Error> {
64    let tmp_file_name = format!("{}/{}.txt", temp_dir().to_str().unwrap(), Uuid::new_v4());
65    let out_file: Result<File, Error> = File::create(&tmp_file_name);
66
67    match out_file {
68        Ok(out_file) => {
69            let buf_reader = BufReader::new(File::open(source_path).unwrap());
70            let mut writer: LineWriter<File> = LineWriter::new(out_file);
71
72            let mut counter = 0;
73
74            for line_result in buf_reader.lines() {
75                let polished = replace_unicode(line_result.unwrap().to_lowercase().as_str(), lang);
76
77                if polished.len() == 5 {
78                    writer.write(polished.as_ref())?;
79                    writer.write(b"\n")?;
80
81                    counter += 1;
82                }
83            }
84
85            Ok((tmp_file_name, counter))
86        }
87        Err(error) => Err(error)
88    }
89}
90
91/// Import temporary file created by [polish] into the dictionary.
92/// Avoid duplicates when inserting a [WordEntry] into the dictionary.
93///
94/// # Arguments
95///
96/// * `tmp_file_name` - A String that holds the name of the temp file created
97fn import(tmp_file_name: String, dictionary: DbDictionary, progress_bar: &ProgressBar) -> Result<i32, Error> {
98    let buf_reader = BufReader::new(File::open(tmp_file_name).unwrap());
99
100    let mut counter = 0;
101    for line_result in buf_reader.lines() {
102        let line = line_result.unwrap();
103
104        match dictionary.create_word(DictionaryEntry {
105            word: line.to_lowercase(),
106            guessed: false
107        }) {
108            None => {},
109            Some(_) => {
110                counter += 1;
111            }
112        }
113
114        progress_bar.inc(1);
115    }
116
117    Ok(counter)
118}
119
120fn setup_spinner() -> ProgressBar {
121    let progress_bar = ProgressBar::new_spinner();
122    progress_bar.enable_steady_tick(120);
123    progress_bar.set_style(ProgressStyle::default_bar()
124        .template("{prefix:.bold.dim} {spinner:.green} {msg}"));
125
126    progress_bar
127}