spellcli 1.2.0

A CLI that allows you to check the spelling of different words.
use std::io;


mod word_process;
use word_process::*;

use std::collections::HashMap;

#[cfg(debug_assertions)]
use std::time;
use std::sync::{mpsc, Arc};
use std::thread;

fn main() {
    
    // input handleing 
    println!("Check word: ");

    let mut input = String::new();

    let result = io::stdin().read_line(&mut input);
    if let Err(e) = result{
        println!("read_line error: {}", e);
    }
    #[cfg(debug_assertions)]
    let timer = time::Instant::now(); // Timer for timing this CLI 
    let word = first_word(&input).to_lowercase();
    if word.len() < 2 { println!("input is too short"); return (); }

    // the difference here is for reduncency; api.dictionaryapi.dev & wordnik_list have differnt
    // words
    let mut num_print_def = 5_u32; // number of deffinitions that are prited
    let num_search_def = 10_u32; // number of deffinitions that are requested


    // getting all the words
    let wordsims: Vec<WordSim> = check_against(&word, num_search_def, 4).into_iter().rev().collect();


    // getting the word defs

    #[cfg(debug_assertions)]
    let silent_failed_def_threads: Arc<u32> = Arc::new(0_u32);
    #[cfg(debug_assertions)]
    let total_def_thread_count = wordsims.len();

    let (tx, rx) = mpsc::channel(); // transmitter and receiver for the def hashmap data
    let mut word_defs: HashMap<String, Vec<WordDef>> = HashMap::new();
    let client = Arc::new(reqwest::blocking::Client::new());
    for wordsim in wordsims.iter() {
        let (word_2, sender, client_ref) = (
            wordsim.get_word_2().to_string(),
            tx.clone(),
            client.clone(),
        );
        #[cfg(debug_assertions)]
        let mut fail_count = silent_failed_def_threads.clone();
        let _ = thread::Builder::new()
            .name(format!("Getting def for word {}", &word_2))
            .spawn(move || {
                // getting data

                let word_defs = 'def_get: { 
                    // Trys up to three times to get the def of a word
                    for _ in 0..3 {
                        let word_defs = get_word_defs(&word_2, 2_u32, &*client_ref);
                        match word_defs{
                            Ok(defs) => break 'def_get defs,
                            // try again if bad gateway
                            Err(e) if *e.to_string() == "error code: 502\n".to_string() => continue,
                            Err(e) => panic!("unknown error: {}", e),
                        }
                    }
                    // panic!("could not get def data");  
                    #[cfg(debug_assertions)]
                    { *Arc::make_mut(&mut fail_count) += 1_u32; }
                    return ();
                };

                let def_data = (
                    word_2.to_string(),
                    word_defs,
                );

                // only send the data if there is a def
                
                let mut there_is_a_def = false; 
                for def in def_data.1.iter() {
                    there_is_a_def |= def.def.is_some();
                }

                if there_is_a_def { sender.send(def_data).unwrap(); }
            }
        );
    }
    std::mem::drop(tx);

    for def in rx{
        word_defs.insert(def.0, def.1);
    }


    // printing info
    println!("");
    
    let mut wordsims_iter = wordsims.into_iter();

    match wordsims_iter.next() {
        Some(wordsim) => {
            if wordsim.get_sim() == 1.0/0.0 && 
                word_defs.get(wordsim.get_word_1()).is_some(){ 

                println!("✅ '{word}' IS a word ✅");
                println!("Definition of {word}");
                print_def(&wordsim, &word_defs, false);

                println!("Simmalar words:\n");

            } else {
                println!("❌ '{word}' is NOT a word ❌");
                println!("Simmalar words:\n");

                print_def(&wordsim, &word_defs, true); 
            }

        }
        None => {
            println!("No matches found for '{word}'");
            return;
        }
    }


    // printing other defs
    for wordsim in wordsims_iter{
        // this is (one of) the word(s) the code found to be simmalar to the input word

        print_def(&wordsim, &word_defs, true);

        num_print_def -= 1;

        if num_print_def == 0 { 
            break;
        }
        
    }


    #[cfg(debug_assertions)]
    {
        println!("Took {} millis to run", timer.elapsed().as_millis());
        println!(
            "{}/{} def threads failed silently", 
            Arc::<u32>::into_inner(silent_failed_def_threads).unwrap(), 
            total_def_thread_count
        );
    }

}

/// Prints out the definition and compareesent of a word if one is found
fn print_def(wordsim: &WordSim, lookup: &HashMap<String, Vec<WordDef>>, display_sim: bool) {

        let word = wordsim.get_word_1();
        let word2 = wordsim.get_word_2();

        let defs = lookup.get(word2);
        if defs.is_none() {
            return;
        }
        let defs = defs.unwrap(); // just checked if this none or not
                                  
        // checking that there are defs to print & creating the def text to be printed
        let mut def_text = String::new();
        for def in defs.iter() {
            if let Some(some_speech) = &def.part_of_speech{            
                if let Some(some_def) = &def.def{ 
                    def_text.push_str(
                        &format!("> {}: {}\n",
                            some_speech,
                            some_def,
                        )
                    );
                }
            }

        }
            
        if display_sim {
            def_text = format!(
                "'{word}' is {}% simmalar to the word '{}'\n{}", 
                wordsim.get_sim() * 100.0,
                wordsim.get_word_2(),
                def_text,
            );

        }

        println!("{}", def_text);
}